Interactive Pyethereum Demo

Ethereum development continues to chug along and the python client is now interoperable with the golang and C++ clients. So you can boot up a pyethereum node, download the latest testnet blockchain, and even issue transactions with the API server, but a nice interface for playing with contracts is still lacking. Even with the C++ or golang clients, this can be frustrating, despite the pretty GUI clients. So, let’s take advantage of the python interpreter to interactively play with an ethereum blockchain in pyethereum and get a feel for the ethereum Virtual Machine and the pyethereum API. This will also inadvertently give us a handle on building native ethereum apps on top of pyethereum.

First thing you’ll want to do is install a copy of pyethereum. You can go for the official version, but I’ve got a clone with some debug flags turned on that are helpful (see processblock.py).

git clone https://github.com/ebuchman/pyethereum
cd pyethereum
sudo pip install -r requirements.txt
sudo python setup.py install
cd ..

If you like, you can do your installs in a virtualenv to keep your global python environment clean. For the sake of simplicity, we install globally here.

You’ll also want the serpent library, so we can write contracts in a high-level language

git clone https://github.com/ethereum/serpent
cd serpent
make && sudo make install
sudo python setup.py install
cd ..

Note: we will be using the example contracts provided by Vitalik in serpent/examples. Vitalik also provided an introduction to using pyethereum and serpent back in April, and much of this will be similar to that, but with some more details.

So, now that we have pyethereum and serpent installed, let’s fire up a python interpreter. I use iPython, mostly because it has tab-completion and lets me explore imported packages really easily (and gives access to function definition and associated documentation). In fact, I figured out most of this tutorial by importing different pyethereum modules in iPython and using tab-completion to explore their innards. If you don’t have iPython, a standard python interpreter is fine, just ignore any mention of tab-completion.

ipython

The focus of this tutorial will be simple contract writing and execution on the blockchain. We need to import the following packages

from pyethereum import transactions, blocks, processblock, utils
import serpent

Before we get started, let’s set up logging so we can get some useful print statements

utils.configure_logging(verbosity=3)

Now let’s get started. The first order of business is to create an address for ourselves. We start by creating an elliptic curve private key. Since a private key is any 256-bit number, the easiest thing to do is take the sha3 hash of some phrase. Let’s actually create two addresses, so we can send funds back and forth.

key = utils.sha3("this is an insecure passphrase")
key2 = utils.sha3("37Dhsg17e92dfghKa Th!s i$ mUcH better r920167dfghjiHFGJsbcm")
addr = utils.privtoaddr(key)
addr2 = utils.privtoaddr(key2)

Now we can create a genesis block, and allocate for ourselves some amount of ether

genesis = blocks.genesis({addr:10**18})

Note that while typically the blockchain grows via mining, we will do all our work here in the genesis block, and will not simulate mining or the growth of the chain. This is sufficient for illustrative purposes in the python interpreter environment, but will have subtle differences from a “real” instance of the blockchain.

Now, if you type genesis. and hit tab, you should see all the wonderful functions available to the block. Try exploring some of them. In particular, let’s take a look at our account:

genesis.account_to_dict(addr)

Accounts have four attributes: an incrementing nonce, a balance, code, and storage. Since this account is not a contract, the code and storage attributes should be 0. And since we have yet to make any transactions, the nonce should start at 0. So let’s send some ether from one account to the other. We begin by constructing and signing a transaction:

tx = transactions.Transaction(0, 1000, 1000, addr2, 56789000, "").sign(key)

The form for a transaction is Transaction(nonce, gasprice, startgas, to, value, data). So here, we are sending 56789 wei from addr to addr2, using 1000 gas and a gasprice of 1000. Since we are not calling a contract, the data is empty, and since it’s our first transaction, the nonce is 0. Now, we apply the transaction to the block:

success, _ = processblock.apply_transaction(genesis, tx)

You should see a bunch of debug output, telling you about the transaction and the state of the block before and after applying it. apply_transaction returns a boolean indicating the success of the transaction. The second return value is variable: if the transaction actually creates a contract, it’s the address of that contract; if it calls a contract, it’s the return value; otherwise, it’s an empty string (so we ignore it here).

If we now check on our accounts, we should see the result of the transaction:

genesis.account_to_dict(addr)
genesis.account_to_dict(addr2)

Neat, right? Let’s now fire up a simple contract, and see how it works. Vitalik has kindly provided a number of example contracts in the serpent/examples directory. We’ll start with namecoin.se, since it is essentially just a simple key-value store. We could compile it all in one line, but I have included a few intermediate steps so you can see the LLL code and the EVM assembly code that this contract compiles down to:

serpent_code = open('serpent/examples/namecoin.se').read()
serpent_code
serpent.compile_to_lll(serpent_code)
serpent.pretty_compile(serpent_code)
byte_code = serpent.compile(serpent_code)

Now, we create a transaction object that will create a new contract with the code we have just compiled, and we apply it to the block

tx2 = transactions.contract(genesis.get_nonce(addr), 10**12, 1000, 0, byte_code).sign(key)
success, contract_addr = processblock.apply_transaction(genesis, tx2)

The debug print should be long and messy. Note that no contract code has been executed here. However, the code has been copied into memory and returned as this is what must be run when the contract is called. This mechanism is useful for including initialization code in the contract, as we shall see later. Now, let’s check on the new address just returned

genesis.account_to_dict(contract_addr)

Since it is a contract, it has a code attribute, namely, the result of our compile. We can cause the code to execute by sending the contract a message! Since it’s a key-value store, it takes two data arguments, a key, and a value:

msg = transactions.Transaction(genesis.get_nonce(addr), 1000, 10000, contract_addr, 0, serpent.encode_datalist(['myname', 69])).sign(key)
s, r = processblock.apply_transaction(genesis, msg)

Note that the data must be properly encoded when sent to a contract, and serpent.encode_datalist() takes care of that for us. So now, finally, we have some interesting debug output, since this time we have actually executed code in the ethereum virtual machine. See if you can follow the trace. We load the first data argument, ‘myname’, which is a string, but since EVM has no concept of strings, it converts the ASCII bytes into a number (in this case 120368310349157. You can get the same result with int('myname'.encode('hex'), 16)). Next we check if there is any value at that storage location. In this case, there isn’t. So we load the next data argument (recall that data arguments come in 32-byte chunks), in this case, 69, and we store it at the location corresponding to the key (‘myname’). Now, let’s check the contract address again:

genesis.account_to_dict(contract_addr)

Wooohooo! Our value has been successfully stored in the contract’s storage! We can retrieve it programmatically with

genesis.get_storage_data(contract_addr, 'myname')

Sure enough, it returns 69. Now if this isn’t fun times, I don’t know what is! Let’s store another value:

msg = transactions.Transaction(genesis.get_nonce(addr), 1000, 10000, contract_addr, 0, serpent.encode_datalist(['is-this-fun', 'yes-it-is'])).sign(key)
s, r = processblock.apply_transaction(genesis, msg)
genesis.account_to_dict(contract_addr)
genesis.get_storage_data(contract_addr, 'is-this-fun')

Again, we see that the value was stored. Though, of course, it is returned as an integer. If you want to see the string:

utils.int_to_big_endian(genesis.get_storage_data(contract_addr, 'is-this-fun'))

Beautiful. Now, let’s try and overwrite one of the values:

msg = transactions.Transaction(genesis.get_nonce(addr), 1000, 10000, contract_addr, 0, serpent.encode_datalist(['is-this-fun', 'not-really'])).sign(key)
s, r = processblock.apply_transaction(genesis, msg)
genesis.account_to_dict(contract_addr)
genesis.get_storage_data(contract_addr, 'is-this-fun')

Notice how the value was not over written! This is explicit in the contract’s source code, and we see now that it works as intended. Good stuff.

Let’s try a slightly more advanced contract, subcurrency.se, which introduces some important tools. Take a look:

init:
    # Initial: premine 1000000 units to creator
    contract.storage[msg.sender] = 1000000
code:
    # If a message with one item is sent, that's a balance query
    if msg.datasize == 1:
        addr = msg.data[0]
        return(contract.storage[addr])
    # If a message with two items [to, value] are sent, that's a transfer request
    else:
        from = msg.sender
        fromvalue = contract.storage[from]
        to = msg.data[0]
        value = msg.data[1]
        if fromvalue >= value:
            contract.storage[from] = fromvalue - value
            contract.storage[to] = contract.storage[to] + value
            return(1)
        else:
            return(0)

The code should be relatively self explanatory – it’s a subcurrency, and users can send that subcurrency back and forth between accounts, but only if they have enough. An important feature is the init/code segmentation. When the contract is created, everything in the init block is run, and the code block is copied into memory and returned. When the contract is called, the code block will execute.

The API for this contract is simple. If one argument is passed, it’s a balance query. You might think this is kind of silly, since we can just query the blockchain directly, though this way other contracts can use this contract’s API as well. If two arguments are passed, the first is the address we are sending funds to, and the second is the amount to send. So let’s compile the code, create the contract, and apply it to the blockchain:

code = serpent.compile(open('serpent/examples/subcurrency.se').read())
tx = transactions.contract(genesis.get_nonce(addr), 1000, 10000, 0, code).sign(key)
s, caddr = processblock.apply_transaction(genesis, tx)

Looking at the debug output, we see that there is some VM execution here, corresponding to the init block, where a certain amount of the subcurrency is stored at our address. We can confirm this

genesis.get_storage_data(caddr, addr)

Ok, so we have 1000000 subcurrency units. What about our other address?

genesis.get_storage_data(caddr, addr2)

Indeed, it has none. So let’s try and send 100 units from this second address to the other, and ensure it fails:

genesis.account_to_dict(caddr)
tx = transactions.Transaction(genesis.get_nonce(addr2), 1000, 10000, caddr, 0, serpent.encode_datalist([addr, 100])).sign(key2)
s, r = processblock.apply_transaction(genesis, tx)
genesis.account_to_dict(caddr)

And we should see that nothing has changed. So now let’s send funds the other way around:

tx = transactions.Transaction(genesis.get_nonce(addr), 1000, 10000, caddr, 0, serpent.encode_datalist([addr2, 100])).sign(key)
s, r = processblock.apply_transaction(genesis, tx)
genesis.account_to_dict(caddr)

Tada! We see that now there are two addresses with funds in this subcurrency contract. Scrolling through the debug output can be painful, because of all the memory print outs. We can disable the print memory flag in processblock.py if desired, but let’s leave it. Finally, let’s try sending more funds then we have from addr2:

tx = transactions.Transaction(genesis.get_nonce(addr2), 1000, 10000, caddr, 0, serpent.encode_datalist([addr, 500])).sign(key2)
s, r = processblock.apply_transaction(genesis, tx)
genesis.account_to_dict(caddr)

Of course, nothing changed, since we didn’t have enough funds. So our contract works as expected! How about that – a brand new crypto currency in just a few lines of code!

From here, there are many things to try. You can explore the other example contracts in this way to get a feel for how they work, and you can test your own contracts as well. The most common problem I have had is not supplying enough gas in a transaction – so look out for that.

I should note, that pyethereum now includes a nice module for testing contracts, so you don’t need as much boiler-plate as I provided here. Check it out at: https://github.com/ethereum/pyethereum/blob/master/tests/test_transactions.py

In another installment, we’ll return to the state trie in pyethereum using real addresses and accounts, and flesh out more clearly and hopefully in fewer words than before.