0x03--智能合约之Solidity基础知识

一个简单的例子

pragma solidity ^0.4.21;

contract SimpleAuction {
    // Parameters of the auction. Times are either
    // absolute unix timestamps (seconds since 1970-01-01)
    // or time periods in seconds.
    address public beneficiary;
    uint public auctionEnd;

    // Current state of the auction.
    address public highestBidder;
    uint public highestBid;

    // Allowed withdrawals of previous bids
    mapping(address => uint) pendingReturns;

    // Set to true at the end, disallows any change
    bool ended;

    // Events that will be fired on changes.
    event HighestBidIncreased(address bidder, uint amount);
    event AuctionEnded(address winner, uint amount);

    // The following is a so-called natspec comment,
    // recognizable by the three slashes.
    // It will be shown when the user is asked to
    // confirm a transaction.

    /// Create a simple auction with `_biddingTime`
    /// seconds bidding time on behalf of the
    /// beneficiary address `_beneficiary`.
    function SimpleAuction(
        uint _biddingTime,
        address _beneficiary
    ) public {
        beneficiary = _beneficiary;
        auctionEnd = now + _biddingTime;
    }

    /// Bid on the auction with the value sent
    /// together with this transaction.
    /// The value will only be refunded if the
    /// auction is not won.
    function bid() public payable {
        // No arguments are necessary, all
        // information is already part of
        // the transaction. The keyword payable
        // is required for the function to
        // be able to receive Ether.

        // Revert the call if the bidding
        // period is over.
        require(now <= auctionEnd);

        // If the bid is not higher, send the
        // money back.
        require(msg.value > highestBid);

        if (highestBid != 0) {
            // Sending back the money by simply using
            // highestBidder.send(highestBid) is a security risk
            // because it could execute an untrusted contract.
            // It is always safer to let the recipients
            // withdraw their money themselves.
            pendingReturns[highestBidder] += highestBid;
        }
        highestBidder = msg.sender;
        highestBid = msg.value;
        emit HighestBidIncreased(msg.sender, msg.value);
    }

    /// Withdraw a bid that was overbid.
    function withdraw() public returns (bool) {
        uint amount = pendingReturns[msg.sender];
        if (amount > 0) {
            // It is important to set this to zero because the recipient
            // can call this function again as part of the receiving call
            // before `send` returns.
            pendingReturns[msg.sender] = 0;

            if (!msg.sender.send(amount)) {
                // No need to call throw here, just reset the amount owing
                pendingReturns[msg.sender] = amount;
                return false;
            }
        }
        return true;
    }

    /// End the auction and send the highest bid
    /// to the beneficiary.
    function auctionEnd() public {
        // It is a good guideline to structure functions that interact
        // with other contracts (i.e. they call functions or send Ether)
        // into three phases:
        // 1. checking conditions
        // 2. performing actions (potentially changing conditions)
        // 3. interacting with other contracts
        // If these phases are mixed up, the other contract could call
        // back into the current contract and modify the state or cause
        // effects (ether payout) to be performed multiple times.
        // If functions called internally include interaction with external
        // contracts, they also have to be considered interaction with
        // external contracts.

        // 1. Conditions
        require(now >= auctionEnd); // auction did not yet end
        require(!ended); // this function has already been called

        // 2. Effects
        ended = true;
        emit AuctionEnded(highestBidder, highestBid);

        // 3. Interaction
        beneficiary.transfer(highestBid);
    }
}

文件布局

声明引用的版本号

pragma solidity ^0.4.0;

引入其他文件

import "filename";
import * as symbolName from "filename";
import {symbol1 as alias, symbol2} from "filename";
import "filename" as symbolName;

引入路径

所有路径名都被视为绝对路径,默认从主目录下引入。要从当前文件的同一目录中导入文件x,使用import “./x”作为x
在编译中还可以使用映射源文件

import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;

solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol

solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \
     module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \
     source.sol

Remix中的映射

允许直接使用网络地址

import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;.

注释

// This is a single-line comment.

/*
This is a
multi-line comment.
*/

参考格式:Doxygen

智能合约结构

Solidity合同与面向对象语言中的类相似。每个合约都可以包含状态变量,函数, 函数修饰符,事件,结构类型和枚举类型的声明。此外,合同可以继承其他合同。

类型

由于Solidity是一个静态类型的语言,所以编译时需明确指定变量的类型(包括本地或状态变量)Solidity编程语言提供了一些元类型(elementary types)可以组合成复杂类型。变量也支持在表达式运算,后面有一些关于运算符执行的先后顺序说明。

类型本身包括后面讲到的值类型(Value Types),引用类型(Refrence Type),一些复杂的内置数据结构等。

值类型(Value Type)

值类型又包含:

  • 布尔(Booleans)
    bool : 返回值为 true or false

  • 整型(Integer)
    int / uint : 有符号和无符号整型

  • 地址(Address)
    address:20个字节的值
    地址成员:

    • balance 和 transfer
    • send
    • call,callcode和delegatecall
  • 字节数组(byte arrays)
    byte 为bytes1的alias,引用连接
    成员:

    • .length

    动态大小的字节数组:

    • bytes : 动态大小的字节数组
    • string:动态大小的UTF-8编码字符串
  • 有理数和整型(Rational and Integer Literals,String literals)
    取值范围 0-9

  • 字符串文字
    字符串文字用双引号或单引号("foo"或'bar')编写,隐式转换

  • 十六进制字面量(Hexadecimal Literals)
    十六进制文字以前缀为关键字hex,并用双引号或单引号(hex"001122FF")括起来。它们的内容必须是十六进制字符串,它们的值将是这些值的二进制表示。

  • 枚举类型(Enums)
  enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
    ActionChoices choice;
  • 函数(Function Types)
function () {internal|external} [pure|constant|view|payable] [returns ()]
  • 存储类型
    • memory:内存
    • storage:存储
    • calldata:存储函数参数的不可修改的非持久性区域
  • 数组(array)
    成员:
    • .length
    uint[] memory a = new uint[](7);
    bytes memory b = new bytes(len);
  • 结构(Structs)
    // Defines a new type with two fields.
    struct Funder {
        address addr;
        uint amount;
    }
  • 映射(Mapping)
mapping(_KeyType => _ValueType)_KeyType_ValueType

删除

delete a

基本类型之间的转换

隐式转换

uint8可以转换为 uint16 and int128 to int256
但是 int8 不能转换为 uint256 ,因为uint256不能为-1

显式转换

int8  y  =  - 3 ; 
uint  x  =  uint (y );

自动会默认一个类型

uint24  x  =  0x123 ; 
var  y  =  x ;

单位和全局可用变量

Ether 单位:

wei,finney,szabo或ether

时间单位

seconds,minutes,hours,days,weeks和 years

特殊变量和函数

阻止和事务属性

  • block.blockhash(uint blockNumber) returns (bytes32):给定块的hash - 仅适用于256个最新块,不包括当前块
  • block.coinbase(address):当前块矿工的地址
  • block.difficulty(uint):当前难度
  • block.gaslimit(uint):当前块gaslimit
  • block.number(uint):当前块数
  • block.timestamp(uint):当前块时间戳,因为unix时期以来的秒数
  • gasleft() returns (uint256):剩余gas
  • msg.data(bytes):完成calldata
  • msg.sender(address):消息的发送者(当前发送地址)
  • msg.sig(bytes4):calldata的前四个字节(即函数标识符)
  • msg.value(uint):与消息一起发送的wei的数量
  • now(uint):当前块时间戳(别名为block.timestamp)
  • tx.gasprice(uint):交易的gas价格
  • tx.origin(address):交易的发送者

错误处理(Error Handling)

  • assert(bool condition):
    如果条件不满足则抛出 - 用于内部错误。
  • require(bool condition):
    如果条件未满足则抛出 - 用于输入或外部组件中的错误。
  • revert():
    中止执行并恢复状态更改

数学和加密函数

  • addmod(uint x, uint y, uint k) returns (uint)
    计算添加以任意精度执行的位置

  • mulmod(uint x, uint y, uint k) returns (uint):
    计算以任意精度执行乘法的位置

  • keccak256(...) returns (bytes32)
    计算(紧密排列的)参数的Ethereum-SHA-3(Keccak-256)散列

  • sha256(...) returns (bytes32)
    计算(紧密排列)参数的SHA-256哈希值

  • sha3(...) returns (bytes32)
    别名 keccak256

  • ripemd160(...) returns (bytes20)
    计算(紧密排列)参数的 RIPEMD-160哈希值

  • ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):
    从椭圆曲线签名中恢复与公钥相关的地址,或在错误时返回零(示例用法)

地址相关

  • .balance (uint256)
    地址的余额,单位wei。

  • .transfer(uint256 amount)
    发起转账。failure:throws ,损失2300gas。

  • .send(uint256 amount) returns (bool)
    发起转账。failure:return false ,损失2300gas。

  • .call(...) returns (bool)
    发起低级别转账。failure:return false ,损失全部gas,可以调整

  • .callcode(...) returns (bool)
    发起低级别转账。failure:return false ,损失全部gas,可以调整

  • .delegatecall(...) returns (bool)
    发起低级别转账。failure:return false ,损失全部gas,可以调整

合同相关

  • this 当前合同
  • selfdestruct(address recipient)
    销毁当前合同,将资金发送到指定地址
  • suicide(address recipient)
    selfdestruct 别名

表达式和控制结构

输入参数和输出参数

输入参数

pragma solidity ^0.4.16;

contract Simple {
    function taker(uint _a, uint _b) public pure {
        // do something with _a and _b.
    }
}

输出参数

return v

返回多个值

return (v0, v1, ..., vn)

控制结构

  • switch
  • goto.
  • if, else
  • while, do, for, break, continue,
  • return,
  • ? :

函数调用

this.g(8);
c.g(2);

内部函数调用

pragma solidity ^0.4.16;

contract C {
    function g(uint a) public pure returns (uint ret) { return f(); }
    function f() internal pure returns (uint ret) { return g(7) + f(); }
}

外部函数调用

pragma solidity ^0.4.0;

contract InfoFeed {
    function info() public payable returns (uint ret) { return 42; }
}

contract Consumer {
    InfoFeed feed;
    function setFeed(address addr) public { feed = InfoFeed(addr); }
    function callFeed() public { feed.info.value(10).gas(800)(); }
}

命名的呼叫和匿名功能参数

pragma solidity ^0.4.0;

contract InfoFeed {
    function info() public payable returns (uint ret) { return 42; }
}

contract Consumer {
    InfoFeed feed;
    function setFeed(address addr) public { feed = InfoFeed(addr); }
    function callFeed() public { feed.info.value(10).gas(800)(); }
}

省略函数参数名称

pragma solidity ^0.4.16;

contract C {
    // omitted name for parameter
    function func(uint k, uint) public pure returns(uint) {
        return k;
    }
}

通过 new 创建合同

pragma solidity ^0.4.0;

contract D {
    uint x;
    function D(uint a) public payable {
        x = a;
    }
}

contract C {
    D d = new D(4); // will be executed as part of C's constructor

    function createD(uint arg) public {
        D newD = new D(arg);
    }

    function createAndEndowD(uint arg, uint amount) public payable {
        // Send ether along with the creation
        D newD = (new D).value(amount)(arg);
    }
}

合同

函数修饰符

  • external
    外部函数,可以从其他合同和交易中调用它们。不能在内部调用,也就是说f()不起作用,但是可以使用this.f()来调用。在接收大量数据的时候更高效。
  • public
    公共函数。默认值。
    可以在内部或者通过消息调用。
  • internal
    内部函数。只能在内部合约进行使用,不能使用this。
  • private
    私有函数。只能在当前合约内使用。

所有外部观察者都可以看到合约内的所有内容。private 只会阻止其他合约访问和修改信息,但在区块链之外,整个世界仍然可以看到代码。

Getter函数

编译器会自动为所有的public 函数或者变量生成 Getter函数。

添加验证( Modifier )

使用方式:

/**
     * @dev Allows owner to remove an employee.
     * @param employeeId The id of the employee.
     */
    function removeEmployee(address employeeId)
        public
        onlyOwner
        employee_exist(employeeId)
    {
        _settlePayment(employeeId);
        _totalSalary = _totalSalary.sub(employees[employeeId].salary);
        delete employees[employeeId];
    }

译自官方文档

恒定状态变量

状态变量可以声明为constant。

函数

声明 View

函数可以声明view,在这种情况下,它们保证不修改状态。

  • 写入状态变量。
  • 发射事件。
  • 创建其他合同。* 使用selfdestruct
  • 通过电话发送以太。
  • 调用任何未标记的功能viewpure
  • 使用低级别呼叫。
  • 使用包含某些操作码的内联汇编

声明pure

函数可以声明为pure,在这种情况下,它们保证不读取或修改状态。

以下内容被认为是从状态中读取的:

  • 从状态变量读取。
  • 访问this.balance或
    .balance。
  • 访问任何成员block,tx,msg(与除外msg.sig和msg.data)。
  • 调用任何未标记的功能pure。
  • 使用包含某些操作码的内联汇编。

Fallback 函数

合同可以有一个未命名的功能。这个函数不能有参数,也不能返回任何东西。如果没有其他函数与给定的函数标识符匹配(或者根本没有提供数据),它将在对合同的调用中执行。

pragma solidity ^0.4.0;

contract Test {
    // This function is called for all messages sent to
    // this contract (there is no other function).
    // Sending Ether to this contract will cause an exception,
    // because the fallback function does not have the `payable`
    // modifier.
    function() public { x = 1; }
    uint x;
}


// This contract keeps all Ether sent to it with no way
// to get it back.
contract Sink {
    function() public payable { }
}

contract Caller {
    function callTest(Test test) public {
        test.call(0xabcdef01); // hash does not exist
        // results in test.x becoming == 1.

        // The following will not compile, but even
        // if someone sends ether to that contract,
        // the transaction will fail and reject the
        // Ether.
        //test.send(2 ether);
    }
}

函数重载

允许有相同名称但是不同参数的方法共存

pragma solidity ^0.4.16;

contract A {
    function f(uint _in) public pure returns (uint out) {
        out = 1;
    }

    function f(uint _in, bytes32 _key) public pure returns (uint out) {
        out = 2;
    }
}

Events

事件方便外部进行监听。

pragma solidity ^0.4.0;

contract ClientReceipt {
    event Deposit(
        address indexed _from,
        bytes32 indexed _id,
        uint _value
    );

    function deposit(bytes32 _id) public payable {
        // Events are emitted using `emit`, followed by
        // the name of the event and the arguments
        // (if any) in parentheses. Any such invocation
        // (even deeply nested) can be detected from
        // the JavaScript API by filtering for `Deposit`.
        emit Deposit(msg.sender, _id, msg.value);
    }
}

在web3中调用事件

var abi = /* abi as generated by the compiler */;
var ClientReceipt = web3.eth.contract(abi);
var clientReceipt = ClientReceipt.at("0x1234...ab67" /* address */);

var event = clientReceipt.Deposit();

// watch for changes
event.watch(function(error, result){
    // result will contain various information
    // including the argumets given to the `Deposit`
    // call.
    if (!error)
        console.log(result);
});

// Or pass a callback to start watching immediately
var event = clientReceipt.Deposit(function(error, result) {
    if (!error)
        console.log(result);
});

日志记录

log0, log1, log2, log3, log4...

pragma solidity ^0.4.10;

contract C {
    function f() public payable {
        bytes32 _id = 0x420042;
        log3(
            bytes32(msg.value),
            bytes32(0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20),
            bytes32(msg.sender),
            _id
        );
    }
}

继承(Inheritance )

详细的路径可以参考专门写继承的一个C3 Linearization 线性化python。
如果想使用父类方法,需要声明 super

构造函数

同名函数为构造函数,也就是在调用的时候默认会执行的函数。可以声明为public,internal。

抽象合约

仅仅声明,没有实体

pragma solidity ^0.4.0;

contract Feline {
    function utterance() public returns (bytes32);
}

接口

接口与抽象类似,但是还有更多的限制。

  • 无法继承其他合同或接口。
  • 无法定义构造函数。
  • 无法定义变量。
  • 无法定义结构。
  • 无法定义枚举。
pragma solidity ^0.4.11;

interface Token {
    function transfer(address recipient, uint amount) public;
}

部署在特定地址的公用函数

Using For

我理解类似apply或者中间件的意思,所有的参数使用先经过这个use 函数的处理。

contract C {
    using Set for Set.Data; // this is the crucial change
    Set.Data knownValues;

    function register(uint value) public {
        // Here, all variables of type Set.Data have
        // corresponding member functions.
        // The following function call is identical to
        // `Set.insert(knownValues, value)`
        require(knownValues.insert(value));
    }
}

优先级

Precedence Description Operator
1 Postfix increment and decrement ++, --
New expression new
Array subscripting []
Member access .
Function-like call ()
Parentheses ()
2 Prefix increment and decrement ++, --
Unary plus and minus +, -
Unary operations delete
Logical NOT !
Bitwise NOT ~
3 Exponentiation **
4 Multiplication, division and modulo *, /, %
5 Addition and subtraction +, -
6 Bitwise shift operators <<, >>
7 Bitwise AND &
8 Bitwise XOR ^
9 Bitwise OR |
10 Inequality operators <, >, <=, >=
11 Equality operators ==, !=
12 Logical AND &&
13 Logical OR ||
14 Ternary operator ? :
15 Assignment operators =, |=, ^=, &=, <<=, >>=, +=, -=, *=, /=, %=
16 Comma operator ,
  • 本文作者:Jack Yao
  • 本文链接: eth_0x03_contract_solidity
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

你可能感兴趣的:(0x03--智能合约之Solidity基础知识)