Contracts as databases

Your contracts storage

Every contract on the blockchain has it’s own storage which only it can write to – and which it can control access to.

A contract’s storage is at its most basic a key-value store with 2^256 possible keys and 2^256 values. This makes for sufficient possible storage to create database structures of any type imaginable.

Solidity has several tools to for you to structure the storage of a contract as a relational database model. In the previous contract you used a mapping to store value associated with an address but this only one of many options.

In this tutorial we will cover the basic use of Structs, State variables and mappings which abstract the creation of complex rules for storing information.

Structs

Structs may be familiar to anyone who has used C type language before – essentially they are physical grouping of variables, stored under one reference:

struct coinwallet {
uint redCoin;
uint greenCoin;
}

This code defines a ‘type’ of struct but does not initialise an instance of it – this means that defining the struct doesn’t write anything to the contracts storage, in order to do this we have to declare an instance of the struct and then assign a value to its parameters:

coinWallet myWallet;
myWallet.redCoin = 500
myWallet.greenCoin = 250

this writes to storage addresses 0 and 1 respectively – try this contract in alethzero:

contract Test{
struct coinWallet {
uint redCoin;
uint greenCoin;
}
coinWallet myWallet;
function Test(){
myWallet.redCoin = 500;
myWallet.greenCoin = 250;
}
}

Note in the above how the underlying parameters of the myWallet variable are accessed by a dot operator on the myWallet instance of the coinWallet struct.

When used as a variable of finite size (ie: not in a mapping as used in our previous metaCoin contract) structs place things continuously in storage starting at key value ‘0x0’.

Mappings

Structs become far more interesting when used as datatypes for values inside of mappings.

Here is our very first metaCoin contract amended so that each user has two balances:

contract metaCoin {
struct coinWallet {
uint redCoin;
uint greenCoin;
}
mapping (address => coinWallet) balances;
function metaCoin() {
balances[msg.sender].redCoin = 10000;
balances[msg.sender].greenCoin = 5000;
}
function sendRed(address receiver, uint amount) {
if (balances[msg.sender].redCoin < amount) return;
balances[msg.sender].redCoin -= amount;
balances[receiver].redCoin += amount;
}
function sendGreen(address receiver, uint amount) {
if (balances[msg.sender].greenCoin < amount) return;
balances[msg.sender].greenCoin -= amount;
balances[receiver].greenCoin += amount;
}
}

Mappings calculate references for data storage by hashing the keys passed to the mapping. More details on how this works can be found here.

We can also include mappings as datatypes within another mapping:

contract rainbowCoin {
mapping (address => mapping (uint => uint)) balances;
function rainbowCoin() {
balances[msg.sender][0] = 10000; ///red coin
balances[msg.sender][1] = 10000; ///orange coin
balances[msg.sender][2] = 10000; ///yellow coin
balances[msg.sender][3] = 10000; ///green coin
balances[msg.sender][4] = 10000; ///blue coin
balances[msg.sender][5] = 10000; ///indigo coin
balances[msg.sender][6] = 10000; ///violet coin
}
function send(address receiver, uint amount, uint coin) {
if (balances[msg.sender][coin] < amount) return;
balances[msg.sender][coin] -= amount;
balances[receiver][coin] += amount;
}
}

Note how this allows us to access elements in the submapping inside balances using square brackets.

It is also possible to place mappings inside of structs, and place structs inside mappings – in fact the only datatype that you cannot place inside a struct or a mapping is itself.

Try the rainbowCoin contract above by pasting it into the alethzero transact pane. You will find that it behaves just like our coin contract from the first tutorial, the only difference is when sending a transaction you must include a third argument ‘coin’ which should be an integer between 0 and 6.

A typical data array for a transaction should look like this:

$0x67df93f2
0x5487699870...
500
4

Accessor Functions

So far you have viewed the contents of any contract you have added to the network using Alethzero’s GUI, however we have not defined any way to ‘get’ the value associated with a key directly. This is necessary requirement for many contracts because although we as external actors can easily view the whole of the contracts storage – other contracts are not able to look inside another contracts storage without executing a message call to a function specifically defined to return the relevant information.

Fortunately for us, Solidity will automatically create an accessor function to any of the state variables we choose to make public using the public keyword. By amending our Rainbow coin contract we can have solidity create an accessor function automatically:

contract rainbowCoin {
mapping (address => mapping (uint => uint)) public balances;
function rainbowCoin() {
balances[msg.sender][0] = 10000; ///red coin
balances[msg.sender][1] = 10000; ///orange coin
balances[msg.sender][2] = 10000; ///green coin
balances[msg.sender][0] = 10000; ///red coin
balances[msg.sender][1] = 10000; ///orange coin
balances[msg.sender][2] = 10000; ///green coin
}
function send(address receiver, uint amount, uint coin) {
if (balances[msg.sender][coin] < amount) return;
balances[msg.sender][coin] -= amount;
balances[receiver][coin] += amount;
}
}

If you copy the above into Alethzero you will see an additional ABI code has been generated by the compiler called ‘balances’. This function could be executed by a transaction, but in reality it never would be. Instead accessor functions are accessed via message calls from other contracts and calls made from our web front-end. Both of these will be demonstrated in a later tutorial.

 

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s