The Resurrection of LLL: Part VII

In part 6 of this series we discussed what is necessary for a contract to integrate with our dispatcher. In the process I presented a simple example contract that satisfies those requirements. In this article I will explore the actual compilation, deployment, initialization and invocation of the dispatcher and its associated contract, including replacing that contract after deployment. I'll demonstrate this using RPC and web3. I intend for this to take only one article, but I'll most likely have to split it into RPC here, then web3 in a separate article. We shall see!

Compilation

Writing source code is all well and good, but if you don't compile your source there's not much point to the exercise! In the case of Ethereum and its virtual machine, you compile a contract to bytecode which will then be executed by the EVM on deployment and invocation.

Way back in part 2 I went over how to install the LLL compiler, lllc, for Ubuntu-based Linux distributions. Covering all possible ways to install lllc is beyond the scope of these articles. Since starting this series, Solidity has been separated from the cpp-ethereum repository. For now, all LLL libraries and the compiler itself are contained in the Solidity repository. Instructions on installing Solidity are here. If you're not on a supported platform you'll probably have to build from source.

To confirm that you have a functioning lllc, open a terminal and type

lllc -h  

You should see output similar to this:

Usage lllc [OPTIONS] <file>  
Options:  
    -b,--binary  Parse, compile and assemble; output byte code in binary.
    -x,--hex  Parse, compile and assemble; output byte code in hex.
    -a,--assembly  Only parse and compile; show assembly.
    -t,--parse-tree  Only parse; show parse tree.
    -h,--help  Show this help message and exit.
    -V,--version  Show the version and exit.

Once you've confirmed that you have a functioning compiler, you'll need to download the source of our dispatcher. Unzip lll-resurrected-master.zip then in your terminal change into the src directory of the project.

I'm going to assume a bash command line for the moment. Using lllc on other platforms should be pretty well the same as what I describe here, but not having access to other platforms makes it impractical for me to accurately document its use on them. If you have success on another platform, please comment below with instructions and I can potentially update this article.

Compiling an LLL source file is quite straightforward. If you're in the dispatcher project's src directory, simply type

lllc dispatcher.lll  

You should see a long string of hexadecimal numbers similar to this:

33600055600060015560cd8060146000396000f360e060020a6000350463d8c2e7aa1415601e576000543314602c576002565b600060015414156076576002565b6001546000146039576002565b600060043514156047576002565b60043560015560016001545560e060020a638129fc1c0260a05260006102a0602060a06001546103e85a03f450005b60e060020a6000350460205236600060a03760205160015401546102a03660a06001546103e85a03f460805260016080511460af576002565b60006020516001540154141560c057005b60205160015401546102a0f3  

Congratulations! You've just compiled your first LLL-based smart contract! You can do the same for arithmetic.lll:

lllc arithmetic.lll  

This will result in an even longer string of hexadecimal numbers.

It may be difficult to see the point in reducing your beautiful source code to this unintelligible output. But this output is what you'll be feeding to Ethereum's blockchain. For interest's sake you can cause the compiler to output assembly source instead of hex codes:

lllc -a dispatcher.lll  

This will produce a lot of output, starting with this:

.code:
  CALLER
  PUSH1 0x0
  SSTORE
  PUSH1 0x0
  PUSH1 0x1
  SSTORE
  PUSH #[$00000000…00000000]
  DUP1
  PUSH [$00000000…00000000]
  PUSH1 0x0
  CODECOPY
  PUSH1 0x0
  RETURN

This assembly source represents a more readable version of our hex output. If you recall from the first article, I said that the Ethereum developers really didn't want to write contracts in assembly language. The code above illustrates why.

One point of interest: If you look closely at the assembly source you can see a few LLL keywords such as caller, sstore and return. There are many more parallels, which shows how "close to the metal" we're working when writing LLL code.

In the end it doesn't really matter. We'll be using the hex output in an RPC call to deploy our contracts on the blockchain. We don't need to know what the compiler has produced as long as it functions as designed.

Deployment

Here's where it gets interesting. We're going to be using Ethereum's JSON RPC API. For those new to this, RPC stands for Remote Procedure Call. Using the RPC API allows one to make calls to a contract on the blockchain (or indeed deploy a contract to the blockchain) and retrieve the results. In our case we'll start by deploying our dispatcher.lll contract.

testrpc

In order to deploy anything to the blockchain using the RPC API you need access to an Ethereum node. For development the best system I've found is ethereumjs/testrpc. From the GitHub page, testrpc is

a Node.js based Ethereum client for testing and development. It uses ethereumjs to simulate full client behavior and make developing Ethereum applications much faster. It also includes all popular RPC functions and features (like events) and can be run deterministically to make development a breeze.

Well, smart contract development is not a breeze, but it can be made much more efficient by the use of testrpc. Running a full Ethereum client (geth, eth, pyethapp, parity, etc.) can be a drain on a developer's resources, especially if running on an under-powered machine. Also, unless you set up a private network and tweak the settings, you have to wait a while for blocks to be mined before checking the results of your transactions. If you're connected to the main Homestead network you'll also be paying for your development testing with real digital currency. No, it's better to start simply and install ethereumjs/testrpc. Go ahead and follow the instructions in the README file to install it on your local machine. I'll wait.

Once installed, start testrpc in a terminal with the simple command

testrpc  

You should see a bunch of output, including the account addresses and keys generated by testrpc. Make note of any one of the ten account addresses as we'll be using it for the from address in RPC calls. The addresses should look similar to this:

0xf4338d23dc14295eb44f70038ffc8b1c42d21d2a  

Back to deployment

Now that we have access to an Ethereum node we can start interacting with it. Since we'll be using the JSON-RPC API, our calls will be JSON-formatted. The first thing we'll do is deploy the dispatcher contract. You'll need the binary string we generated earlier when we compiled dispatcher.lll. Here's the JSON necessary to deploy the dispatcher contract to our testrpc Ethereum node:

{
    "jsonrpc": "2.0", 
    "method": "eth_sendTransaction",
    "params": [{ 
        "gas": "0xb8a90", 
        "from": "0xf4338d23dc14295eb44f70038ffc8b1c42d21d2a",
        "data": "0x33600055600060015560cd8060146000396000f360e060020a6000350463d8c2e7aa1415601e576000543314602c576002565b600060015414156076576002565b6001546000146039576002565b600060043514156047576002565b60043560015560016001545560e060020a638129fc1c0260a05260006102a0602060a06001546103e85a03f450005b60e060020a6000350460205236600060a03760205160015401546102a03660a06001546103e85a03f460805260016080511460af576002565b60006020516001540154141560c057005b60205160015401546102a0f3"
    }], 
    "id": 1 
}

Of course, you'll have to substitute your own account address in the from field or it won't work. For convenience, I've already included the dispatcher's bytecode in the data field.

The next question is, how do we send this JSON to testrpc? My preferred method is through curl. However, in order to avoid everyone having to install even more software, I would suggest using an online tool of some kind, such as the Guru JSON-RPC Tester. Set Service URL to http://localhost:8545 then paste the JSON above into the Request JSON String field, remembering to change the from address.

Make sure you can see the terminal in which you're running testrpc, then click the Call Method button. You should see some activity in the testrpc terminal and the Raw Response JSON String field in your browser should have output that looks similar to this:

{"id":1,"jsonrpc":"2.0","result":"0x0b77dbc163ba6679468f1e29d4539eba496d7aacbe4d91d231e6963c7e04571c"}

The result field contains the transaction ID resulting from your RPC call. In order to see the details of your transaction you can request a transaction receipt. Paste the following JSON into the Request JSON String field—substituting your own transaction ID in params, of course—then click the Call Method button:

{
    "jsonrpc": "2.0", 
    "method": "eth_getTransactionReceipt",
    "params": ["0x0b77dbc163ba6679468f1e29d4539eba496d7aacbe4d91d231e6963c7e04571c"], 
    "id": 1 
}

You'll again see the resulting JSON response in the Raw Response JSON String field. Helpfully, the response is broken out into its constituent parts. Under Response objects you'll see result.contractAddress. This is the dispatcher contract's address as deployed on the testrpc blockchain. Since the dispatcher will be representing your contract on the blockchain, it's the dispatcher's address that people will use to call functions on your contract. The dispatcher will then relay those requests to your contract. Make note of that address.

Now that we've deployed the dispatcher contract, we need a contract to which the dispatcher can relay function calls. Conveniently, we've already developed an example contract: arithmetic.lll. Compile it with the same command as we used previously:

lllc arithmetic.lll  

Deploy this similarly to the dispatcher. Paste the JSON below into the Guru JSON-RPC Tester with your own from address and hit Call Method:

{
    "jsonrpc": "2.0", 
    "method": "eth_sendTransaction",
    "params": [{ 
        "gas": "0xb8a90", 
        "from": "0xf4338d23dc14295eb44f70038ffc8b1c42d21d2a",
        "data": "6101bd8061000d6000396000f3600060015454141561000f576002565b638129fc1c60e060020a600035041415610030576000543314610051576002565b63cabfb93460e060020a6000350414156100945760005433146100cb576002565b602063cabfb9346001540155602063eee97206600154015560206320fb79e7600154015560016101a0526011806101ac60003960002060206101a0a160206101a0f35b63eee9720660e060020a600035041415610139576002600435026101a05260108061017460003960002060206101a0a160206101a0f35b600060043514156100da576002565b60006001545560015460405260043560605260605160015560016001545560e060020a638129fc1c0260a05260206102a0602060a06001546103e85a03f45060405160198061018460003960002060206060a260015460005260206000f35b60e060020a600035046320fb79e71415610170576002600435046101a052600f8061019d60003960002060206101a0a160206101a0f35b600256446f75626c65642875696e74323536295265706c6163656428616464726573732c616464726573732948616c7665642875696e7432353629496e697469616c697a656428626f6f6c29"
    }], 
    "id": 1 
}

Again, grab the result and substitute it in the params field to do another eth_getTransactionReceipt. Make a note of the result.contractAddress field. We'll be feeding this address to the dispatcher momentarily.

As an alternative to doing an eth_getTransactionReceipt call, you can simply check the output in the testrpc terminal. You'll see both the transaction ID and the ID of the contract just created. However, the eth_getTransactionReceipt call will come in handy later when we want to check event log output.

Initialization

Now that both contracts have been deployed, we need to tell the dispatcher where to find its associated contract. In order to do this we will call the dispatcher's pseudo-constructor. For that to happen we need to know the constructor's function ID. As described previously, a function's ID is derived as the first four bytes of the Keccak hash of the ASCII form of the signature. The signature for the constructor is Dispatcher(address). The sha3 hash of this is:

0xd8c2e7aacfa82a3fc5206b15fb7d8b4218fceb194669d66f2863b43f70c5bdcd  

To get the function ID we just use the first four bytes: 0xd8c2e7aa. To derive function IDs for your own contracts, you can use an online tool such as Keccak-256 Online.

Now that we have the dispatcher's constructor function ID and the address of our arithmetic contract, we can initialize the dispatcher. Again, we'll use eth_sendTransaction but this time we'll supply a to address:

{
    "jsonrpc": "2.0",
    "method": "eth_sendTransaction",
    "params": [{
        "gas": "0xb8a90",
        "from": "0xf4338d23dc14295eb44f70038ffc8b1c42d21d2a",
        "to": "0x0d496fc1ff4aa765a816dd88ad53fe12188de453",
        "data": "0xd8c2e7aa000000000000000000000000db6e784e52e7224deec2214230fdbc908d26b0d7"
    }], 
    "id": 1
}

The to address should be the address of your dispatcher contract. The data field requires a bit of explanation. It goes back to the ABI Examples I referred to previously. The four bytes of the function ID are followed by 32-byte chunks representing other input data. The dispatcher's constructor takes one parameter: the address of the associated contract. So in our case we can see the 0xd8c2e7aa at the beginning of the data string. This is followed by 32 bytes which represent the address of our arithmetic contract. Of course, you need to supply the address of the contract on your testrpc blockchain.

Paste that JSON into the Guru JSON-RPC Tester and hit Call Method. A bit anticlimactically, you'll see the same output you've seen previously: a simple transaction ID in the result field. Do another call to eth_getTransactionReceipt and you should see the details of your transaction. It will look a lot like this (formatted for clarity):

{
    "id":1,
    "jsonrpc":"2.0",
    "result":{
        "transactionHash":"0x82f4d84b94df4277cb58e1f544be899154e6985a19dbb81d66430b2f296643ac",
        "transactionIndex":"0x00",
        "blockHash":"0xad62f430c79328644b77c96d61a34cebe305e19f42fe9c00b9814f7e1afd63d5",
        "blockNumber":"0x03",
        "cumulativeGasUsed":"0x01e730",
        "gasUsed":"0x01e730",
        "contractAddress":null,
        "logs":[{
            "logIndex":"0x00",
            "transactionIndex":"0x0",
            "transactionHash":"0x82f4d84b94df4277cb58e1f544be899154e6985a19dbb81d66430b2f296643ac",
            "blockHash":"0xad62f430c79328644b77c96d61a34cebe305e19f42fe9c00b9814f7e1afd63d5",
            "blockNumber":"0x03",
            "address":"0x0d496fc1ff4aa765a816dd88ad53fe12188de453",
            "data":"0x0000000000000000000000000000000000000000000000000000000000000001",
            "topics":[
                "0xdebf9dd297d3980055770d6c047a67eed8f6b1e91397831703b5f2a73bec1320"
            ],
            "type":"mined"
        }]
    }
}

This looks similar to the other transaction receipts except for two things. There is no contract address returned (as we're not deploying a contract with this call), and the logs section has been filled out. The eth_sendTransaction call was to the dispatcher contract's address, but the dispatcher emits no events. The log output came from the initialize function in our arithmetic contract. If you refer back to the source code you can see that the function does its initialization then emits an Initialized(bool) event via log1. All this event does is output true, or 1, indicating that the contract was successfully initialized. In the log output you can see that the data section contains

0x0000000000000000000000000000000000000000000000000000000000000001  

or simply, 1.

With one call we executed the dispatcher's constructor, which recorded its associated contract's address then in turn called that contract's initialization function which emitted an event indicating its success. From this point on, any calls to the arithmetic contract go through the dispatcher, giving us the opportunity to replace that contract at any time.

Invocation

Now that everything is set up we can actually invoke functions on our arithmetic contract. First, let's try something simple: we'll call the double function to, rather obviously, double a number passed in. This time we'll use eth_call, which calls the function without requiring a transaction or any gas expenditure. This is possible because double doesn't make any changes to the state of the blockchain; it's simply a request for data. Here's the JSON to accomplish the call:

{  
    "jsonrpc": "2.0", 
    "method": "eth_call",
    "params": [
        { 
            "from": "0xf4338d23dc14295eb44f70038ffc8b1c42d21d2a",
            "to": "0x0d496fc1ff4aa765a816dd88ad53fe12188de453",
            "data": "0xeee972060000000000000000000000000000000000000000000000000000000000000080"
        }, 
        "latest"
    ], 
    "id": 1
}

Again you can see that the data section comprises the function's ID, double(uint256), represented by 0xeee97206, followed by a 32-byte number representing 0x80. This is the number we want to double. The latest entry refers to the latest mined block. Using latest is fine for our purposes. Please see The default block parameter section of the RPC documentation for an explanation of other values.

Pasting the preceding JSON into the RPC tester, we get as a result:

{"id":1,"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000100"}

You can see that the result field contains 0x100, which is double 0x80. More congratulations are in order! You just successfully called your contract on the blockchain through our dispatcher.

To make using our dispatcher at all worthwhile, we'll now replace our arithmetic contract with another one. In our case it'll be an exact duplicate for simplicity, but there's nothing stopping you from re-implementing the contract completely as long as you maintain its ABI.

Again for simplicity, I'm just going to re-run the RPC call we made to deploy the arithmetic contract in the first place. This will deploy a new contract on the blockchain. If you had made any changes to the arithmetic source, you would supply the new hex string in the data field:

{
    "jsonrpc": "2.0", 
    "method": "eth_sendTransaction",
    "params": [{ 
        "gas": "0xb8a90", 
        "from": "0xf4338d23dc14295eb44f70038ffc8b1c42d21d2a",
        "data": "6101bd8061000d6000396000f3600060015454141561000f576002565b638129fc1c60e060020a600035041415610030576000543314610051576002565b63cabfb93460e060020a6000350414156100945760005433146100cb576002565b602063cabfb9346001540155602063eee97206600154015560206320fb79e7600154015560016101a0526011806101ac60003960002060206101a0a160206101a0f35b63eee9720660e060020a600035041415610139576002600435026101a05260108061017460003960002060206101a0a160206101a0f35b600060043514156100da576002565b60006001545560015460405260043560605260605160015560016001545560e060020a638129fc1c0260a05260206102a0602060a06001546103e85a03f45060405160198061018460003960002060206060a260015460005260206000f35b60e060020a600035046320fb79e71415610170576002600435046101a052600f8061019d60003960002060206101a0a160206101a0f35b600256446f75626c65642875696e74323536295265706c6163656428616464726573732c616464726573732948616c7665642875696e7432353629496e697469616c697a656428626f6f6c29"
    }], 
    "id": 1 
}

Checking the transaction receipt or looking at the testrpc log output, note the new contract's address. Now we have to construct a call to the current arithmetic contract (through the dispatcher, of course) to instruct it to replace itself:

{  
    "jsonrpc": "2.0", 
    "method": "eth_sendTransaction",
    "params": [{ 
        "gas": "0xb8a90", 
        "from": "0xf4338d23dc14295eb44f70038ffc8b1c42d21d2a",
        "to": "0x0d496fc1ff4aa765a816dd88ad53fe12188de453",
        "data": "0xcabfb934000000000000000000000000fca471ea6747e947db2b42a71ceea77495bfa105"
    }], 
    "id": 1 
}

This looks very similar to the dispatcher's constructor call. In fact it's almost identical, with the exception that the function ID is 0xcabfb934, representing replace(address), not 0xd8c2e7aa as before. And, of course, we have a new contract address. For the last time, paste that JSON into the Guru JSON-RPC Tester and hit Call Method. Once you have the transaction ID, retrieve the transaction receipt as before. You'll see something similar to the following JSON (formatted for clarity):

{
    "id":1,
    "jsonrpc":"2.0",
    "result":{
        "transactionHash":"0x448d4b4fb468c215b4bfa2cf22200fcd30976c3dc42441b9a0211cf725b3b5cd",
        "transactionIndex":"0x00",
        "blockHash":"0x7bc3d31f307d5430557f483db8cb38aa78c35d1e5b2391613aad432bfd040c4d",
        "blockNumber":"0x05",
        "cumulativeGasUsed":"0x018f90",
        "gasUsed":"0x018f90",
        "contractAddress":null,
        "logs":[{
            "logIndex":"0x00",
            "transactionIndex":"0x0",
            "transactionHash":"0x448d4b4fb468c215b4bfa2cf22200fcd30976c3dc42441b9a0211cf725b3b5cd",
            "blockHash":"0x7bc3d31f307d5430557f483db8cb38aa78c35d1e5b2391613aad432bfd040c4d",
            "blockNumber":"0x05",
            "address":"0x0d496fc1ff4aa765a816dd88ad53fe12188de453",
            "data":"0x0000000000000000000000000000000000000000000000000000000000000001",
            "topics":[
                "0xdebf9dd297d3980055770d6c047a67eed8f6b1e91397831703b5f2a73bec1320"
            ],
            "type":"mined"
        },{
            "logIndex":"0x01",
            "transactionIndex":"0x0",
            "transactionHash":"0x448d4b4fb468c215b4bfa2cf22200fcd30976c3dc42441b9a0211cf725b3b5cd",
            "blockHash":"0x7bc3d31f307d5430557f483db8cb38aa78c35d1e5b2391613aad432bfd040c4d",
            "blockNumber":"0x05",
            "address":"0x0d496fc1ff4aa765a816dd88ad53fe12188de453",
            "data":"0x000000000000000000000000fca471ea6747e947db2b42a71ceea77495bfa105",
            "topics":[
                "0x25f1f5bf76d36a31ec1b14caf64b580331299fd2037e3589426317b1cdfd4ecb",
                "0x000000000000000000000000db6e784e52e7224deec2214230fdbc908d26b0d7"
            ],
            "type":"mined"
        }]
    }
}

This is the most verbose transaction receipt yet, mainly due to the multiple log entries. Let's look at the entry with a log index of 0x01 first. This is produced by the reigning arithmetic contract's replace(address) function. Its data field contains the address of the replacement contract so you can confirm that the function… er, functioned properly. The first topic is the Keccak hash of the event signature, Replaced(address,address), as per the Ethereum Contract ABI documentation. The second topic is the address of the contract being replaced. Calling code can record this address in case there's a need to revert to the old arithmetic contract for whatever reason.

The entry with a logIndex of 0x00 is produced by the new contract's initialize function. The replace(address) function must allow the replacement function to initialize. This is the event that results from its initialization.

Conclusion

Well that escalated quickly! This article is almost twice as long as any other in the series. I kept it as one article because I felt it was important to complete the entire compile/deploy/initialize/invoke cycle without a break. It does seem like a lot of work, but once you've done it a few times it won't feel so onerous. In the next article I'll cover the same sequence but instead use web3 calls. I'm guessing that will complete our series, but who knows?