Ganache-Core Notes #2: Exposing methods of the underlying vm
Now that I learned how to add a rpc method method in the ganache-core, I wanted to be able to modify the blockchain data directly. Ganache-core simulates a geth client and also a blockchain, it simulates how a geth client receives rpc requests from websocket or http, then query or send a transaction to the blockchain.
I followed the execution of eth_sendtransaction
. The eth_sendtransaction
in geth_api_double.js
queues the transaction information to the statemanager.js
in action_queue
then the state manager immediately invokes processNextAction
. The transaction is then pulled out from the queue and passed into processTransaction
.
After that, ganache-core
basically packages the transaction into a block, then invokes the runBlock
method of the underlying blockchain’s virtual machine. This can be seen in processBlock
of lib/blockchain_double.js
:
1
2
3
4
5
6
7
8
9
const results = await vm
.runBlock({
block: block,
generate: true,
skipBlockValidation: true
})
.catch((vmerr) => ({ vmerr }));
After I figure out the relationship between geth_api_double
, statemanager
, and blockchain_double
, I decided to directly invoke the virtual machine from geth_api_double
for convenience. From geth_api_double
’s point of view, the vm is located at: this.state.blockchain.vm
where the vm is an instance of ethereumjs-vm
.
The ethereumjs-vm
provides high-level api that allows me to run transaction or blocks, however, what I needed was to directly modify the storage or code at a given address. Thus I digged into the documentation and found that under the vm
there is a statemanager
(Not to be confused with ganache’s state manager) which provides some low-level api such as putContractStorage
and putContractCode
. We can then invoke this by simply calling the target function and passing in the callback. A code snippet of how this is done might be clearer:
1
2
3
4
5
6
7
8
9
10
GethApiDouble.prototype.putContractCode = function(contractAddr, value, callback){
// putContractCode(address: Buffer, value: Buffer, cb: any): void
const vmAddr = Buffer.from(contractAddr, 'hex');
const vmValue = Buffer.from(value, 'hex');
var self = this;
self.state.blockchain.vm.stateManager.putContractCode(vmAddr, vmValue, function(err, ret){
callback(err, ret);
});
};
Notes:
- It is important to refer to the exact version of the
ethereumjs-vm
. In the beginning it was really confusing to me as the newest documentation doesn’t seem to contain the callback function at all.
Questions I currently have:
- Why queue the actions if they were immediately processed?
Reference: