solidity文档审阅javascript
`pragma solidity ^0.4.0;
contract HelloWorld{
uint balance;
function update(uint amount) returns (address, uint){
balance += amount;
return (msg.sender, balance);
}
}`java
读取函数输出的新值,累加到合约变量中 返回发送人的地址 和累余额。python
部署函数 create 能在浏览器函数创造能调用的函数按钮。 在update输入参数,点击按钮就能执行函数而且打印结果。git
出错的话 等待浏览器加载资源完成再操做es6
1.版本申明
pragma solidity ^0.4.0;
2.全局引入
import “filename”;
3.自定义命名空间
import * as symbolName from “filename”
非es6兼容的简写语法
import “filename” as symbolName
等同于import*as symbolName from "filename"github
注意事项:非.打头的会被认为是绝对路径 所以要引用同级目录 必须如下写法 import “./x” as x 错误方式 import “x” as x;(这样会在全局目录下 等同与网络地址)web
命令行编译器 经过如下命名空间提供支持算法
context:prefix=target
在contex目录下的以prefix开头的会被替换为target
举例说:若是你将github.com/ethereum/dapp-bin拷到本地的/usr/local/dapp-bin,并使用下述方式使用文件
import “github.com/ethereum/dapp-bin/library/iterable_mapping.sol” as it_mapping;
要编译只须要
solc github.com/ethereum/dapp-bin=/usr/local/dapp-bin source.solapi
旧版本的写法
solc module1:github.com/ethereum/dapp-bin=/usr/local/dapp-bin modeule2:github.com/ethereum/dapp-bin=/usr/local/dapp-bin_old source.sol数组
solc仅仅容许包含实际存在的文件 他必存在于你映射后的目录里 若是你想包含绝对路径那么能够将命名空间映射为= 注意:若是多重映射指向同一个文件 那么取最长的文件
会自动映射到github上 而后自动检索文件 所以能够经过如下方式进行引入一个迭代包
import “github.com/ethereum/dapp-bin/library/iterable_mapping.sol” as it_mapping
备注:将来可能会支持其余的源码方式
//注释
多行注释/.../
// this is a single-line comment
///或者/../ 也可使用Doxygen语法 能够生成对文档的说明 参数的注解或者用户在调用函数,弹出来的确认内容。
pragma solidity ^0.4.0;
/** @title Shape calculator.*/
contract shapeCalculator{
/**
*@dev calculate a rectangle's suface and perimeter
*@param w width of the rectangles *@param h height of the rectangles *@return s surface of the rectangles *@return p perimeter of the rectangles */ function rectangles(uint w, uint h) returns (uint s, uint p){ s = w * h; p = 2 * ( w + h) ; }
}
状态变量
pragma solidity ^0.4.0;
// simple store example
contract simpleStorage{
uint valueStore; //state variable
}
函数
函数分为内部和外部调用 同时对于其余合约不一样级别的可见性和访问控制
函数修饰符
事件
事件是evm提供的一个便利接口 用于获取当前实践
例如
pragma solidity ^0.4.0;
contract SimpleAuction {
event aNewHigherBid(address bidder, uint amount);
function bid(uint bidValue) external { aNewHigherBid(msg.sender, msg.value); }
}
结构类型
结构体
pragma solidity ^0.4.0;
contract Company {
//user defined Employee
struct type
//group with serveral variables
struct employee{
string name;
uint age;
uint salary;
}
//User defined `manager` struct type //group with serveral variables struct manager{ employee employ; string title; }
}
枚举类型
用于列举全部的状况
pragma solidity ^0.4.0;
contract Home {
enum Switch{On,Off}
}
库就是合约 他的目的在于仅仅部署一次来复用代码 经过的是DELEGATECALL这个方法 。 这意味着库函数调用的 他的代码是在上下文中执行
使用库合约的 能够称为隐藏的父合约,他们不会显示出如今继承关系上
对两个库文件使用的说明
pragma solidity ^0.4.0;
library Set {
// We define a new struct datatype that will be used to
// hold its data in the calling contract.
struct Data { mapping(uint => bool) flags; }
// Note that the first parameter is of type "storage
// reference" and thus only its storage address and not
// its contents is passed as part of the call. This is a
// special feature of library functions. It is idiomatic
// to call the first parameter 'self', if the function can
// be seen as a method of that object.
function insert(Data storage self, uint value)
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}
function remove(Data storage self, uint value)
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}
function contains(Data storage self, uint value)
returns (bool)
{
return self.flags[value];
}
}
contract C {
Set.Data knownValues;
function register(uint value) { // The library functions can be called without a // specific instance of the library, since the // "instance" will be the current contract. if (!Set.insert(knownValues, value)) throw; } // In this contract, we can also directly access knownValues.flags, if we want.
}
1.library定义了一个结构体 用来在调用合约时候使用 若是函数须要操做数据 这个数据通常是经过库函数的第一个参数传入 按惯例会把参数定为self
2.self的类型是storage 意味着是一个函数引用 修改他会影响全部的地方
3.库函数的使用不须要实例化 c.register 能够看出使用set.insert 但实际上当前的合约自己就是一个实例
3.c能够直接访问 kownValues这个值能够被库函数调用
另外一种调用方法:
`pragma solidity ^0.4.0;
library BigInt {
struct bigint {
uint[] limbs;
}
function fromUint(uint x) internal returns (bigint r) { r.limbs = new uint[](1); r.limbs[0] = x; } function add(bigint _a, bigint _b) internal returns (bigint r) { r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length)); uint carry = 0; for (uint i = 0; i < r.limbs.length; ++i) { uint a = limb(_a, i); uint b = limb(_b, i); r.limbs[i] = a + b + carry; if (a + b < a || (a + b == uint(-1) && carry > 0)) carry = 1; else carry = 0; } if (carry > 0) { // too bad, we have to add a limb uint[] memory newLimbs = new uint[](r.limbs.length + 1); for (i = 0; i < r.limbs.length; ++i) newLimbs[i] = r.limbs[i]; newLimbs[i] = carry; r.limbs = newLimbs; } } function limb(bigint _a, uint _limb) internal returns (uint) { return _limb < _a.limbs.length ? _a.limbs[_limb] : 0; } function max(uint a, uint b) private returns (uint) { return a > b ? a : b; }
}
contract C {
using BigInt for BigInt.bigint;
function f() { var x = BigInt.fromUint(7); var y = BigInt.fromUint(uint(-1)); var z = x.add(y); }
}`
编译器并不知道库的最终地址 这些地址必须linker填进的最终字节码钟 若是地址没有参数的方式正确编译器 编译后的字节码仍会包含一个这样的字符 能够手动替换为十六进制地址。
using A for *的效果是,库A中的函数被附着在作任意的类型上。
using A for B;指令仅在当前的做用域有效,且暂时仅仅支持当前的合约这个做用域,后续也很是有可能解除这个限制,容许做用到全局范围。若是能做用到全局范围,经过引入一些模块(module),数据类型将能经过库函数扩展功能,而不须要每一个地方都得写一遍相似的代码了。
solidity预留了3个32字节的大小的槽位
0-64:哈希方法的暂存空间(scratch space)
64-96:当前已分配内存大小(也称空闲内存指针(free memory pointer))
有一些在Solidity中的操做须要超过64字节的临时空间,这样就会超过预留的暂存空间。他们就将会分配到空闲内存指针所在的地方,但因为他们自身的特色,生命周期相对较短,且指针自己不能更新,内存也许会,也许不会被清零(zerod out)。所以,你们不该该认为空闲的内存必定已是清零(zeroed out)的。
空间的空间不必定会被清零。
调用合约的帐户 输入的数据须要符合abi的标准
特色:
1.第一项是按照低位对其存储
2.基本理性存储使用实际字节
3.基本类型不能存放 放入下一个槽位
4.结构体和数组老是占满槽位
若是操做少于32 应当使用实际的字节
编译器会将多个元素进行打包到一个槽位
最后为了方便evm进行优化 尝试有意识的storage变量和结构体成员 好比应当使用uint128 uint128 uint256 而不是使用uint128 uint256 uint128
原理:编译器会将多个元素打包一个槽位 进行一次读写 当操做momory 编译器不会优化 上述草操做没有意义
结构体和数组里的元素按照他们给定的顺序存储。
因为他们不可预估大小。
1.evm堆栈
2.映射和动态数组很具规则在p沾满一个未满的槽位。 对一个的动态数组,位置p这个槽位。 对于映射,这个槽位没有填充任何数据。 数组的原始位置是keccak(256) 映射位置k.p
pragma solidity ^0.4.4;
contract C {
struct s { uint a; uint b; }
uint x;
mapping(uint => mapping(uint => s)) data;
}
3.按照实际的代码来看 结构体从0开始 定义了一个结构体 但并无对应的结构体变量 故不占用空间。 uint x实际uint256 占用32个字节 占用槽位0 因此映射data 将从槽位1开始
data[4][9].b的位置在kecca256(uint256(9)).keccak256(uint256(4).uint256(1))+1;
做为AST输出的一部分,编译器会提供AST某个节点以应的源代码的范围。
上述源代码的映射都使用整数来引用源代码
使用var来做为本地变量
contract FunctionSelector {
function select(bool useB, uint x) returns (uint z) {
var f = a;
if (useB) f = b;
return f(x);
}
function a(uint x) returns (uint z) {
return x * x;
}
function b(uint x) returns (uint z) {
return 2 * x;
}
}
能够对var赋值为不一样的函数
设计思想:在任何潜在的参与数据带来反作用以前 清理掉这些脏数据。 这些脏数据会带来意想不到的错误。 特别是storage中。
若是不会形成反作用则不会清理。
1.使用他编写代码要可读
2.从汇编语言转为字节码应该尽量少坑
3.控制流便于优化和验证
所以solidity设计了下面的东西:
for
switch
这样比swap dup jump jumpi 更加可读
函数形式的语句如mul(add(x,y),7)比7y x add num更加可读
引入一个绝对阶段来实现 该阶段只能以很是规则的方式取除较高阶别的构造,而且仍容许检查低级汇编语言。 函数名变量名 这些遵循soldity的规则
为何使用高层级的构造器。
每个变量在栈的钟位置是已知的。
示例:
咱们来考虑一下solidity程序的字节码:
contract C {
function f(uint x) returns (uint y) {
y = 1;
for (uint i = 0; i < x; i++)
y = 2 * y;
}
}
生成的汇编
{
mstore(0x40, 0x60) // store the "free memory pointer"
// function dispatcher
switch div(calldataload(0), exp(2, 226))
case 0xb3de648b {
let (r) = f(calldataload(4))
let ret := $allocate(0x20)
mstore(ret, r)
return(ret, 0x20)
}
default { revert(0, 0) }
// memory allocator
function $allocate(size) -> pos {
pos := mload(0x40)
mstore(0x40, add(pos, size))
}
// the contract function
function f(x) -> y {
y := 1
for { let i := 0 } lt(i, x) { i := add(i, 1) } {
y := mul(2, y)
}
}
}
脱机汇编
{
mstore(0x40, 0x60)
{
let $0 := div(calldataload(0), exp(2, 226))
jumpi($case1, eq($0, 0xb3de648b))
jump($caseDefault)
$case1:
{
// the function call - we put return label and arguments on the stack
$ret1 calldataload(4) jump(f)
// This is unreachable code. Opcodes are added that mirror the
// effect of the function on the stack height: Arguments are
// removed and return values are introduced.
pop pop
let r := 0
$ret1: // the actual return point
$ret2 0x20 jump($allocate)
pop pop let ret := 0
$ret2:
mstore(ret, r)
return(ret, 0x20)
// although it is useless, the jump is automatically inserted,
// since the desugaring process is a purely syntactic operation that
// does not analyze control-flow
jump($endswitch)
}
$caseDefault:
{
revert(0, 0)
jump($endswitch)
}
$endswitch:
}
jump($afterFunction)
allocate:
{
// we jump over the unreachable code that introduces the function arguments
jump($start)
let $retpos := 0 let size := 0
$start:
// output variables live in the same scope as the arguments and is
// actually allocated.
let pos := 0
{
pos := mload(0x40)
mstore(0x40, add(pos, size))
}
// This code replaces the arguments by the return values and jumps back.
swap1 pop swap1 jump
// Again unreachable code that corrects stack height.
0 0
}
f:
{
jump($start)
let $retpos := 0 let x := 0
$start:
let y := 0
{
let i := 0
$for_begin:
jumpi($for_end, iszero(lt(i, x)))
{
y := mul(2, y)
}
$for_continue:
{ i := add(i, 1) }
jump($for_begin)
$for_end:
} // Here, a pop instruction will be inserted for i
swap1 pop swap1 jump
0 0
}
$afterFunction:
stop
}
汇编的几个阶段:
1.解析
2.脱汇编
3.生成指令流
4.生成字节码
关键点:
1>
将字节流转为符号流,去掉其中的C++风格的注释(一种特殊的源代码引用的注释,这里不打算深刻讨论)。
将符号流转为下述定义的语法结构的AST。
注册块中定义的标识符,标注从哪里开始(根据AST节点的注解),变量能够被访问。
2>
一个AST转换,移除其中的for,switch和函数构建。结果仍由同一个解析器,但它不肯定使用什么构造。若是添加仅跳转到而且不继续的jumpdests,则添加有关堆栈内容的信息,除非没有局部变量访问到外部做用域或栈高度与上一条指令相同。
3>
在操做码流生成期间,咱们在一个计数器中跟踪当前的栈高,因此经过名称访问栈的变量是可能的。栈高在会修改栈的操做码后或每个标签后进行栈调整。当每个新局部变量被引入时,它都会用当前的栈高进行注册。若是要访问一个变量(或者拷贝其值,或者对其赋值),会根据当前栈高与变量引入时的当时栈高的不一样来选择合适的DUP或SWAP指令。
着手解决各类特性:
容许函数风格
内联局部变量
可访问外部变量
标签
循环
switch语句
函数调用
assembly内联编译语言
内有安全机制 指的是一种底层访问
示例:
使用时间:
在编译器没办法获得有效率的代码时候很是有用。
pragma solidity ^0.4.0;
library VectorSum {
// This function is less efficient because the optimizer currently fails to
// remove the bounds checks in array access.
function sumSolidity(uint[] _data) returns (uint o_sum) {
for (uint i = 0; i < _data.length; ++i)
o_sum += _data[i];
}
// We know that we only access the array in bounds, so we can avoid the check. // 0x20 needs to be added to an array because the first slot contains the // array length. function sumAsm(uint[] _data) returns (uint o_sum) { for (uint i = 0; i < _data.length; ++i) { assembly { o_sum := mload(add(add(_data, 0x20), mul(i, 0x20))) } } }
}
字面量 ox123 42 或者abc
操做码 mload sload dup1 sstore 后面有课支持的指令列表
函数风格 add(1,mlod(0))
标签 name:
变量定义 let x:=7 let x:=add(y,3)
赋值 jump(name) x3 add
函数风格的赋值
支持块级局部变量
solidity惯例
固定长度的memory数组没有一个长度字段 但他们将很快增长这个字段 让定长和变长数组之间有更好的转换能力 因此不要依赖这一点
限制
接口仅仅限制abi能够表示的内容不会有任何信息丢失
接口用本身的关键词表示
interface Token{
function transfer(address recipient,uint amount);
}
合约能够继承接口 由于他们能够继承其余合约。
抽象函数是没有函数体的函数
以下:
pragma solidity ^0.4.0;
contract Feline {
function utterance() returns (bytes32);
}
这样的合约不能经过编译 即便合约内容 也包含一些正常函数
pragma solidity ^0.4.0;
contract Feline {
function utterance() returns (bytes32);
function getContractName() returns (string){ return "Feline"; }
}
contract Cat is Feline {
function utterance() returns (bytes32) { return "miaow"; }
}
若是从一个合约从一个合约里继承 但却没有实现全部函数 那么他也是一个抽象合约
多重继承 能够继承多个合约
原理:父合于会被拷贝过来代码 造成一个合体
一个特殊的例子:
pragma solidity ^0.4.0;
contract mortal is owned {
function kill() {
if (msg.sender == owner) selfdestruct(owner);
}
}
contract Base1 is mortal {
function kill() { /* do cleanup 1 */ mortal.kill(); }
}
contract Base2 is mortal {
function kill() { /* do cleanup 2 */ mortal.kill(); }
}
contract Final is Base1, Base2 {
}
结果:合约只会执行base2.kill 由于会派生重写 会跳base1.kill 由于他根本不知道有base1
一个变通的方法是使用super
pragma solidity ^0.4.0;
contract Base {
uint x;
function Base(uint _x) { x = _x; }
}
contract Derived is Base(7) {
function Derived(uint _y) Base(_y * _y) {
}
}
一种方式是头部声名 还有一种是合约头生声名 第二钟方式优先
须要解决的是钻石问题 钻石问题 解决方法是参考python
pragma solidity ^0.4.0;
contract X {}
contract A is X {}
contract C is A, X {}
错误Linearization of inheritance graph impossible
缘由是c会请求x来重写a a又是重写x 所以这是一个不可能解决的矛盾
当继承最后致使一个合约同时才能在多个名字相同的修改器时 他会被视为一个错误 同新的若是事件与修改器重名
或为一个例外 getter能够覆盖一个public函数
事件是使用EVM日志内置功能的方便工具,在DAPP的接口中,它能够反过来调用Javascript的监听事件的回调。
原理:事件能够被继承 被调用时候 出发的参数将存储到交易日志中 这些日志与合约地址关联 并合并到区块链中 区块存在就能够一直访问。
日至不能再合约被直接访问
如何获取日志的例子
var abi = /* abi as generated by the compiler /;
var ClientReceipt = web3.eth.contract(abi);
var clientReceipt = ClientReceipt.at(0x123 / 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);
});
步骤:
1.使用web3调用abi 2.拿到合约地址contract.at 实例化合约 3.调用watch方法 获取result
4.回调函数
经过函数能够访问 logN表明当问参数的数目 默认从0开始
第一个参数被做为日志的一部分 其余的会做为主题
log3{
msg.value,
0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20,
msg.sender,
_id
}
没有匹配任何值会默认调用这个函数
支付eth时也会调用这个函数。
若是下述函数写在回退里面 那么花费将会不少
1.写入合约
2.建立合约
3.执行一个外部函数
4.发送ether
注意 要保证你的函数花费在2300gas之内
一个没有定义一个回退函数的合约 若是接收ether 会触发异常 返还ether(0.4.0开始) 合约要接收ether 必须事先回退函数
能够被定义为constant
赋值的表达式不容许:1)访问storage;2)区块链数据,如now,this.balance,block.number;3)合约执行的中间数据,如msg.gas;4)向外部合约发起调用。
当前的支持的仅有值类型和字符串。
函数也能够被声明为常量 这类函数承诺本身不修改状态
当前不会强制使用 可是建议对于不修改的数据标记为constant
修改器(Modifiers)能够用来轻易的改变一个函数的行为。好比用于在函数执行前检查某种前置条件。修改器是一种合约属性,可被继承,同时还可被派生的合约重写(override)。
一个变量在声明后都有初始值为字节表示的全0值。也就是全部类型的默认值是典型的零态(zero-state)。举例来讲,默认的bool的值为false,uint和int的默认值为0。
全部的类型默认零态。 举例 bool默false uint 和int默认值为0
对于变长的数组bytes和string,默认值则为空数组和空字符串。
另外 一个变量被声明了 那么他会在开始前被初始化位默认值
pragma solidity ^0.4.0;
contract C{
function foo() returns (uint) {
// baz is implicitly initialized as 0
uint bar = 5;
if (true) {
bar += baz;
} else {
uint baz = 10;// never executes
}
return bar;// returns 5
}
}
内置结果返回多个值
数组类型 赋值语法有一些复杂
赋值给一个状态变量老是建立一个彻底无关的拷贝
赋值给一个局部变量 仅仅对支持的类型 如那些32字节之内的静态类型 建立一份彻底无关的拷贝
。。。。麻烦
若是是数据结构和数组 bytes 和string 由状态变量赋值为一个局部变量,局部变量只是持有原始状态变量的一个引用。
表达式的求值顺序并非肯定的,更正式的说法是,表达式树一个节点的某个节点在求值时的顺序是不肯定的,可是它确定会比节点自己先执行。
不能限制gas使用 由于无足够的余额 会抛出一个异常
直接调用 或者递归调用
contract C {
function g(uint a) returns (uint ret) { return f(); }
function f() returns (uint ret) { return g(7) + f(); }
}
在合约的构造器中,不能使用this调用函数,由于当前合约尚未建立完成。
发生异常的几种状况
若是被调用的合约不存在,或者是不包代码的账户,或调用的合约产生了异常,或者gas不足,均会形成函数调用发生异常。写一个函数,只有当状态变量(state variables)的值有对应的改变时,才调用外部函数,这样你的合约就不会有可重入性漏洞。
可重入漏洞 外部函数修改内部的值 合约之间交互存在潜在危险
函数调用的参数,能够经过指定名字的方式调用,但能够以任意的顺序,使用方式是{}包含。但参数的类型和数量要与定义一致。
没有使用的参数名能够省略(通常常见于返回值)。这些名字在栈(stack)上存在,但不可访问。
没有使用的能够省略 这样的在站上存在 可是不能够访问
持if,else,while,do,for,break,continue,return,?:。
好比if(1){...}在Solidity中是无效的。由于不存在自动转换。
同javascript同样,函数有输入参数,但与之不一样的是,函数可能有任意数量的返回参数。
当返回多个参数时,使用return (v0, v1, ..., vn)。返回结果的数量须要与定义的一致。
.balance (uint256):
Address的余额,以wei为单位。
.transfer(uint256 amount):
发送给定数量的ether,以wei为单位,到某个地址。失败时抛出异常。
.send(uint256 amount) returns (bool):
发送给定数量的ether,以wei为单位,到某个地址。失败时返回false。
.call(...) returns (bool):
发起底层的call调用。失败时返回false。
.callcode(...) returns (bool):
发起底层的callcode调用,失败时返回false。
.delegatecall(...) returns (bool):
发起底层的delegatecall调用,失败时返回false。
asser(bool condition):
若是条件不知足,抛出异常。
addmod(uint x, uint y, uint k) returns (uint):
计算(x + y) % k。加法支持任意的精度。但不超过(wrap around?)2**256。
keccak256(...) returns (bytes32):
使用以太坊的(Keccak-256)计算HASH值。紧密打包。
sha3(...) returns (bytes32):
等同于keccak256()。紧密打包。
sha256(...) returns (bytes32):
使用SHA-256计算HASH值。紧密打包。
ripemd160(...) returns (bytes20):
使用RIPEMD-160计算HASH值。紧密打包。
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):
经过签名信息恢复非对称加密算法公匙地址。若是出错会返回0,附录提供了一个例子1.
revert():
取消执行,并回撤状态变化。
- 在私链上 一个可能存在问题是:
向一个不存在的合约发送消息,很是昂贵,因此才会致使Out-Of-Gas的问题。
2.须要注意的是字面量会用,尽量小的空间来存储它们。好比,keccak256(0) == keccak256(uint8(0)),keccak256(0x12345678) == keccak256(uint32(0x12345678))
3.若是须要补位,须要明确的类型转换,如keccak256("\x00\x12")等同于keccak256(uint16(0x12))
一些变量村子当前上下文中 用来获取一些区块信息
block.blockhash(uint blockNumber) returns (bytes32),给定区块号的哈希值,只支持最近256个区块,且不包含当前区块。
block.coinbase (address) 当前块矿工的地址。
block.difficulty (uint)当前块的难度。
block.gaslimit (uint)当前块的gaslimit。
block.number (uint)当前区块的块号。
block.timestamp (uint)当前块的时间戳。
msg.data (bytes)完整的调用数据(calldata)。
msg.gas (uint)当前还剩的gas。
msg.sender (address)当前调用发起人的地址。
msg.sig (bytes4)调用数据的前四个字节(函数标识符)。
msg.value (uint)这个消息所附带的货币量,单位为wei。
now (uint)当前块的时间戳,等同于block.timestamp
tx.gasprice (uint) 交易的gas价格。
tx.origin (address)交易的发送者(完整的调用链)
1 == 1 seconds
1 minutes == 60 seconds
1 hours == 60 minutes
1 days == 24 hours
1 weeks = 7 days
1 years = 365 days
若是须要进行单位转换 要特别当心 不是天天都有24小时 不是每一年都是365天
解决方案: 必须有一个外部的orcale来跟新获得一个准确的日历(内部消耗gas)
示范:
pragma solidity ^0.4.0;
contract DeleteExample{
function nowInSeconds() returns (uint256){ return now; } function f(uint start, uint daysAfter) { if (now >= start + daysAfter * 1 days) { } }
}
一个字面量的数字,可使用后缀wei,finney,szabo或ether来在不一样面额中转换。不含任何后缀的默认单位是wei。如2 ether == 2000 finney的结果是true。
函数的参数,包括返回参数,不可使用var这种不指定类型的方式。
代码for (var i = 0; i < 2000; i++) {}将是一个无限循环,由于一个uint8的i的将小于2000。
代码不能编译经过
编译器会根据第一个值进行推断
隐士转换和显示转换。
Uint8 能够转换成uint256 可是int8不能转换成uint256
由于uint256不能表示-1
显示转换:
pragma solidity ^0.4.0;
contract DeleteExample{
uint a;
function f() returns (uint){ int8 y = -3; uint x = uint(y); return x; }
}
uint32 a = 0x12345678;
uint16 b = uint16(a); // b will be 0x5678 now
运算有
-= += *= %= |= &= ^= ++ --
不能对一个storage的类型赋值
pragma solidity ^0.4.0;
//file indeed for compile
//may store in somewhere and import
contract MappingExample{
mapping(address => uint) public balances;
function update(uint amount) returns (address addr){ balances[msg.sender] = amount; return msg.sender; }
}
contract MappingUser{
address conAddr; address userAddr; function f() returns (uint amount){ //address not resolved! //tringing conAddr = hex"0xf2bd5de8b57ebfc45dcee97524a7a08fccc80aef"; userAddr = hex"0xca35b7d915458ef540ade6068dfe2f44e8fa733c"; return MappingExample(conAddr).balances(userAddr); }
}
solidity 和结构体自定义类型
不能声明一个struct同时将这个struct做为这个struct的一个成员。这个限制是基于结构体的大小必须是有限的。
虽然数据结构能做为一个mapping的值,但数据类型不能包含它自身类型的成员,由于数据结构的大小必须是有限的。
须要注意的是在函数中,将一个struct赋值给一个局部变量(默认是storage类型),实际是拷贝的引用,因此修改局部变量值时,会影响到原变量。
固然,你也能够直接经过访问成员修改值,而不用必定赋值给一个局部变量,如campaigns[comapingnId].amount = 0
storage来讲 元素的类型是任意的 类型能够是数组映射或者数据结构
可使用new新建momory数组 可是不能更改 storag能够修改。 Error: Expression has to be an lvalue.
数组字面量,是指以表达式方式隐式声明一个数组,并做为一个数组变量使用的方式。下面是一个简单的例子:
pragma solidity ^0.4.0;
contract C {
function f() {
g([uint(1), 2, 3]);
}
function g(uint[3] _data) {
// ...
}
}
abi不能支持数组 之后也许会解除限制
length属性
不能访问超出数组长度的方式
push方法
storage的变长数组都有一个push
限制的状况处理:
web3.js能返回数据 可是不能返回数据 一种方法是使用一个很是大的静态数组
calldata
存储的是函数 而且是只读的 不会被永久存储 外部函数的参数被强制指定为calldata 效果与momoey差很少
外部函数(External function)的参数(不包括返回参数)强制为:calldata
状态变量(State variables)强制为: storage
函数参数括返回参数:memory
全部其它的局部变量:storage
复杂类型
不一样于以前的类型 复杂类型占据更大的空间 超256 拷贝占据空间更多的空间
1.赋值
2.参数传递
3.函数调用中返回
外部函数
内部函数
函数的完整定义
function (
默认是内部调用external 没有返回必须值省略结果returns
f和this.f的区别
前者是内部调用 后者是外部调用
这里的this和大多数语言向违背
library是什么呢。
library引入时为何使用using,这和文件引入的import有何区别。
library内的函数全是internal的。
library内的函数,他的参数函数为何是internal的,不该该是external的?
uint[]是什么类型,不能写作[]uint
memory又是什么呢,为何map函数明明是两个参数,但只须要传一个呢。
用户自定义类型 应当至少一名成员
因为一个字节是8位,因此一个hex是由两个[0-9a-z]字符组成的。因此var b = hex"A";不是成双的字符串是会报错的。
pragma solidity ^0.4.0;
contract HexLiteralBytes{
function test() returns (bytes4, bytes1, bytes1, bytes1, bytes1){
bytes4 a = hex"001122FF";
return (a, a[0], a[1], a[2], a[3]);
}
}
Result: "0x001122ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000000"
Transaction cost: 21857 gas.
Execution cost: 585 gas.
Decoded:
bytes4: 0x001122ff
bytes1: 0x00
bytes1: 0x11
bytes1: 0x22
bytes1: 0xff
字符串字面量是指由单引号,或双引号引发来的字符串。字符串并不像C语言,包含结束符,foo这个字符串大小仅为三个字节。
字符串的长度也是能够边唱的 能够转化为字节
pragma solidity ^0.4.0;
contract StringConvert{
function test() returns (bytes3){
bytes3 a = "123";
//bytes3 b = "1234"; //Error: Type literal_string "1234" is not implicitly convertible to expected type bytes3. return a;
}
}
字符串字面量支持转义字符,好比\n,\xNN,\uNNNN。其中\xNN表式16进制值,最终录入合适的字节。而\uNNNN表示Unicode码点值,最终会转换为UTF8的序列。
定点数
小数字面量
支持的运算符
数字字面量
大多数含小数的十进制,均不可被二进制准确表达,好比5.3743的类型多是ufixed8*248。若是你想使用这样的值,须要明确指定精度x + ufixed(5.3743),不然会报类型转换错误。
字面量截断
字面量转换
pragma solidity ^0.4.0;
contract IntegerLiteralConvert{
function literalTest(){
uint128 a = 1;
//uint128 b = 2.5 + a + 0.5;
//Error: Operator + not compatible with types rational_const 5/2 and uint128
}
}
定长字节数组
bytes1, ... ,bytes32,容许值以步长1递增。byte默认表示byte1。
运算符
<=,<,==,!=,>=,>,返回值为bool类型。
&,|,^(异或),~非
成员变量
.length表示这个字节数组的长度(只读)。
动态大小的字节数组
bytes: 动态长度的字节数组,参见数组(Arrays)。非值类型1。
string: 动态长度的UTF-8编码的字符类型,参见数组(Arrays)。非值类型[valueType]。
习惯说: bytes用来存储任意长度字节数据 若是长度确认 尽可能使用byte1到byte32之间的一个
以太坊地址的长度,大小20个字节,160位,因此能够用一个uint160编码。
地址是全部合约的基础。 全部的合约都会继承地址对象
地址类型成员
属性:balance
函数:send(),call(),delegatecall(),callcode()。
字面量
39-41位长的没有经过合法检查 会提出警告
balance
核心点是添加一个双引号
this
返回当前合约
send
像某个地址发钱 单位是WEI
充值合约
pragma solidity ^0.4.0;
//请注意这个仅是Demo,请不要用到正式环境
contract PayTest {
//获得当前合约的余额
function getBalance() returns (uint) {
return this.balance;//0
}
//向当前合约存款 function deposit() payable returns(address addr, uint amount, bool success){ //msg.sender 全局变量,调用合约的发起方 //msg.value 全局变量,调用合约的发起方转发的货币量,以wei为单位。 //send() 执行的结果 return (msg.sender, msg.value, this.send(msg.value)); }
}
这个合约实现的是充值。this.send(msg.value)意指向合约自身发送msg.value量的以太币。msg.value是合约调用方附带的以太币。
深度不超过1024 若是gas不够执行失败 使用这个方法检查成功与否 执行失败 全部交易回撤
call(),delegatecall(),callcode()都是底层的消息传递调用,最好仅在万不得已才进行使用,由于他们破坏了Solidity的类型安全。
他们的区别在于call能够调用支持的abi协议 call方法返回一个bool值 delegatecall仅仅执行另外一个工具箱 callcode访问权限收到限制
!逻辑非
&& 逻辑与
|| 逻辑或
== 等于
!= 不等于
Solidity是一个静态类型的语言,因此编译时需明确指定变量的类型. solidity提供了一些基本类型来组合复杂类型
值类型(Value Type)
值类型包含
布尔(Booleans)
整型(Integer)
地址(Address)
定长字节数组(fixed byte arrays)
有理数和整型(Rational and Integer Literals,String literals)
枚举类型(Enums)
函数(Function Types)
不定长字节数组(bytes)
字符串(string)
数组(Array)
结构体(Struts)
合约相似面向对象语言中的类。
支持继承
状态变量(State Variables),函数(Functions),函数修饰符(Function Modifiers),事件(Events),结构类型(Structs Types)和枚举类型(Enum Types)。