One of the reasons why I am interested in the blockchain world is it's complexity. The web3 backend that executes the client's transaction is an intricate "system of systems" that relies on concepts from variety of disciplines such as databases, P2P networking, cryptography, virtualization, Turing-complete machines, etc.. Actually, understanding blockchain was one of the most difficult things for me and kind of a milestone on the road to professional / nerdy growth (after C pointers, assembly and math).
My instinct tells me that where the complexity is present, vulnerabilities follow. Well, this correlates with available data on the current (2023) threat landscape that informs us that ransomware and crypto attacks are in top 3 when it comes to generating damages [source needed]. Before I delve into specific categories of threats, I wanted to prepare a threat model to be able to enumerate all the interesting vectors.
My approach to threat models is to start from the client's perspective. I am exploring all of the use cases and it allows me to understand the significance of the product / service components. Good understanding of all components and their interconnections forms a foundation of a good threat model.
In the diagram above we can see a user's wallet which she is using to interact with the blockchain. The user wallet in it's most basic form is the private key of an asymmetric keypair. The public key or its part is forming the address that constitutes one of the parties participating in the transaction on the blockchain.
Blockchain is a chain of blocks (sic!) that hold information about all transactions between participants (represented addresses) since its inception (the genesis block). The fundamental feature of the blockchain and its advantage over other forms of databases (centralized or decentralized) is trust. Thanks to an intricate web of protocols and algorithms, well-designed blockchains achieved what's called Byzantine Fault Tolerance. In simple words, they can trusted not to alter the immutable database of transactions. So the assumption here is that we can trust the data in the blockchain because it's very difficult to compromise it's integrity (we'll be discussing various challenges to this assumption later).
Ok, let's go over use cases.
Ethereum node operator starts his node (let's assume full sync mode). The node is trying to discover other nodes via the P2P network. A lot of information on the Ethereum P2P protocol ca be found in the developer's documentation. For now, let's just say that P2P networking is separated into two layers - execution layer and consensus layer.
Introducing the new node into into both networks is being done with use of the discovery protocol. This is a critical moment because up until this point a fresh node (with no snapshot saved) has no knowledge about the state of the blockchain. In order to synchronize with the network and get a full picture of the state it first needs to discover its fellow nodes and exchange information with them.
The discovery protocol involves a form of Kadmelia protocol. Fun fact - is a popular and efficient protocol that has been used throughout various P2P popular networks including eMule, BitTorrent AND the infamous Storm botnet. I am unfortunately old enough to say that I remember these "major historical events". As a first step the node is reaching out to the hardcoded bootnodes, the nodes responsible for "introducing" the node into the network, i.e. redirect it to other nodes that it will retrieve further data about the network status and who become its neighbours. Assuming that the introduction into the network went smoothly and the node is now interconnected with its neighbors in both layers, now the data exchange starts.
The two layers are communicating with each other on the local node via the RPC connection to perform their respective functions.
Now a user that has a wallet in the network with sufficient ETH balance to pay (both in terms of gas fees and transaction value) can submit a transaction for the execution (Image 1, step 1) to a node (e.g., JSON-RPC-based Ethereum gateway). The transaction get's added to its mempool and the node uses the Gossip protocol over the execution layer to propagate the transaction throughout the network (Image 1, step 2).
Eventually, the transaction lands in the mempool of a node that has been selected to be a block producerfor a selected slot (Image 1, step 3). In case of the Ethereum network which is currently using PoS-based consensus protocol nodes are selected as block producers among a set of validators. The validators are nodes that decided to stake a significant amount of money as a collateral that exposes them to a set of incentives and punishments to encourage their honest behavior on the network. It means, it doesn't pay off for them to tamper with your transaction with their execution client.
If the node is the block producer, it asks it's execution client to create a new block using the transactions from the local mempool. It selects transactions from a mempool and bundles them into a block (Image 2, step 4), executes them, updates the global state and calculates a block hash (Image 2, step 5). The state of the blockchain refers to the global database that encompasses all data of all accounts ever interacted with on the blockchain. This means, all balances of all accounts and all contracts states of all contracts. The state of the blockchain is represented by a hash value. The block producer proceeds to broadcasts the block to its neighbors over the consensus layer.
The consensus client of the validator nodes is responsible for receiving the block containing the transaction, verifying its correctness and attest to it. It pre-validates the block, "unwraps" it and hands over the transactions contained within to its own execution client, which is responsible for re-execution of all of the transactions in the block and ensure that the hash attached is correct. If that's the case, validator issues a block attestation and propagates it throughout the network through the consensus layer (Image 2, step 6).
Remaining validators in the network are repeating this process. This means that the block is collecting attestations from the nodes. While doing so, the block is first added to the head of the chain, and after some time, once it collects sufficient attestations, is considered finalized and becomes part of the blockchain.