Skip to content

Instantly share code, notes, and snippets.

@Ayms
Last active March 24, 2023 17:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ayms/04b3084a14ee202e707b3faec57ed26e to your computer and use it in GitHub Desktop.
Save Ayms/04b3084a14ee202e707b3faec57ed26e to your computer and use it in GitHub Desktop.
Bitcoin, Ethereum, smart contracts, lightning, sidechains, Defi, DApps, NFTs, DEX, DAO explained - Centralization vs Decentralization

Note: this article is for some parts a bit technical, but you can skip those parts and just read the plain text, which is supposed to be understandable by anybody

Note2: for Ethereum some sections are copy/paste from articles and Ethereum official documentation

The intent here is to explain what is really behind the crypto currencies technologies which is never really explained and people don't know unless they have some (kind of deep) technical knowledge

I often react when people are talking about crypto currencies as "virtual" money, this is not virtual money but electronic money, there is a work behind this and this is no more virtual than a bank bill

Now, when it comes to discuss sidechains and smart contracts, things can really become virtual, let's see how it works on bictoin and ethereum, for comprehension purposes we have to go a little far in technical details while using some simplification language as some experts might notice and start with easy things, this is not a vulgarization article stating vague notions as we can usually read

First it must be understood that a (smart) contract is some code stored in the blockchain, created and hosted by a transaction that will execute when triggered by another transaction and/or at creation time, as simple as that, that's the main principles

This means that to trigger a contract on a given blockchain you must own some related crypto currency, at least to pay the network for the transactions you want to do, so this is of course a limitation unless a third party holding the contract is willing to pay for the users, which in that case just means that this is a centralized system, second limitation, among others as we will see

Bitcoin

Let's take bitcoin first, it is assumed here that the reader knows the bases of bitcoin transactions, inputs, outputs and utxos at least

Basically contracts are scripts from which an address is derived from in an output that are coded with instructions pushed on a stack, when a contract is triggered (an output of the corresponding contract address is attempted to be spent) the script does execute and if successful the transaction is valid

The executed script is called scriptSig, it can be seen (sometimes but not always) as: first inputs are variables, last one is a function (the contract associated to the output address) using previous variables, each operation is referenced as OP_something who are a limited set of operations performing a specific task when pushed onto the stack, they are hard coded and defined here: https://en.bitcoin.it/wiki/Script

Each output of a transaction contains another script called scriptPubKey, the most used are (simplifying here, segwit will be explained later):

Pay to public key hash (P2PKH): OP_DUP OP_HASH160 |hash160 of pubkey| OP_EQUALVERIFY OP_CHECKSIG

Pay to script hash (P2SH): OP_HASH160 |hash160 of redeem script| OP_EQUAL

OP_RETURN: OP_RETURN followed by some data

|hash160 of ...| is the hash160 of the 33B public key or the redeem script (the contract address), it is then base58 encoded to give an address starting with a 1 for P2PKH and 3 for P2SH (this is only a representation, to know the type of transaction only scripPubKey is relevant)

The transaction that spends an output must build the correct scriptSig which is executed first, then scriptPubKey (from the corresponding output) is executed (after the latest data pushed on the stack is used as the input for scriptPubKey execution in case of p2sh)

Simple example of stack execution:

For simplification please note in what follows that everything inside || is also an OP_PUSHDATA (inside another one generally, and generally is length+data if length|0x4b)

OP_PUSHDATA 31 OP_PUSHDATA 31 OP_PUSHDATA OP_ADD 62 OP_EQUAL --> 011f 011f 0493013e87

OP_PUSHDATA 31 --> 31 is pushed to the stack
OP_PUSHDATA 31 --> 31 is pushed to the stack
OP_PUSHDATA OP_ADD |62| OP_EQUAL --> OP_ADD 62 OP_EQUAL is pushed to the stack

OP_ADD --> add 31 and 1 and remove them from the stack, push the result 62 on the stack, it is followed by 62 from the script pushed on the stack, OP_EQUAL checks that 62=62, 1 is returned, the script is successful

Example 1: standard p2pkh (pay to public key hash)

scriptSig: OP_PUSHDATA signature OP_PUSHDATA pubkey

scriptPubKey: OP_DUP OP_HASH160 |hash160 of pubkey| OP_EQUALVERIFY OP_CHECKSIG

The script executes verifying that has160(pubkey)=|hash160 of pubkey|, then that the transaction was signed with the private key corresponding to pubkey using pubkey, this proves that the one that spent the output owns the associated address, the sequence is the following:

signature
pubkey
OP_DUP OP_HASH160 |hash160 of pubkey| OP_EQUALVERIFY OP_CHECKSIG

signature
pubkey
pubkey
OP_HASH160 |hash160 of pubkey| OP_EQUALVERIFY OP_CHECKSIG

signature
pubkey
hash160(pubkey)
|hash160 of pubkey| OP_EQUALVERIFY OP_CHECKSIG

Note: OP_EQUALVERIFY performs OP_EQUAL then OP_VERIFY, if hash160(pubkey)=|hash160 of pubkey| then OP_EQUAL pushes 1 (true) into the stack, if not 0 (false), OP_VERIFY consumes the top item of the stack and terminates the script if not true (if not it removes the true result from OP_EQUAL from the stack)

signature
pubkey
1 OP_VERIFY OP_CHECKSIG

signature
pubkey
OP_CHECKSIG --> check signature using pubkey

Pubkey is not really supposed to be needed in scriptSig since it can be deducted from the signature (and the known message that was signed), for some reasons bitcoin p2pkh enforces anyway to put pubkey in scriptSig

As you can notice pubkey is now known for further transactions reusing the same address (which is generally not advised), so the first check becomes partially useless (partially because we still need to check that the private key used to sign matches the public key of the output address)

This is the same for p2sh, once an output has been spent the redeem script is known (and then idem for segwit also)

Example 2: p2sh (pay to script hash) - Multisig

Multisig 2 of 3 (a contract is created between 3 persons, someone that wants to spend the transactions must know at least 2 of the 3 keys)

The redeem script is: OP_2 |pubkey1||pubkey2||pubkey3|OP_3 OP_CHECKMULTISIG, everybody creating the multisig transaction know it since they did it together, but they must be at least two to spend the transaction

To create the multisig contract and send some coins to it, you just create a transaction from some places where participants to the contract have bitcoins and send them to the 3-address corresponding to the redeem script, producing the following scriptPubKey in the corresponding output (as we saw above):

OP_HASH160 |hash160 of redeem script| OP_EQUAL

This is used at the end to check that you have the knowledge of the redeem script (which of course people can't deduct from the hash160)

The general behavior is the following in the transaction used to spend this output:

scriptSig:
OP_PUSHDATA sign1
OP_PUSHDATA sign2
OP_PUSHDATA OP_2 |pubkey1||pubkey2||pubkey3| OP_3 OP_CHECKMULTISIG (redeem script)

In fact the real scriptSig in that case is OP_0 |scriptSig|

OP_0 is required because of a bug in OP_CHECKMULTISIG, it pops one too many items off the execution stack, so a dummy value must be placed on the stack

This checks that the transaction was signed with at least two private keys corresponding to two public keys contained in the redeem script, then the redeem script is pushed back and scriptPubKey executes just checking that hash160(script)=|hash160 of redeem script|, this check becomes useless if the same address corresponding to the redeem script is reused since the redeem script became public (but it's quite unlikely that someone finds a different script matching the address to make for example a valid transaction with its own keys and steal the coins)

Example 3: SHA1

Bitcoin's scripting is a bit rigid, can't interact with the blockchain or the outside world but any scriptSig and scriptPubKey can be imagined as long as they are "standard", there is a small number of standard script forms that are relayed from node to node (currently defined here: https://github.com/bitcoin/bitcoin/blob/master/src/script/standard.h#L56), non-standard scripts are accepted if they are in a block, but nodes will not relay them (https://prestwi.ch/the-bitcoin-nonstandard/)

Basically "anyone can spend" transactions are considered as non standard (and anyone interecepting such transaction could just modify it to spend it for himself), ie to be standard a transaction must involve a check signature mechanism

To spend a non standard transaction you must usually find a miner that would accept it (without stealing you) and include it in a block

An example of non standard transaction is the SHA1 bounty, to boost research some people did create a script to reward people that would find a SHA1 collision, the script was created in a transaction containing:

output 1: OP_RETURN |the redeem script in hexa| --> OP_2DUP OP_EQUAL OP_NOT OP_VERIFY OP_SHA1 OP_SWAP OP_SHA1 OP_EQUAL

output 2: |some bitcoins| and OP_HASH160 |20 bytes hash160 of the redeem script in hexa| OP_EQUAL --> contract address

Then some people did fund the bounty additionally making transactions with outputs corresponding to the contract address

The OP_RETURN is just used to indicate what script should be used, a zero value is associated to it and it can't be spent

In the previous example we needed to prove that we knew the script and owned the related private keys, in this one the initial transaction indicates what is the script but only this one can be used and it must execute successfully, ie you can only spend it and get the associated coins if you know a SHA1 collision

So to spend it you need to make a transaction providing a script using the above one that will execute correctly (ie checking that you found a collision and that the script used to spend the output was the above one), there is no story of private key and signing here

This is what happened using the script (scriptSig):

OP_PUSHDATA |data1 resulting to sha1=a|
OP_PUSHDATA |data2 resulting to sha1=a|
OP_PUSHDATA OP_2DUP OP_EQUAL OP_NOT OP_VERIFY OP_SHA1 OP_SWAP OP_SHA1 OP_EQUAL

See: https://www.blockchain.com/fr/btc/address/37k7toV1Nv4DfmQbmZ8KuZDQCYK9x5KpzP

Then, as before, OP_2DUP OP_EQUAL OP_NOT OP_VERIFY OP_SHA1 OP_SWAP OP_SHA1 OP_EQUAL is pushed back onto the stack, scriptPubKey also, to check that its hash160 matches the one of scriptPubKey (ie the correct redeem script was used)

Example 4: Lightning

Lightning network is probably the most advanced and clever sidechain (while some don't consider it as a sidechain but a set of methods that sidechains can use), and decentralized, decentralized here meaning that even if only two people in the world are using lightning they don't depend on any other third parties and/or centralized system, it is designed to work alone so people can't lose anything even if the other parties try to cheat or are not cooperating

But it remains questionable whether this can be used by/for normal people (ie without the help of a centralized system...)

Basically the principles are that two people lock some bitcoins they own in a multisig 2 of 2 transaction (the funding transaction), then transact coins via the lightning channel they have opened, which consists in creating valid transaction between them spending the multisig transaction output with correct balances in the outputs, but off-chain, the transactions are not broadcasted, once they decide to close the channel (ie they don't foresee to make further business together), they just send the latest transaction to the network, each time a lightning transaction happens they both sign a transaction with the correct status, then to close the channel anyone can just send this transaction to the bitcoin network, you can stop here if you don't want to understand what follows, in reality it's a little bit more complicate

Additional mechanismes are used to be able to transact funds via multiple hops and multiple channels (A sends funds to D via B and C), because of course you are not going to open a channel with D just to buy a pizza, but you need to be able to communicate with D to achieve this and exchange a secret

Someone could cheat and broadcast an earlier transaction at his advantage, that's why the process maintains for each state two transactions

There are different proposals here but the lightning paper defines a mechanism where the cheater will lose everything he got (and everything he put in the initial transaction)

Let's see in more details how this works, first Alice and Bob send funds to the "Funding transaction", they create one transaction spending two of their inputs (let's say 0.4 for Alice and 0.6 for Bob, normally it should be 50/50 but let's simplify) to the agreed 2-of-2 address with then one output of 1 BTC (let's forget about the network fees here and below to simplify again), this process uses a special mechanism to be able to refund Alice and/or Bob if one does not cooperate, at the end the multisig address is funded with 1 BTC with a valid transaction signed by both that is sent to the network

Each time Alice and Bob exchange coins on the Lightning channel they create first two "Commitment transactions" C1a and C1b, let's say Alice sends 0.1 to Bob:

The input is the output of the funding transaction, and associated scriptSig is |sign Alice||sign Bob||2-of-2 redeem script of the funding transaction|, the output are:

C1a (sent signed by Bob to Alice, then only Alice can broadcast signed by her):
output 0: address Revocation_script_1 0.3 BTC
output 1: address pubkey Bob 0.7 BTC

C1b (sent signed by Alice to Bob, then only Bob can broadcast signed by him):
output 0: address pubkey Alice 0.3 BTC
output 1: address Revocation_script_1b 0.7 BTC

At this point both of them could broadcast a transaction reflecting the state of the channel, now Alice sends 0.1 again to Bob:

C2a (sent signed by Bob to Alice, then only Alice can broadcast):
output 0: address Revocation_script_2 0.2 BTC
output 1: address pubkey Bob 0.8 BTC

C2b (sent signed by Alice to Bob, then only Bob broadcast):
output 0: address pubkey Alice 0.2 BTC
output 1: address Revocation_script_2b 0.8 BTC

But all of a sudden Alice is not happy with this and decides to cheat and broadcast C1a, then stealing Bob, that's why there are the two revocation scripts which basically are:

Revocation_script_1: 63 |one_time_pubkey_Alice + some_pubkey_Bob| 67 |nb of blocks| b2 75 |pubkey Alice| 68 ac, which is:

OP_IF
|one_time_pubkey_Alice + some_pubkey_Bob|
OP_ELSE
|nb blocks|
OP_CHECKSEQUENCEVERIFY 
OP_DROP
|pubkey Alice|
OP_ENDIF
OP_CHECKSIG 

Revocation_script_2 is the contrary, then output 0 in C1a is the address corresponding to the revocation_script_1, normally hash160(63 |one_time_pubkey_Alice + some_pubkey_Bob| 67 |nb of blocks| b2 75 |pubkey Alice| 68 ac) but in fact sha256(63 |one_time_pubkey_Alice + some_pubkey_Bob| 67 |nb of blocks| b2 75 |pubkey Alice| 68 ac) since segwit is used, but let's not complicate things here (see the segwit section below for more details), output 1 of C1a is just the address of Bob

So before creating/signing C1a and C1b Alice and Bob exchange a secret (Alice sends one_time_pubkey_Alice to Bob and vice versa)

To spend output 0 of C1a a transaction must be made with scriptSig= |signature| redeem_script_1, the signature must match either one_time_pubkey_Alice + some_pubkey_Bob or pubkey Alice for the transaction to be valid

In elliptic world the private key of pubkey1+pubkey2 is privkey1+privkey2 (with elliptic maths)

But we can notice that Alice will be able to spend from this output only after nb_blocks (which is 100, ie ~16 hours), Bob cannot broadcast C1a and spend output 0 to himself since the transaction must be signed by Alice, and he does not know the private key corresponding to one_time_pubkey_Alice

When Alice and Bob decide to go to state 2 and build C2a/C2b, Alice sends to Bob the private key corresponding to one_time_pubkey_Alice and Bob does the same

So if Alice broadcast later the wrong state C1a then Bob will immediatly spend output 0 (ie before the nb_blocks) using the private key corresponding to privkey_of_one_time_pubkey_Alice + privkey_of_some_pubkey_Bob

Alice can't spend this output since she does not know privkey_of_some_pubkey_Bob

And vice-versa for Alice, of course Alice and Bob should not reuse the one_time adresses since the associated private keys are known each time a new state is set

This is a little simplified here since ligthning add some other mechanisms but the intent is to show other scripts examples and how determinist it is in the case of lightning, you can notice that to close the channels at least two transactions are needed (+ 1 for the funding transaction)

Others like eltoo propose an alternative regarding the revocation punishment, now the main principles remain the same

We consider the lightning case as different than what we describe below, things (addresses, coins) are not mixed, nothing is virtual, everything is just logical and determinist, no third parties are needed to enforce anything, but the system has some drawbacks: you must watch what the other party is doing (or use a third party in case Bob is in a trek in Nepal when Alice is trying to cheat...), you can make mistakes (like broadcasting an earlier state) and lose all of your coins, and you must be able to communicate with the other party which can raise some privacy concerns

Example 5: Segwit

Segwit is about the same as above except that scriptSig elements are put at the end of the transaction in the witness field, the number of inputs indicate how many scriptSig are there and each one starts by a number indicating the number of elements

The intent for segwit is to avoid transactions malleability (ie avoid that the same transaction can have different formats) and increase blocks size from 1 to 4 MB with a soft fork based on a concept of "weight" replacing the usual accounting of transactions' size in bytes, so the block limit is 4M"weight units(WU)" where one witness byte counts for 1WU and non witness byte for 4WU, in practice it gives blocks with a size of ~2MB

Strangely most of segwit transactions are bigger than non segwit ones, but in terms of weight (for network fees calculation for example and maximal block size capacity) they are something like ~2 times smaller, in short segwit transactions are bigger but count for smaller, this is virtual to promote segwit transactions and avoid another hard fork based solution, nodes that do not support segwit will receive the transactions without the witness part and will validate/broadcast them since they see it as an anyone can spend transaction (but can't definitely validate them to include them in a block since the witness part is of course required)

Unlike OP_PUSHDATA, {length} in this section refers to a varlen as defined in the bitcoin protocol

{20-byte-key-hash} is the hash160 of the public key

{32-byte-hash} is the sha256 of the redeem script

P2WPKH

witness:
	02 nb of elements
	{length}{signature}
	{length}{pubkey}
scriptSig: 00(empty)
scriptPubKey: 16 length
	00 witness version
	14{20-byte-key-hash}

The signature is verified as |signature| |pubkey| OP_CHECKSIG and the HASH160 of the pubkey in witness must match the witness program {20-byte-key-hash} (ie hash160 of (0014{20-byte-key-hash}), to check that the private key used to sign corresponds to the public key in witness), the witness must contain exactly two items

The address is the base58 encoding of hash160 of (0014{20-byte-key-hash}) or related bech32 address corresponding to {20-byte-key-hash} witness program

P2PWKH nested in P2SH

witness:
	02 nb of elements
	{length}{signature}
	{length}{pubkey}
scriptSig: 17 scriptSig length
	16 length
	00 (witness version)
	14{20-byte-key-hash}
scriptPubKey: 17 length
	OP_HASH160 |20-byte-script-hash| OP_EQUAL
	(a914{20-byte-script-hash}87)

The signature is verified as above + scriptSig pushed to the stack and scriptPubKey executed checking that hash160 of (0014{20-byte-key-hash}) is |20-byte-script-hash|

This is less efficient than previous method but the address is compatible with usual p2sh transactions

The address is the base58 encoding of hash160 of (0014{20-byte-key-hash}) or related bech32 address corresponding to {20-byte-key-hash} witness program

P2WSH (1 of 2 multisig)

witness:
	03 nb of elements
	00 OP_0
	{length}{signature1}
	{length}{1 |pubkey1| |pubkey2| 2 CHECKMULTISIG} (redeem script)
scriptSig: 00 (empty)
scriptPubKey:  22 length
	00 (witness version)
	20 {32-byte-hash}
	(0x0020{32-byte-hash})

The last item in the witness (the "witnessScript" {1 |pubkey1| |pubkey2| 2 CHECKMULTISIG}) is popped off, hashed with SHA256, compared against the {32-byte-hash} in scriptPubKey, and deserialized {1 |pubkey1| |pubkey2| 2 CHECKMULTISIG}

The script is executed with remaining data from witness 0 |signature1| then 0 |signature1| 1 |pubkey1| |pubkey2| 2 CHECKMULTISIG

The address is the base58 encoding of hash160 of (0020{32-byte-hash}) or related bech32 address corresponding to {32-byte-hash} witness program

P2WSH nested in P2SH(1 of 2 multisig)

witness:
	03 nb of elements
	00 OP_0
	{length}{signature1}
	{length}{1 |pubkey1| |pubkey2| 2 CHECKMULTISIG} (redeem script)
scriptSig: 23 scriptSig length
	22 length
	00 (witness version)
	20 {32-byte-hash}
	(0x220020{32-byte-hash})
scriptPubKey: 17 length
	HASH160 |20-byte-hash| EQUAL
	(0xA914{20-byte-hash}87)

The signature is verified as above + scriptSig pushed to the stack and scriptPubKey executed checking that hash160 of (0020{32-byte-hash}) is |20-byte-hash|

The address is the base58 encoding of hash160 of (0020{32-byte-hash})

txid calculation for segwit:

A normal txid is the reverse of double sha256 of [nVersion][txins][txouts][nLockTime]

A segwit txid is the reverse of double sha256 of [nVersion][marker][flag][txins][txouts][witness][nLockTime]

Example 5-bis: Taproot (P2TR)

Taproot is the latest upgrade on Bitcoin

The intent is to simplify the validation of a transaction and whatever it is to try to make it look like a "normal" transaction, which is just sending coins to another address without scripts (like multisig) being detected, and keys being revealed, in addition it results in significant space savings and savings to verification times

Of course it's clearly designed for Lightning, to hide Lightning transactions

Taproot is very complicate, so let's explain here the basic principles

First the destination address of a taproot transaction is:

01-Q

Where 01 allows to identify a P2TR transaction (witness program since the spending data are stored in the witness fields), and Q is the aggregate of a public key P and a public key M, knowned as "tweaked" address, where P and M will be explained below

Two options appear here on spending and will be explained below again: the key path where you provide a signature for public key P, or satisfying one of the scripts contained in the Merkle tree, where M is the root of the Merkle tree (known as MAST), and where the Merkle tree root is the hash of different scripts, in that case you must disclose the script (and have the knowledge of the other hashes in the Merkle tree to reach the root), so it becomes the same as an usual transaction and you did not hide anything at the end

This is using Schnorr signatures, which allows P to be the concatenation of several public keys (multisig, so you don't have to disclose each key on spending, multiple signers can produce a joint public key and then jointly sign with one signature), therefore to build the signature for the key path you must do it with the others (if applicable, in case of multisig deal for example or closing a Lightning channel)

In that case the transaction appears as a "normal" one, ie nobody for example can detect that it was a multisig one

If not you can choose the second option (tapleaf) and do a transaction that will unveal one of the scripts in the Merkle tree (cheating on lightning for example or detecting someone cheating, see above) along with the necessary Merkle branch hashes of other scripts, that you know since the transaction was made by you and others if applicable

We simply explain taproot here but really no more non understandable technical details are required, for more information please look at https://blog.bitmex.com/the-schnorr-signature-taproot-softfork-proposal/ and https://blog.bitmex.com/taproot-demonstration/

Example 6: Sidechains

Here we go, sidechains on bitcoin (or drivechains), we gave an example in our Tether example How stablecoins really work (Tether example), how to crack them and why you should take care if you use them, the principles are always the same: make a transaction on the main chain doing some stuff related to the sidechain, eventually make subsequent transactions coming from the sidechain to update the previous one and store in the blockchain what happened on the sidechain (like sending coins or tokens or a reference linked to the sidechain (like a file))

This is very vague intentionally because "some stuff" can be whatever you like, basically the intent is to offload the blockchain via the sidechain, make faster less expensive transactions, store what can't be stored in the blockchain, etc and then at the end store the result/proof/whatever related to it in the blockchain

The most trivial system is the one we described for Tether, just storing "sidechain transactions" in the bitcoin blockchain, sidechain transaction can be moving coins or invented tokens on the sidechain, or storing references to some supposed assets on the sidechain, etc

More subtile systems have been designed, like locking coins on bitcoin blockchain, doing things in the sidechain (exchange bitcoins between people) and unlocking them with the result of what happened on the sidechain

Locking coins can be based on the timelock, sequence or sending them to a multisig contract, then the coins will be unlocked according to what happened on the sidechain with a correct transaction, for example as we saw above when people decide to close a lightning channel with the latest transaction signed by the two people involved reflecting the latest status of the coins/balance for both

Other systems envision the same thing involving some third parties to validate the multisig transaction, drivechains for example with is another level next to the sidechain designed to validate what comes back from the sidechain, escrow or oracles, everything is the same at the end

It's difficult to summarize whatever exist since as you can see whatever can be invented, it always end up with making a valid transaction on the bitcoin blockchain to update the status and correlate it with what happened on the sidechain

The communication of states between the main blockchain and the sidechain is often referenced as "SPV proof", by clients implementing the Simple Payment Verification protocol who are just to summarize light bitcoin clients retrieving efficiently targeted information from the blockchain without needing to host it like a full node, the SPV proof is just the result of the request, it has been extended in blockstream white paper to define new OP_codes to lock coins in the blockchain and unlock them by a SPV proof, it could be seen as a kind of evented system but is not (despite it is often presented as such), the SPV proof is just an answer to a request or a request

The sidechain can (or not) then implement a system (often wrongly referrenced as a blockchain) and store things associated to "some stuff", again it can be anything you want, assets, tokens, coins

The enormous drawback of this is that this is necessarily (at least up to now although some claim it is not) a centralized system that is really virtual, the only guarantee that this system provides is that people making transaction with "some stuff" (to send token they own for example) own the bitcoin addresses associated to |some stuff| in the centralized system and to make sure that the related operation is stored forever in the blockchain as a proof that it happened (or that it has been faked, for example you can store a transaction sending to someone 10K Tether that you don't have), that's all, it does not provide any security regarding what can happen to "some stuff" if the centralized system stops operating

The second one is that Alice and Bob must own bitcoins to transact "some stuff" unless the sidechain handles it, which in that case means centralization again

In addition in most of implementations real bitcoin addresses are mixed between bitcoin blockchain itself and the sidechains, which can be confusing

Ethereum

Let's consider Ethereum now, things work differently and can become quite complicate as we will see, easier to implement but prone to (sometimes huge) mistakes and hacks

On Ethereum there are a few different types of transactions:

Regular transactions: a transaction from one account to another.
Contract deployment transactions: a transaction without a 'to' address, where the data field is used for the contract code.
Execution of a contract: a transaction that interacts with a deployed smart contract. In this case, 'to' address is the smart contract address.

A submitted transaction includes the following information:

recipient – the receiving address (if an externally-owned account, the transaction will transfer value. If a contract account, the transaction will execute the contract code)
signature – the identifier of the sender. This is generated when the sender's private key signs the transaction and confirms the sender has authorized this transaction
nonce - a sequencially incrementing counter which indicates the transaction number from the account
value – amount of ETH to transfer from sender to recipient (in WEI, a denomination of ETH)
data – optional field to include arbitrary data
gasLimit – the maximum amount of gas units that can be consumed by the transaction. Units of gas represent computational steps
maxPriorityFeePerGas - the maximum amount of gas to be included as a tip to the validator
maxFeePerGas - the maximum amount of gas willing to be paid for the transaction (inclusive of baseFeePerGas and maxPriorityFeePerGas)

The signature allows to check that the sender really owns the sending address

On Ethereum the notion of account describes whether a privately hold address or a contract one:

Externally-owned

Creating an account costs nothing
Can initiate transactions
Transactions between externally-owned accounts can only be ETH/token transfers
Made up of a cryptographic pair of keys: public and private keys that control account activities

Contract

Creating a contract has a cost because you're using network storage
Can only send transactions in response to receiving a transaction
Transactions from an external account to a contract account can trigger code which can execute many different actions, such as transferring tokens or even creating a new contract
Contract accounts don't have private keys. Instead, they are controlled by the logic of the smart contract code

To create a contract you generate a transaction with no destination with the data field populated by some code you created using solidity Ethereum language, the contract will just execute, the execution resulting in storing that code in fact, a contract address is derived from it, a storage is associated to each account (ie address)

Next, the contract will execute when you send a transaction to the contract address, with parameters and references to trigger the contract's functions, this works also between contracts using messages which are similar to transactions but not stored on the blockchain neither signed (Internal transactions)

The code can execute Ethereum's internal functions that will operate on the blockchain or trigger events (stored with the contract in the blockchain) that will be caught by external systems such as sidechains implementing gateways/oracle (via the RPC API for example), like bitcoin the ethereum network can't natively communicate with the outside

For more details you can read https://ftsrg.mit.bme.hu/blockchain-ethereumlab/guide.html

"Ethereum is a generic blockchain-based computing platform where nodes in a peer-to-peer network are maintaining the ledger. Nodes run the Ethereum Virtual Machine (EVM) and execute transactions and contracts compiled into EVM bytecode. The main entities on the network are the accounts, identified by their 160-bit addresses. There are two kinds of accounts as already explained.

An externally owned account is associated with a balance in Ether, the native cryptocurrency of Ethereum. It is typically owned by human users (via public/private keypairs). A contract account, in addition to its balance also stores the compiled contract bytecode and the data (state) associated with the contract.

Contracts are usually written in a high-level language (such as Solidity) and then compiled into EVM bytecode. A compiled contract can be deployed to the blockchain by a special deployment transaction. From that point on, users or other contracts can interact with the deployed contract by issuing transactions to its address (with a zero ETH Value if relevant for the contract called). The transaction contains the function to be called (with its parameters) and an execution fee called gas. Optionally, some value of Ether (the native cryptocurrency of the Ethereum platform) can also be associated with the call. The nodes then execute the transaction by running the contract code."

You must pay the network using gas to execute the contracts (or transactions), each operation consumes some amount of gas depending on what is required (memory, cpu, storage), if your transaction does not run out of gas it will be successful

Example: simple contract

Contract address A : 
	function transfer_Eth(val, address){...}

If Alice wants to trigger the function transfer_Eth of contract A to send 1 ETH to Bob, she will do a transaction from her address to the contract address with a data field specifying:

[|reference to transfer_Eth function| |val=1| |address=Bob|]

Then contract A will execute and send 1 ETH to Bob, Alice must pay the gas for that transaction

This one is of course a stupid contract since Alice could send the coins directly to Bob without using a contract, this is just to understand how things work, another one (from solidity docs):

Contract B simpleStorage:

uint storedData;
function set(uint x) public {
    storedData = x;
}
function get() public view returns (uint) {
    return storedData;
	
}

Same principle, transaction to B with [|reference to set| |val=10|] --> 10 is stored in the blockchain

Then transaction to B with [|reference to get|] --> this is asynchronous since the transaction needs to be confirmed, then events must be used and added to the above code to get the return result 10

Example 2: tokens and sidechains

Contract address A : |some stuff that defines a token and or a sidechain|

|some stuff| again can be anything that will be executed one Ethereum network and interpreted by a centralized system via events

You can define for example:

A:  function create_myToken(){...}
	function new_Token(params) {...}
	function send_myToken(token,address) {
		event: do internal transaction from token address xxx to ethereum address |address|
	}
	function sayHello(destination){event: send "hello" sms to destination}

Then you can create internal tokens which can be anything (coins, images, whatever you like) on the sidechain, or via a transaction calling new_Token (but then the creator has to pay the network)

ERC20 tokens for example can be totally virtual (but not all), you can just create them with only one transaction specifying the supply, now even if they can be totally virtual you must pay the network each time a transfer happens, other tokens like ERC721 non fungible tokens might require some minting and pay the network for it each time, and now we have ERC-1155 mixing ERC20/ERC721, it's useless to go through all the possibilities, basically whatever is not mined/minted has just no value, can be call virtual and can't be trusted

Everything related to the sidechain blockchain and tokens might be stored on a centralized system, like bitcoin the Ethereum layer is only used to guarantee that someone doing something for a token it owns on a given address really owns that address and to make sure that the related operation is stored forever in the blockchain as a proof that it happened (and depending on the contract to make sure that the operation is allowed and owners known by the blockchain, for example transfer of ownership of ERC721 tokens)

Alice can send a token she owns to Bob making a transaction triggering send_myToken(val, Bob's address), the output might show something like:

Eth transaction Alice to Contract A with data [|reference to send_myToken function| |token| |address=Bob|]
	internal transaction address xxx to Bob's address

Again we are mixing the addresses between Ethereum mainnet and the sidechains and to exchange tokens people must own ethers, an easy mistake would be to send some ETH to some smart contract addresses not designed for this, which then would be lost

And again, even if everybody is claiming that it will be fixed, this is a very centralized system that can collapse at any moment, or can be forked and faked, for example if an ERC721 sidechain shuts down people are just left with tokens that they own on ethereum blockchain not linked to anything any longer, so of zero value

So it's quite possible to create whatever you like as far as you and/or the users have the ethers to sustain the system, a specific drawback of Ethereum is that this generates quite a lot of useless transactions flooding the network making it difficult to scale, even if sidechains are claiming to handle things outside of the mainnet until they are final

Example 3: ERC-xyz tokens

Ethereum,like Bitcoin, does not make things easy for people, using some barbarous names like ERC-xyz, which stands for Ethereum Request for Comment for xyz proposal and is derived from EIP-xyz which stands for Ethereum Improvement Proposal xyz

If we consider smart contracts only, it's just a way to specify a smart contract, reviewed and validated by the Ethereum community, defining the main methods and standard libraries

Then you can build around those methods, extend them and define your customized ERC-xyz token (ERC20, CryptoKitties, Opensea, NFTs, etc)

Of course you should take care not to introduce possible hacks

Example 4: DApps, NFTs, DEX, Defi and DAO

Things can look to become a bit more complicate here, but not so much in fact, and obviously most systems are centralized and insecure, please read the very good article from Moxie Marlinspike My first impressions of web3, we will mention it again below

What are DApps, NFTs, NFT, DEX, Defi and DAO? To make it short: not a lot in their current form, except those that are really decentralized, they are built on top of smart contracts which just allow to use some predifined standards (ERC-xyz) to represent them

For Dapps and NFTs, you can build/extend them "minting" them to a smart contract, which just means making transactions to this smart contract that will get registered in the Ethereum blockchain, the reference to the related item being wrapped inside the transaction and recognizable by the smart contract operator, each transaction must pay the network, so some value is there

The reference is often a web link or some storage owned by a centralized system again (the operator of the contract), then if the centralized system collapses, you lose your assets (not exactly depending on the kind of asset because you can have a copy of it, like everybody in the world, but you lose the ownership of it)

DApps are something maintained by a centralized system and ruled by a smart contract where people can interact with making transactions to interact with the asset

NFTs are the same

DEX is the same too, an exchange maintained by a central authority where you can swap ERC-20 tokens, first you send some ERC-20 tokens to the DEX contract, then you approve it to authorize the exchange to transfer them, then you sign the transaction sent by the exchange to swap your tokens to other ones, the exchange making the calculation for the price differences between tokens

Defi, DAO, etc, the same

As you can see the "D" in the names is supposed to mean "Decentralized", but as you understand it's not always decentralized

And you need ETH to interact with those contracts since you must pay gas for the transactions, which can turn to be quite expensive

Example 5: Wrapped Tokens and Interacting with a smart contract

Wrapped tokens, this one is not bad since really decentralized

A Wrapped token is an ERC-20 token, it is used to create a pivot token (the wrapped token) to interact with ERC-20 tokens (ie transfer ERC-20 token X to ERC-20 token Y)

Let's take WETH as an example, you will wrap ETH making a transaction to the WETH smart contract (with the value field corresponding to the number of ETH wrapped) which will store your ETH to the WETH contract address and credit WETH to the destination address in the contract parameters (the destination address being most likely the same than the transaction sending address)

Unwrapping is about the same, making a transaction to the WETH smart contract (with a value field here set to 0), which will send back your ETH to the specified address and decrement your WETH accordingly (as well as the corresponding ETH owned by the smart contract), proving that you own the WETH sending address (most likely the same that you used to wrap your ETH)

Again, your are mixing mainnet addresses with smart contracts internal addresses

Now, let's try to do something simple: get your WETH balance for a given address, some tools exist like Etherscan (the Ethereum explorer), Metamask (an Ethereum wallet), Ethereum "clients" based on RPC calls, etc, surprisingly those tools rely on web servers to retrieve the requested information (like Infura)

For example go to the WETH contract and click "Read Contract": https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#readContract

You can activate the dev tools, then enter an address (example: 0xA9283f9E3B4F549AcCeEF63bBfF76f958e1f57c7) for the BalanceOf WETH ERC-20 function, and you will see that the result comes from https://node1.web3api.com/ (XHR call to Etherscan)

Without going into details here, you can interact with a smart contract public functions (and access its storage for example) without sending a transaction, to alter the storage or interact with private functions (by the contract itself only) you must do a transaction, depending on what tool/wallet you are using you don't really know if the results come from a queried full node or from intermediates states stored by a platform

Like Bitcoin nodes, Ethereum has a RPC interface to retrieve some information directly from the blockchain but this is limited to mainnet information (as opposed to smart contracts for example)

Example 6: Opensea

We can not review here all the use cases of Ethereum by many platforms/tokens but let's review one of the most famous one: Opensea

There are different options and it's not simple, so let's consider that transaction https://etherscan.io/tx/0x2ef2804ea48de9c8f5faa7bfa5f081c7378d9e34d5e76071da6341b89475e9eb/advanced , as we can see the buyer transfers WETH to the seller, Opensea gets its fees, and the NFT gets transfered to the buyer

First the buyer sends ETH to the WETH contract, then approves Opensea to spend it to the seller, this makes two transactions, the advantage of using WETH if for the seller to be able to make many bids with the same amount of WETH, with no gas fees that time

When the buyer confirms the offer he signs the Opensea proposed transaction from its wallet which will then perform all the steps mentioned above, including Opensea smart contract interacting with WETH smart contract with internal transactions (which, again, do not need to be signed since they are internal and already approved by the sender, one smart contract will query another one and the result will just be what it is authorized to do by the receiving smart contract)

Example 7: Moxie's experience

Again we can wonder what is really decentralized, and Moxie noticed the same things :

"Almost all dApps use either Infura or Alchemy in order to interact with the blockchain. In fact, even when you connect a wallet like MetaMask to a dApp, and the dApp interacts with the blockchain via your wallet, MetaMask is just making calls to Infura!

These client APIs are not using anything to verify blockchain state or the authenticity of responses. The results aren’t even signed. An app like Autonomous Art says “hey what’s the output of this view function on this smart contract,” Alchemy or Infura responds with a JSON blob that says 'this is the output,' and the app renders it."

"Looking at many of the NFTs on popular marketplaces being sold for tens, hundreds, or millions of dollars, that URL often just points to some VPS running Apache somewhere. Anyone with access to that machine, anyone who buys that domain name in the future, or anyone who compromises that machine can change the image, title, description, etc for the NFT to whatever they’d like at any time (regardless of whether or not they “own” the token)."

"What I found most interesting, though, is that after OpenSea removed my NFT, it also no longer appeared in any crypto wallet on my device. This is web3, though, how is that possible?"

"All this means that if your NFT is removed from OpenSea, it also disappears from your wallet. It doesn’t functionally matter that my NFT is indelibly on the blockchain somewhere"

Indeed the NFT is still there on the blockchain but the wallets can't see it

"For example, to accept a bid on my NFT, I would have had to pay over $80-$150+ just in ethereum transaction fees. That puts an artificial floor on all bids, since otherwise you’d lose money by accepting a bid for less than the gas fees"

Conclusion

What is the best system? As we can see bitcoin and ethereum can look to be working very differently, but not so much in fact, at the end the same principles are used

Bitcoin is more rigid, Ethereum more malleable but vulnerable to code mistakes, which happened several time in the past and even forced Ethereum to roll back the blockchain (DAO hack). Another representation of Ethereum is that it could be compared to a lake where many rivers are going into, at a certain point of time the lake might overfill or get overloaded, this has been fixed since Ethereum is now burning coins and with the switch to proof of stake, it might even become deflationary (ie more coins are burned than created), which anyway does not help decentralization because while it's the very same between pow and pos regarding network centralization, the difference is that pos will centralize also the coins, because the nodes don't need to reinject them to pay for the staking, unlike miners

Somewhere Ethereum looks like a bit of a mess being flooded by useless transactions, but we can see that smart contracts operate by themselves and if well designed are not supposed to give power to a central authority, except that in practice a central authority is usually required to interact with those smart contracts

For both systems sidechains can generate objects to which some market values could be attributed to, but that's where things turn to be virtual against electronic, intrinsically they might have no value if not decentralized

Why finally are sidechains not decentralized? It can be and everybody is saying that it will evolve that way and working hard on it, but basically it requires to create another bitcoin like or ethereum like network to handle a sidechain's decentralized blockchain like system, so to restart from zero, invent maybe a new protocol for this, deploy nodes, make sure that they are decentralized, not easy...

And if we consider the reality today, ie that bitcoin and ethereum are certainly not decentralized systems (like any crypto currency network), because the number of full nodes can't be compared to what is required for a real decentralized system, mining (or coin ownership) is very centralized, as well are devs, then the path for sidechains to be decentralized will most likely be long

But the solution to all of this is obvious, for unknown reasons everybody want sidechains to behave like blockchains, this is useless because the goal of the sidechain is to securely manage the intermediate states until the final one is stored on the blockchain used, and the reference to it if applicable securely stored on the sidechain, there is no need to keep forever the intermediate states, therefore the sidechain can "just" be a secure decentralized system that has nothing to do with a blockchain and destroy the intermediate states

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment