I’m very proud of my favorite personal project BlockchainJ: build a Blockchain in Java, from scratch, using TDD (Test-Driven Development). Previous posts:
Building a Blockchain: Introduction
Building a Blockchain: Main Entities
Building a Blockchain: Serialization
Building a Blockchain: Key Value Stores
Building a Blockchain: Subroutines in the Virtual Machine
Building a Blockchain: Towards Beam Synchronization
It’s based in Ethereum, so it supports smart contracts. I’m using the same bytecodes, to keep compability with the ecosystem (like Solidity compilers and wallets).
Today, I want to describe my current implementation of how to execute an smart contract in the VirtualMachine.java class. A diagram:
The left classes are injected at constructor time. The right classes are injected when I want to execute the code. Using TDD, I could change the design at any moment, without pain. Really, I changed this design in the last week, without much effort. The instances injected at constructor time are:
BlockData.java: having the information of the current block under execution, like number, difficulty, chain id (yes, I added chain id to the block header)
MessageData.java: it has the information about the current call: sender address, origin (original transaction sender) address, receiver address (usually the current smart contract under execution), gas limit, gas price, etc. Maybe I could split this data in two: TransactionData, and MessageData. The first one would be inmutable ALONG the execution of the smart contract and its internal call. Remember: an smart contract could INVOKE ANOTHER smart contract, and again, having many levels of invocations. This instance is inmutable during the invocation of the current code under process.
ExecutionContext.java: an interface that allows the access and update of the world state: accounts and contract storages. The important thing to take into account: after execution of the code, its changes could be commited or rollbacked, depending on the success or failure of the execution (ie a revert should raise a rollback, so the world state is not changed; but only a rollback of this level: an smart contract A could call smart contract B, then B reverts, then the changes OF B (ONLY B) are rollbacked; then the smart contract A could decide to bubble up the revert or not).
At execution time I provide to the virtual machine two instances:
Storage.java: the current smart contract storage (a minor but critical detail: it should be provided by the current execution context, to live under the umbrella of rollback or commit; this coupling says to me something: I could review this design later, TDD rules!)
Code: a byte array with the bytecodes
At the end, the execution returns an ExecutionResult.java with information about the success or failure of the execution, error message, etc.
Angel “Java” Lopez