一個智能合約是一套以數字形式定義的承諾(promises) ,包括合約參與方能夠在上面執行這些承諾的協議。一個合約由一組代碼(合約的函數)和數據(合約的狀態)組成,而且運行在以太坊虛擬機上.
以太坊虛擬機(EVM)使用了256比特長度的機器碼,是一種基於堆棧的虛擬機,用於執行以太坊智能合約 。因爲EVM是針對以太坊體系設計的,所以使用了以太坊帳戶模型(Account Model)進行價值傳輸。html
讀取交易數據。 讀取或寫入合約本身的存儲空間。 讀取環境變量(塊高,哈希值,gas) 向另外一個合約發送一個「內部交易」。
Solidity是一種智能合約高級語言,運行在Ethereum虛擬機(EVM)之上。java
solidity 語言特色node
它的語法接近於Javascript,是一種面向對象的語言。但做爲一種真正意義上運行在網絡上的去中心合約,它有不少的不一樣點:git
合約開發步驟:github
1. 寫合約 2. 編譯合約 3. 部署合約 4. 測試合約 5. 生成java文件
說明:WeBase
幫助用戶開發、測試和生成對應的Java類,用戶獲取java類,能夠直接進行業務開發,加快開發進度和效率。web
pragma solidity ^0.4.24; contract HelloWorld { string name; function HelloWorld() { name = "Hello, World!"; } function get()constant returns(string) { return name; } function set(string n) { name = n; } }
下載應用腳手架spring
$ git clone https://github.com/FISCO-BCOS/spring-boot-starter.git
[更多工具和例子]()編程
address:以太坊地址的長度,大小20個字節,160位,因此能夠用一個uint160編碼。地址是全部合約的基礎,全部的合約都會繼承地址對象,也能夠隨時將一個地址串,獲得對應的代碼進行調用。合約的地址是基於帳號隨機數和交易數據的哈希計算出來的json
ABI:是以太坊的一種合約間調用時或消息發送時的一個消息格式。就是定義操做函數簽名,參數編碼,返回結果編碼等。
交易:以太坊中「交易」是指存儲從外部帳戶發出的消息的簽名數據包。
簡單理解是:只要對區塊鏈進行寫操做,必定會發生交易。
交易回執:發生交易後的返回值
版本聲明
pragma solidity ^0.4.24;
狀態變量(State Variables)
string name;
詳細說明見下文
函數(Functions)
function get()constant returns(string) { return name; } function set(string n) { name = n; }
事件(Events)
//事件的聲明 event AddMsg(address indexed sender, bytes32 msg); //事件的使用 function setData(int256 x) public { storedData = x; AddMsg(msg.sender, "in the set() method"); }
結構類型(Structs Types)
contract Contract { struct Data { uint deadline; uint amount; } Data data; function set(uint id, uint deadline, uint amount) { data.deadline = deadline; data.amount = amount; } }
函數修飾符(Function Modifiers)
相似於hook
modifier only_with_at_least(int x) { if (x >= 5) { x = x+10; _; } }
面向條件的編程(COP)是面向合約編程的一個子域,做爲一種面向函數和命令式編程的混合模式。COP解決了這個問題,經過須要程序員顯示地枚舉全部的條件。邏輯變得扁平,沒有條件的狀態變化。條件片斷能夠被正確的文檔化,複用,能夠根據需求和實現來推斷。重要的是,COP在編程中把預先條件看成爲一等公民。這樣的模式規範能保證合約的安全。
例子:
contract Token { // The balance of everyone mapping (address => uint) public balances; // Constructor - we're a millionaire! function Token() { balances[msg.sender] = 1000000; } // Transfer `_amount` tokens of ours to `_dest`. function transfer(uint _amount, address _dest) { balances[msg.sender] -= _amount; balances[_dest] += _amount; } }
改進後:
function transfer(uint _amount, address _dest) { if (balances[msg.sender] < _amount) return; balances[msg.sender] -= _amount; balances[_dest] += _amount; }
COP的風格
modifier only_with_at_least(uint x) { if (balances[msg.sender] >= x) _; } function transfer(uint _amount, address _dest) only_with_at_least(_amount) { balances[msg.sender] -= _amount; balances[_dest] += _amount; }
!邏輯非&& 邏輯與|| 邏輯或== 等於!= 不等於
更多詳情見官方API
變量的存儲位置屬性。有三種類型,memory,storage和calldata。
Storage - 狀態變量的存儲模型
大小固定的變量(除了映射,變長數組之外的全部類型)在存儲(storage)中是依次連續從位置0開始排列的。若是多個變量佔用的大小少於32字節,會盡量的打包到單個storage槽位裏,具體規則以下:
爲了方便EVM進行優化,嘗試有意識排序storage的變量和結構體的成員,從而讓他們能打包得更緊密。好比,按這樣的順序定義,uint128, uint128, uint256,而不是uint128, uint256, uint128。由於後一種會佔用三個槽位。
Memory - 內存變量的佈局(Layout in Memory)
Solidity預留了3個32字節大小的槽位:
0-64:哈希方法的暫存空間(scratch space)
64-96:當前已分配內存大小(也稱空閒內存指針(free memory pointer))
暫存空間可在語句之間使用(如在內聯編譯時使用)
Solidity老是在空閒內存指針所在位置建立一個新對象,且對應的內存永遠不會被釋放(也許將來會改變這種作法)。
有一些在Solidity中的操做須要超過64字節的臨時空間,這樣就會超過預留的暫存空間。他們就將會分配到空閒內存指針所在的地方,但因爲他們自身的特色,生命週期相對較短,且指針自己不能更新,內存也許會,也許不會被清零(zerod out)。所以,你們不該該認爲空閒的內存必定已是清零(zeroed out)的。
例子
以太坊地址的長度,大小20個字節,160位,因此能夠用一個uint160編碼。地址是全部合約的基礎,全部的合約都會繼承地址對象,也能夠隨時將一個地址串,獲得對應的代碼進行調用
event AddMsg(address indexed sender, bytes32 msg);
事件是使用EVM日誌內置功能的方便工具,在DAPP的接口中,它能夠反過來調用Javascript的監聽事件的回調。
var event = instance.AddMsg({}, function(error, result) { if (!error) { var msg = "AddMsg: " + utils.hex2a(result.args.msg) + " from " console.log(msg); return; } else { console.log('it error') } });
數組是定長或者是變長數組。有length屬性,表示當前的數組長度。
通常使用定長的 bytes1~bytes32。在知道字符串長度的狀況下,指定長度時,更加節省空間。
new uint[] memory a = new uint[](7);
例子
pragma solidity ^0.4.0; contract SimpleStartDemo{ uint[] stateVar; function f(){ //定義一個變長數組 uint[] memory memVar; //不能在使用new初始化之前使用 //VM Exception: invalid opcode //memVar [0] = 100; //經過new初始化一個memory的變長數組 memVar = new uint[](2); //不能在使用new初始化之前使用 //VM Exception: invalid opcode //stateVar[0] = 1; //經過new初始化一個storage的變長數組 stateVar = new uint[](2); stateVar[0] = 1; } }
length屬性
storage變長數組是能夠修改length
memory變長數組是不能夠修改length
push方法
storage變長數組可使用push方法
bytes可使用push方法
pragma solidity ^0.4.2; contract SimpleStartDemo { uint[] stateVar; function f() returns (uint){ //在元素初始化前使用 stateVar.push(1); stateVar = new uint[](1); stateVar[0] = 0; //自動擴充長度 uint pusharr = stateVar.push(1); uint len = stateVar.length; //不支持memory //Member "push" is not available in uint256[] memory outside of storage. //uint[] memory memVar = new uint[](1); //memVar.push(1); return len; } }
下標:和其餘語言相似
Memory數組是不能修改修改數組大小的屬性
例子
pragma solidity ^0.4.2;
contract SimpleStartDemo {
function f() { //建立一個memory的數組 uint[] memory a = new uint[](7); //不能修改長度 //Error: Expression has to be an lvalue. //a.length = 100; } //storage uint[] b; function g(){ b = new uint[](7); //能夠修改storage的數組 b.length = 10; b[9] = 100; }
}
EVM的限制
因爲EVM的限制,不能經過外部函數直接返回動態數組和多維數組
//Data層數據 struct Rate { int key1; int unit; uint[3] exDataArr; bytes32[3] exDataStr; } mapping(int =>Rate) Rates; function getRate(int key1) public constant returns(int,uint[3],bytes32[3]) { uint[3] memory exDataInt = Rates[key1].exDataArr; bytes32[3] memory exDataStr = Rates[key1].exDataStr; return (Rates[key1].unit,exDataInt,exDataStr); }
業務場景
function (<parameter types>) {internal(默認)|external} constant [returns (<return types>)]
pragma solidity ^0.4.5; contract FuntionTest{ function internalFunc() internal{} function externalFunc() external{} function callFunc(){ //直接使用內部的方式調用 internalFunc(); //不能在內部調用一個外部函數,會報編譯錯誤。 //Error: Undeclared identifier. //externalFunc(); //不能經過`external`的方式調用一個`internal` //Member "internalFunc" not found or not visible after argument-dependent lookup in contract FuntionTest //this.internalFunc(); //使用`this`以`external`的方式調用一個外部函數 this.externalFunc(); } } contract FunctionTest1{ function externalCall(FuntionTest ft){ //調用另外一個合約的外部函數 ft.externalFunc(); //不能調用另外一個合約的內部函數 //Error: Member "internalFunc" not found or not visible after argument-dependent lookup in contract FuntionTest //ft.internalFunc(); } }
訪問函數有外部(external)可見性。若是經過內部(internal)的方式訪問,好比直接訪問,你能夠直接把它當一個變量進行使用,但若是使用外部(external)的方式來訪問,如經過this.,那麼它必須經過函數的方式來調用。
pragma solidity ^0.4.2; contract SimpleStartDemo { uint public c = 10; function accessInternal() returns (uint){ return c; } function accessExternal() returns (uint){ return this.c(); } }
修改器(Modifiers)能夠用來輕易的改變一個函數的行爲。好比用於在函數執行前檢查某種前置條件。修改器是一種合約屬性,可被繼承,同時還可被派生的合約重寫(override)
pragma solidity ^0.4.2; contract SimpleStartDemo { int256 storedData; event AddMsg(address indexed sender, bytes32 msg); modifier only_with_at_least(int x) { if (x >= 5) { x = x+10; _; } } function setData(int256 x) public only_with_at_least(x){ storedData = x; AddMsg(msg.sender, "[in the set() method]"); } }
函數也可被聲明爲常量,這類函數將承諾本身不修改區塊鏈上任何狀態。
通常從鏈上獲取數據時,get函數都會加上constant
Solidity經過複製包括多態的代碼來支持多重繼承。
父類
pragma solidity ^0.4.4; contract Meta { string public name; string public abi; address metaAddress; function Meta(string n,string a){ name=n; abi=a; } function getMeta()public constant returns(string,string,address){ return (name,abi,metaAddress); } function setMetaAddress(address meta) public { metaAddress=meta; } }
子類
pragma solidity ^0.4.4; import "Meta.sol"; contract Demo is Meta{ bytes32 public orgID; function Demo (string n,string abi,bytes32 id) Meta(n,abi) { orgID = id; } }
基於EVM的限制,不能經過外部函數返回動態的內容
合約的架構分兩層數據合約和邏輯合約,方便後期合約的升級。更多詳情,請參見淺談以太坊智能合約的設計模式與升級方法 。