Post

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:

This post is licensed under CC BY 4.0 by the author.