Solidity学习(1)

1.概要

Solidity是契约导向的实现智能合约的高级语言。 它受到C ++,Python和JavaScript的影响,旨在针对以太坊虚拟机(EVM)。
Solidity是静态类型的,支持继承,库和复杂的用户定义类型等功能。
Solidity可以创建投票,众筹,拍卖(盲拍),多重签名钱包等等的合约。

注:
验证Solidity目前比较好用的是Remix:https://ethereum.github.io/browser-solidity/#version=soljson-v0.4.18+commit.9cf6e910.js
Remix: 基于浏览器的IDE集成编译器和稳定运行时环境,没有服务器端组件。

还有一些其他的可用的Solidity集成软件:

  • IntelliJ IDEA plugin
  • Visual Studio Extension
  • Package for SublimeText — Solidity
  • language syntax
  • Etheratom

2. 智能合约

2.1 一个简单的智能合约

2.1.1 storage

pragma solidity ^0.4.0;

contract SimpleStorage {
    uint storedData;

    function set(uint x) {
        storedData = x;
    }

    function get() constant returns (uint) {
        return storedData;
    }
}

第一行简单说明了代码源为Solidity版本为0.4.0或者其他不会破坏功能的新版本信息(如:version 0.5.0)。这个保证了合约不会与新的版本的编译器发生冲突。

Solidity意义上的合约是位于以太坊区块链的特定地址的代码(其功能)和数据(其状态)的集合。
uint storedData; 声明一个类型为uint(无符号整数为256位)的称为storedData的状态变量。
函数set和get可以用来修改或检索变量的值。

注:要访问状态变量不需要this。

除了允许任何人存储世界上任何人都可以访问的单一号码之外,这个合同还没有做太多的工作(由于以太坊建立的基础设施),没有一个(可行)的方法来阻止你发布这个号码。 当然,任何人都可以用不同的值重新打电话给你,并覆盖你的电话号码,但号码仍然会保存在区块链的历史记录中。 稍后,我们将看到如何强制进行访问限制,以便只有您可以更改数字。

除了简单的存储一个数字以外,这个合约还可以做更多事情(由于以太坊的基础结构),这个世界上没有一个(可行)的方法来阻止你发布这个数字。当然,任何人都可以用一个不同的数字set again,并覆盖你的数字。但是,这个数字会永久的保存在区块链的历史记录中。后面会讲到如何强制进行访问限制,这样就只有我们自己可以更改这个数字。

所有标识符(合同名称,函数名称和变量名称)都限制为ASCII字符集。
可以在字符串变量中存储UTF-8编码的数据。
使用Unicode文本时要小心,因为类似的(甚至相同的)字符可以具有不同的code points,因此会被编码为不同的字节数组。

2.1.2 代币的例子(Subcurrency Example)

下面的合约将实现一个加密货币的最简单形式。 可以凭空产生货币,但是这只有创建合约的人才能做到(用其他货币的发行模式也很简单,只是实现细节上的差异)。 此外,任何人都可以互相发送货币,无需注册用户名和密码 ——只需要一个以太坊密钥对。

pragma solidity ^0.4.0;

contract Coin {
    // The keyword "public" makes those variables
    // readable from outside.
    address public minter;
    mapping (address => uint) public balances;

    // Events allow light clients to react on
    // changes efficiently.
    event Sent(address from, address to, uint amount);

    // This is the constructor whose code is
    // run only when the contract is created.
    function Coin() {
        minter = msg.sender;
    }

    function mint(address receiver, uint amount) {
        if (msg.sender != minter) return;
        balances[receiver] += amount;
    }

    function send(address receiver, uint amount) {
        if (balances[msg.sender] < amount) return;
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        Sent(msg.sender, receiver, amount);
    }
}

address public minter;这行代码声明了一个可公开访问的状态变量,类型为address。address类型为160bits,不允许任何算术运算。public关键字会自动生成一个函数,允许访问状态变量的当前值。没有public关键字的变量将无法被其他合约访问。该函数如下:

function minter() returns (address) { return minter; }

当然,添加一个类似这样的函数是行不通的,因为有一个状态变量和该函数重名,并且编译器也会报错的。

下一行代码:mapping (address => uint) public balances;创建了一个public状态变量。这个类型将地址映射为无符号整形。mapping可以被认为是一个哈希表,每一个可能的key对应的value被虚拟的初始化为全0.这个类比不是很严谨,对于一个mapping,无法获取一个包含其所有key或者value的链表。所以我们需要自己记着添加了哪些东西到mapping中。更好的方式是维护一个这样的链表,或者使用其他更高级的数据类型。或者只在不受这个缺陷影响的场景中使用mapping,就像这个例子。在这个例子中由public关键字生成的访问函数将会更加复杂,其代码大致如下:

function balances(address _account) returns (uint) {
    return balances[_account];
}

我们可以通过这个函数查询某个特定账号的余额。

这行代码event Sent(address from, address to, uint amount); 声明了一个“event”在最后一行的函数“send”中被触发。

客户端(服务端应用也适用)可以以很低的开销来监听这些由区块链触发的事件。事件触发时,监听者会同时接收到from,to,value这些参数值,可以方便的用于跟踪交易。为了监听这个事件,你可以使用如下代码:

Coin.Sent().watch({}, '', function(error, result) {
    if (!error) {
        console.log("Coin transfer: " + result.args.amount +
            " coins were sent from " + result.args.from +
            " to " + result.args.to + ".");
        console.log("Balances now:\n" +
            "Sender: " + Coin.balances.call(result.args.from) +
            "Receiver: " + Coin.balances.call(result.args.to));
    }
}

注意在客户端中是如何调用自动生成的 balances 函数的。

这里有个比较特殊的函数 Coin。它是一个构造函数,会在合约创建的时候运行,之后就无法被调用。它会永久得存储合约创建者的地址。msg(以及tx和block)是一个神奇的全局变量,它包含了一些可以被合约代码访问的属于区块链的属性。msg.sender 总是存放着当前函数的外部调用者的地址。

最后,真正被用户或者其他合约调用,用来完成本合约功能的函数是mint和send。如果合约创建者之外的其他人调用mint,什么都不会发生。而send可以被任何人(拥有一定数量的代币)调用,发送一些币给其他人。注意,当你通过该合约发送一些代币到某个地址,在区块链浏览器中查询该地址将什么也看不到。因为发送代币导致的余额变化只存储在该代币合约的数据存储中。通过事件我们可以很容易创建一个可以追踪你的新币交易和余额的“区块链浏览器”。

2.2 区块链基础

(1)事务/交易
区块链是一个全局共享的,事务性的数据库。每个人都可以读取其中的记录,但是如果想修改的话,就必须创建一个事务。
(2)区块
区块按时间排成一个线性序列。以太坊为17秒。
“双花攻击”——排名较后的交易会被拒绝并剔除出区块。

2.3 以太坊虚拟机(EVM)

以太坊虚拟机(EVM)是以太坊中智能合约的运行环境。它不仅被沙箱封装起来,事实上它被完全隔离,也就是说运行在EVM内部的代码不能接触到网络、文件系统或者其它进程。甚至智能合约与其它智能合约只有有限的接触。

(1)账户
以太坊有两类账户,共用一个地址空间:外部账户(被公钥-私钥对控制),合约账户(被存储在账户中的代码控制)。
外部账户的地址由公钥决定,合约账户的地址在创建合约时确定(这个地址由合约创造者的地址和该地址发出过的交易数量计算得到,地址发出过的交易数量被称作“nonce”)。
合约账户存储了代码,外部账户没有。
每个账户都有一个key-value形式的持久化存储。其中key和value长度都是256比特,名字都叫做storage。
每个账户都有一个以太币余额(单位“Wei”),该账户余额可以通过向她发送带有以太币的交易来改变。

(2)交易(没看懂)
一笔交易是一条消息,从一个账户发送到另一个账户(可能是相同的账户或者零账户,见下文)。交易可以包含二进制数据(payload)和以太币。
如果目标账户包含代码,该代码会执行,payload就是输入数据。
如果目标账户是零账户(账户地址是0),交易将创建一个新合约。正如上文所讲,这个合约地址不是零地址,而是由合约创建者的地址和该地址发出过的交易数量(被称为nonce)计算得到。创建合约交易的payload被当作EVM字节码执行。执行的输出做为合约代码被永久存储。这意味着,为了创建一个合约,你不需要向合约发送真正的合约代码,而是发送能够返回真正代码的代码。

(3)Gas
以太坊上每笔交易都会被收取一定数量的gas。

(4)存储,主存和栈
每个账户都有一块持久化内存区域被称为存储。形式为key-value。一个合约只能对他自己的存储进行读写。
第二个内存区被称为主存。合约每次执行消息调用时,都有一块新的,被清除过的主存。
EVM不是基于寄存器,而是基于栈的虚拟机。

(5)指令集
EVM的指令集被刻意保持在最小规模,以尽可能避免可能导致共识问题的错误实现。

(6)消息调用
合约可以通过消息调用的方式来调用其他合约或者发送以太币到非合约账户。

(7)代码调用/库
存在一种特殊类型的消息调用,被称为callcode。它跟消息调用几乎完全一样,只是加载自目标地址的代码将在发起调用的合约上下文中运行。
这意味着一个合约可以在运行时从另外一个地址动态加载代码。存储,当前地址和余额都指向发起调用的合约,只有代码是从被调用地址获取的。Solidity可以实现”库”。

(8)日志
在区块层面,可以用一种特殊的可索引的数据结构来存储数据。这个特性被称为日志,Solidity用它来实现事件。合约创建之后就无法访问日志数据,但是这些数据可以从区块链外高效的访问。因为部分日志数据被存储在布隆过滤器(Bloom filter) 中,我们可以高效并且安全的搜索日志,所以那些没有下载整个区块链的网络节点(轻客户端)也可以找到这些日志。

(9)创建
合约甚至可以通过一个特殊的指令来创建其他合约(不是简单的向零地址发起调用)。创建合约的调用跟普通的消息调用的区别在于,负载数据执行的结果被当作代码,调用者/创建者在栈上得到新合约的地址。

(10)自毁
只有在某个地址上的合约执行自毁操作时,合约代码才会从区块链上移除。合约地址上剩余的以太币会发送给指定的目标,然后其存储和代码被移除。
注意,即使一个合约的代码不包含自毁指令,依然可以通过代码调用(callcode)来执行这个操作。

其实后面都这大部分都没怎么看懂。。。。。。。。。。

参考文献:
http://solidity.readthedocs.io/en/latest/installing-solidity.html
http://wiki.jikexueyuan.com/project/solidity-zh/installing-solidity.html

展开阅读全文

没有更多推荐了,返回首页