Write your first Blockchain: Learning Solidity Programming in 15 minutes

0
3145
15 min read
This post is a book extract from the title Mastering Blockchain, authored by Imran Bashir. The book begins with the technical foundations of blockchain, teaching you the fundamentals of cryptography and how it keeps data secure.

Our article aims to quickly get you up to speed with Blockchain development using the Solidity Programming language.

Introducing solidity

Solidity is a domain-specific language of choice for programming contracts in Ethereum. There are, however, other languages, such as serpent, Mutan, and LLL but solidity is the most popular at the time of writing this. Its syntax is closer to JavaScript and C. Solidity has evolved into a mature language over the last few years and is quite easy to use, but it still has a long way to go before it can become advanced and feature-rich like other well established languages. Nevertheless, this is the most widely used language available for programming contracts currently.

It is a statically typed language, which means that variable type checking in solidity is carried out at compile time. Each variable, either state or local, must be specified with a type at compile time. This is beneficial in the sense that any validation and checking is completed at compile time and certain types of bugs, such as interpretation of data types, can be caught earlier in the development cycle instead of at run time, which could be costly, especially in the case of the blockchain/smart contracts paradigm. Other features of the language include inheritance, libraries, and the ability to define composite data types.

Solidity is also a called contract-oriented language. In solidity, contracts are equivalent to the concept of classes in other object-oriented programming languages.


Types

Solidity has two categories of data types: value types and reference types.

Value types

These are explained in detail here.

Boolean

This data type has two possible values, true or false, for example:

bool v = true;

This statement assigns the value true to v.

Integers

This data type represents integers. A table is shown here, which shows various keywords used to declare integer data types.

Solidity language

For example, in this code, note that uint is an alias for uint256:

uint256 x;

uint y;

int256 z;

These types can also be declared with the constant keyword, which means that no storage slot will be reserved by the compiler for these variables. In this case, each occurrence will be replaced with the actual value:

uint constant z=10+10;

State variables are declared outside the body of a function, and they remain available throughout the contract depending on the accessibility assigned to them and as long as the contract persists.

Address

This data type holds a 160-bit long (20 byte) value. This type has several members that can be used to interact with and query the contracts. These members are described here:

Balance

The balance member returns the balance of the address in Wei.

Send

This member is used to send an amount of ether to an address (Ethereum’s 160-bit address) and returns true or false depending on the result of the transaction, for example, the following:

address to = 0x6414cc08d148dce9ebf5a2d0b7c220ed2d3203da;

address from = this;

if (to.balance < 10 && from.balance > 50) to.send(20);

Call functions

The call, callcode, and delegatecall are provided in order to interact with functions that do not have Application Binary Interface (ABI). These functions should be used with caution as they are not safe to use due to the impact on type safety and security of the contracts.

Array value types (fixed size and dynamically sized byte arrays)

Solidity has fixed size and dynamically sized byte arrays. Fixed size keywords range from bytes1 to bytes32, whereas dynamically sized keywords include bytes and strings. bytes are used for raw byte data and string is used for strings encoded in UTF-8. As these arrays are returned by the value, calling them will incur gas cost. length is a member of array value types and returns the length of the byte array.

An example of a static (fixed size) array is as follows:

bytes32[10] bankAccounts;

An example of a dynamically sized array is as follows:

bytes32[] trades;

Get length of trades:

trades.length;

Literals

These are used to represent a fixed value.

Integer literals

Integer literals are a sequence of decimal numbers in the range of 0-9. An example is shown as follows:

uint8 x = 2;

String literals

String literals specify a set of characters written with double or single quotes. An example is shown as follows:

'packt'

"packt”

Hexadecimal literals

Hexadecimal literals are prefixed with the keyword hex and specified within double or single quotation marks. An example is shown as follows:

(hex'AABBCC');

Enums

This allows the creation of user-defined types. An example is shown as follows: enum

Order{Filled, Placed, Expired };

Order private ord;

ord=Order.Filled;

Explicit conversion to and from all integer types is allowed with enums.

Function types

There are two function types: internal and external functions.

Internal functions

These can be used only within the context of the current contract.

External functions

External functions can be called via external function calls.

A function in solidity can be marked as a constant. Constant functions cannot change anything in the contract; they only return values when they are invoked and do not cost any gas. This is the practical implementation of the concept of call as discussed in the previous chapter.

The syntax to declare a function is shown as follows:

function <nameofthefunction> (<parameter types> <name of the variable>)

{internal|external} [constant] [payable] [returns (<return types> <name of

the variable>)]

Reference types

As the name suggests, these types are passed by reference and are discussed in the following section.

Arrays

Arrays represent a contiguous set of elements of the same size and type laid out at a memory location. The concept is the same as any other programming language. Arrays have two members named length and push:

uint[] OrderIds;

Structs

These constructs can be used to group a set of dissimilar data types under a logical group. These can be used to define new types, as shown in the following example:

Struct Trade

{

uint tradeid;

uint quantity;

uint price;

string trader;

}

Data location

Data location specifies where a particular complex data type will be stored. Depending on the default or annotation specified, the location can be storage or memory. This is applicable to arrays and structs and can be specified using the storage or memory keywords. As copying between memory and storage can be quite expensive, specifying a location can be helpful to control the gas expenditure at times. Calldata is another memory location that is used to store function arguments. Parameters of external functions use calldata memory. By default, parameters of functions are stored in memory, whereas all other local variables make use of storage. State variables, on the other hand, are required to use storage.

Mappings

Mappings are used for a key to value mapping. This is a way to associate a value with a key. All values in this map are already initialized with all zeroes, for example, the following:

mapping (address => uint) offers;

This example shows that offers is declared as a mapping. Another example makes this clearer:

mapping (string => uint) bids;

bids["packt"] = 10;

This is basically a dictionary or a hash table where string values are mapped to integer values. The mapping named bids has a packt string value mapped to value 10.

Global variables

Solidity provides a number of global variables that are always available in the global namespace. These variables provide information about blocks and transactions. Additionally, cryptographic functions and address-related variables are available as well.

A subset of available functions and variables is shown as follows:

keccak256(...) returns (bytes32)

This function is used to compute the keccak256 hash of the argument provided to the Function:

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)

returns (address)

This function returns the associated address of the public key from the elliptic curve signature:

block.number

This returns the current block number.

Control structures

Control structures available in solidity are if – else, do, while, for, break, continue, return. They work in a manner similar to how they work in C-language or JavaScript.

Events

Events in solidity can be used to log certain events in EVM logs. These are quite useful when external interfaces are required to be notified of any change or event in the contract. These logs are stored on the blockchain in transaction logs. Logs cannot be accessed from the contracts but are used as a mechanism to notify change of state or the occurrence of an event (meeting a condition) in the contract.

In a simple example here, the valueEvent event will return true if the x parameter passed to function Matcher is equal to or greater than 10:

contract valueChecker {

uint8 price=10;

event valueEvent(bool returnValue);

function Matcher(uint8 x) returns (bool)

{

if (x>=price)

{

valueEvent(true);

return true;

}

}

}

Inheritance

Inheritance is supported in solidity. The is keyword is used to derive a contract from another contract. In the following example, valueChecker2 is derived from the valueChecker contract. The derived contract has access to all nonprivate members of the parent contract:

contract valueChecker

{

uint8 price=10;

event valueEvent(bool returnValue);

function Matcher(uint8 x) returns (bool)

{

 if (x>=price)

 {

  valueEvent(true);

  return true;

  }

 }

}

contract valueChecker2 is valueChecker

{

function Matcher2() returns (uint)

{

return price + 10;

}

    }

In the preceding example, if uint8 price = 10 is changed to uint8 private price = 10, then it will not be accessible by the valuechecker2 contract. This is because now the member is declared as private, it is not allowed to be accessed by any other contract.

Libraries

Libraries are deployed only once at a specific address and their code is called via CALLCODE/DELEGATECALL Opcode of the EVM. The key idea behind libraries is code reusability. They are similar to contracts and act as base contracts to the calling contracts. A library can be declared as shown in the following example:

library Addition

{

function Add(uint x,uint y) returns (uint z)

 {

   return x + y;

 }

}

This library can then be called in the contract, as shown here. First, it needs to be imported and it can be used anywhere in the code. A simple example is shown as follows:

Import "Addition.sol"

function Addtwovalues() returns(uint)

{

return Addition.Add(100,100);

}

There are a few limitations with libraries; for example, they cannot have state variables and cannot inherit or be inherited. Moreover, they cannot receive Ether either; this is in contrast to contracts that can receive Ether.

Functions

Functions in solidity are modules of code that are associated with a contract. Functions are declared with a name, optional parameters, access modifier, optional constant keyword, and optional return type. This is shown in the following example:

function orderMatcher(uint x) private constant returns(bool returnvalue)

In the preceding example, function is the keyword used to declare the function. orderMatcher is the function name, uint x is an optional parameter, private is the access modifier/specifier that controls access to the function from external contracts, constant is an optional keyword used to specify that this function does not change anything in the contract but is used only to retrieve values from the contract instead, and returns (bool returnvalue) is the optional return type of the function.

  • How to define a function: The syntax of defining a function is shown as follows:
function <name of the function>(<parameters>) <visibility

specifier> returns (<return data type> <name of the variable>)

{

 <function body>

}
  • Function signature: Functions in solidity are identified by its signature, which is the first four bytes of the keccak-256 hash of its full signature string. This is also visible in browser solidity, as shown in the following screenshot. D99c89cb is the first four bytes of 32 byte keccak-256 hash of the function named Matcher.

Solidity language for blockchain

In this example function, Matcher has the signature hash of d99c89cb. This information is useful in order to build interfaces.

  • Input parameters of a function: Input parameters of a function are declared in the form of <data type> <parameter name>. This example clarifies the concept where uint x and uint y are input parameters of the checkValues function:
contract myContract

{

function checkValues(uint x, uint y)

{

}

}
  • Output parameters of a function: Output parameters of a function are declared in the form of <data type> <parameter name>. This example shows a simple function returning a uint value:
contract myContract

{

Function getValue() returns (uint z)

{

 z=x+y;

}

}

A function can return multiple values. In the preceding example function, getValue only returns one value, but a function can return up to 14 values of different data types. The names of the unused return parameters can be omitted optionally.

  • Internal function calls: Functions within the context of the current contract can be called internally in a direct manner. These calls are made to call the functions that exist within the same contract. These calls result in simple JUMP calls at the EVM byte code level.
  • External function calls: External function calls are made via message calls from a contract to another contract. In this case, all function parameters are copied to the memory. If a call to an internal function is made using the this keyword, it is also considered an external call. The this variable is a pointer that refers to the current contract. It is explicitly convertible to an address and all members for a contract are inherited from the address.
  • Fall back functions: This is an unnamed function in a contract with no arguments and return data. This function executes every time ether is received. It is required to be implemented within a contract if the contract is intended to receive ether; otherwise, an exception will be thrown and ether will be returned. This function also executes if no other function signatures match in the contract. If the contract is expected to receive ether, then the fall back function should be declared with the payable modifier. The payable is required; otherwise, this function will not be able to receive any ether. This function can be called using the address.call() method as, for example, in the following:
function ()

{

throw;

}

In this case, if the fallback function is called according to the conditions described earlier; it will call throw, which will roll back the state to what it was before making the call. It can also be some other construct than throw; for example, it can log an event that can be used as an alert to feed back the outcome of the call to the calling application.

  • Modifier functions: These functions are used to change the behavior of a function and can be called before other functions. Usually, they are used to check some conditions or verification before executing the function. _(underscore) is used in the modifier functions that will be replaced with the actual body of the function when the modifier is called. Basically, it symbolizes the function that needs to be guarded. This concept is similar to guard functions in other languages.
  • Constructor function: This is an optional function that has the same name as the contract and is executed once a contract is created. Constructor functions cannot be called later on by users, and there is only one constructor allowed in a contract. This implies that no overloading functionality is available.
  • Function visibility specifiers (access modifiers): Functions can be defined with four access specifiers as follows:
  • External: These functions are accessible from other contracts and transactions. They cannot be called internally unless the this keyword is used.
  • Public: By default, functions are public. They can be called either internally or using messages.
  • Internal: Internal functions are visible to other derived contracts from the parent contract.
  • Private: Private functions are only visible to the same contract they are declared in.
  • Other important keywords/functions throw: throw is used to stop execution. As a result, all state changes are reverted. In this case, no gas is returned to the transaction originator because all the remaining gas is consumed.

Layout of a solidity source code file

Version pragma

In order to address compatibility issues that may arise from future versions of the solidity compiler version, pragma can be used to specify the version of the compatible compiler as, for example, in the following:

pragma solidity ^0.5.0

This will ensure that the source file does not compile with versions smaller than 0.5.0 and versions starting from 0.6.0.

Import

Import in solidity allows the importing of symbols from the existing solidity files into the current global scope. This is similar to import statements available in JavaScript, as for example, in the following:

Import "module-name";

Comments

Comments can be added in the solidity source code file in a manner similar to C-language. Multiple line comments are enclosed in /* and */, whereas single line comments start with //.

An example solidity program is as follows, showing the use of pragma, import, and comments:

Solidity language for blockchain

To summarize, we went through a brief introduction to the solidity language. Detailed documentation and coding guidelines are available online.

If you found this article useful, and would like to learn more about building blockchains, go ahead and grab the book Mastering Blockchain, authored by Imran Bashir.

Mastering Blockchain

 

LEAVE A REPLY

Please enter your comment!
Please enter your name here