follow us on twitter . like us on facebook . follow us on instagram . subscribe to our youtube channel . announcements on telegram channel . ask urgent question ONLY . Subscribe to our reddit . Altcoins Talks Shop Shop


This is an Ad. Advertised sites are not endorsement by our Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction. Advertise Here Ads bidding Bidding Open

Author Topic: 深入浅出谈谈智能合约:编译、创建和部署合约  (Read 521 times)

Offline billy.ryoko

  • Legendary
  • *
  • Activity: 1506
  • points:
    6024
  • Karma: 2
  • Trade Count: (0)
  • Referrals: 1
  • Last Active: April 06, 2021, 04:53:54 PM
    • View Profile

  • Total Badges: 19
    Badges: (View All)
    10 Posts First Post Fifth year Anniversary
什么是合约?
 

合约是代码(它的功能)和数据(它的状态)的集合,存在于以太坊区块链的特定地址。 合约账户能够在彼此之间传递信息,进行图灵完备的运算。合约依靠被称作以太坊虚拟机(EVM) 字节代码(以太坊特有的二进制格式)上的区块链运行。

合约很典型地用诸如Solidity等高级语言写成,然后编译成字节代码上传到区块链上。

另请参阅:
也存在其他语言, 尤其是Serpent和LLL,在此文本的以太坊高级语言章节会进一步阐述。去中心化应用开发资源列出了综合的开发环境,帮助你用这些语言开发的开发者工具,提供测试,和部署支持等功能。

 

以太坊高级语言
合约依靠被称作以太坊虚拟机(EVM) 字节代码(以太坊特有的二进制格式)上的区块链运行。然而,合约很典型地用诸如Solidity等高级语言写成,然后用以太坊虚拟机编译器编译成字节代码上传到区块链。

下面是开发者可以用来为以太坊写智能合约的高级语言。

Solidity
Solidity是和JavaScript相似的语言,你可以用它来开发合约并编译成以太坊虚拟机字节代码。

它目前是以太坊最受欢迎的语言。

Solidity文本 – Solidity是以太坊的旗舰高级语言,用于写合约。
Solidity在线实时编译器
标准合约API
有用的去中心化模式 – 用于去中心化应用开发的代码片段。
Serpent
Serpent是和Python类似的语言,可以用于开发合约编译成以太坊虚拟机字节代码。它力求简洁, 将低级语言在效率方面的优点和编程风格的操作简易相结合,同时合约编程增加了独特的领域特定功能。Serpent用LLL编译。

以太坊维基百科上的Serpent
Serpent以太坊虚拟机编译器
LLL
Lisp Like Language (LLL)是和Assembly类似的低级语言。它追求极简;本质上只是直接对以太坊虚拟机的一点包装。

GitHub上的LIBLLL
LLL实例
Mutan (弃用)
Mutan是个静态类型,由Jeffrey Wilcke 开发设计的C类语言。它已经不再受到维护。

 

写合约
 

没有Hello World程序,语言就不完整。Solidity在以太坊环境内操作,没有明显的“输出”字符串的方式。我们能做的最接近的事就是用日志记录事件来把字符串放进区块链:

1
2
3
4
contract HelloWorld {
  event Print(string out);
  function() { Print("Hello, World!"); }
}
每次执行时,这个合约都会在区块链创建一个日志入口,印着“Hello,World!”参数。

另请参阅:
Solidity docs里有更多写Solidity代码的示例和指导。

 

编译合约
 

solidity合约的编译可以通过很多机制完成。

通过命令行使用solc编译器。
在geth或eth提供的javascript控制台使用web3.eth.compile.solidity (这仍然需要安装solc 编译器)。
在线Solidity实时编译器。
建立solidity合约的Meteor dapp Cosmo。
Mix IDE。
以太坊钱包。
注意:关于solc和编译Solidity合约代码的更多信息可在此查看。

在geth设置solidity编译器
如果你启动了geth节点,就可以查看哪个编译器可用。

1
2
> web3.eth.getCompilers();
["lll", "solidity", "serpent"]
这一指令会返回到显示当前哪个编译器可用的字符串。

注意:solc编译器和cpp- ethereum一起安装。或者,你可以自己创建。

如果你的solc可执行文件不在标准位置,可以用—solc标志为solc可执行文件指定一个定制路线。

1
$ geth --solc /usr/local/bin/solc
或者你可以通过控制台在执行期间设置这个选项:

1
2
3
4
> admin.setSolc("/usr/local/bin/solc")
solc, the solidity compiler commandline interface
Version: 0.2.2-02bb315d/.-Darwin/appleclang/JIT linked to libethereum-1.2.0-8007cef0/.-Darwin/appleclang/JIT
path: /usr/local/bin/solc
编译一个简单合约
让我们编译一个简单的合约源:

1
> source = "contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"
这个合约提供了一个单一方法multiply,它和一个正整数a调用并返回到a * 7 。

你准备在geth JS控制台用eth.compile.solidity()编译solidity代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
> contract = eth.compile.solidity(source).test
{
  code: '605280600c6000396000f3006000357c01000000000000000000000000000000000000000000000000000000
  info: {
    language: 'Solidity',
    languageVersion: '0',
    compilerVersion: '0.9.13',
    abiDefinition: [{
      constant: false,
      inputs: [{
name: 'a',
        type: 'uint256'
      } ],
      name: 'multiply',
      outputs: [{
name: 'd',
        type: 'uint256'
      } ],
      type: 'function'
    } ],
    userDoc: {
      methods: {
      }
    },
    developerDoc: {
methods: {
} },
    source: 'contract test { function multiply(uint a) returns(uint d) { return a * 7; } }'
  }
}
注意:编译器通过RPC因此也能通过web3.js,对浏览器内任何通过RPC/IPC连接到geth的Ðapp可用。

下面的例子会向你展示如何通过JSON-RPC接合geth来使用编译器。

1
2
$ geth --datadir ~/eth/ --loglevel 6 --logtostderr=true --rpc --rpcport 8100 --rpccorsdomain ' * ' --mine console 2>> ~/eth/eth.log
$ curl -X POST --data '{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["contract test {
单源编译器输出会给出你合约对象,每个都代表一个单独的合约。eth.compile.solidity 的实际返还值是合约名字到合约对象的映射。由于合约名字是test,eth.compile.solidity(source).test会给出包含下列领域的测试合约对:

Code 编译的以太坊虚拟机字节代码
Info 从编译器输出的额外元数据
Source 源代码
Language 合约语言 (Solidity,Serpent,LLL)
LanguageVersion 合约语言版本
compilerVersion 用于编译这个合约的solidity编译器版本。
abiDefinition 应用的二进制界面定义
userDoc 用户的NatSpec Doc。
developerDoc 开发者的NatSpec Doc。
编译器输出的直接结构化(到code和info)反映了两种非常不同的部署路径。编译的以太坊虚拟机代码和一个合约创建交易被发送到区块,剩下的(info)在理想状态下会存活在去中心化云上,公开验证的元数据则执行区块链上的代码。

如果你的源包含多个合约,输出会包括每个合约一个入口,对应的合约信息对象可以用作为属性名称的合约名字检索到。你可以通过检测当前的GlobalRegistrar代码来试一下:

1
contracts = eth.compile.solidity(globalRegistrarSrc)
创建和部署合约
开始这一章节之前,确保你有解锁的账户和一些资金。 你现在会在区块链上创建一个合约,方法是用上一章节的以太坊虚拟机代码作为数据给空地址发送交易。

注意:用在线Solidity实时编译器或Mix IDE程序会更容易完成。

1
2
3
4
var primaryAddress = eth.accounts[0]
var abi = [{ constant: false, inputs: [{ name: 'a', type: 'uint256' } ]
var MyContract = eth.contract(abi)
var contract = MyContract.new(arg1, arg2, ..., {from: primaryAddress, data: evmByteCodeFromPrevio
所有的二进制数据都以十六进制的格式序列化。十六进制字符串总会有一个十六进制前缀0x。

注意:注意arg1, arg2, …是合约构造函数参数,以备它要接受参数。如果合约不需要构造函数参数,就可以忽略这些参数。

值得指出的是,这一步骤需要你支付执行。一旦交易成功进入到区块,你的账户余额(你作为发送方放在from领域)会根据以太坊虚拟机的gas规则被扣减。一段时间以后,你的交易会在一个区块中出现,确认它带来的状态是共识。你的合约现在存在于区块链上。 以不同步的方式做同样的事看起来是这样:

1
2
3
MyContract.new([arg1, arg2, ...,]{from: primaryAccount, data: evmCode}, function(err, contract) { if (!err && contract.address)
    console.log(contract.address);
});
与合约交互
与合约交互典型的做法是用诸如eth.contract()功能的抽象层,它会返回到javascript对象,和所有可用的合约功能一起,作为可调用的javascript功能。 描述合约可用功能的标准方式是ABI定义。这个对象是一个字符串,它描述了调用签名和每个可用合约功能的返回值。

1
2
var Multiply7 = eth.contract(contract.info.abiDefinition);
var myMultiply7 = Multiply7.at(address);
现在ABI中具体说明的所有功能调用都在合约实例中可用。你可以用两种方法中的一种来调用这些合约实例上的方法。

1
2
3
4
> myMultiply7.multiply.sendTransaction(3, {from: address})
"0x12345"
> myMultiply7.multiply.call(3)
21
当用sendTransaction被调用的时候,功能调用通过发送交易来执行。需要花费以太币来发送,调用会永久记录在区块链上。用这种方式进行的调用返回值是交易散表。

当用call被调用的时候,功能在以太坊虚拟机被本地执行,功能返回值和功能一起返回。用这种方式进行的调用不会记录在区块链上,因此也不会改变合约内部状态。这种调用方式被称为恒定功能调用。以这种方式进行的调用不花费以太币。

如果你只对返回值感兴趣,那么你应该用call 。如果你只关心合约状态的副作用,就应该用sendTransaction。

在上面的例子中,不会产生副作用,因此sendTransaction只会烧gas,增加宇宙的熵。

合约元数据
在之前的章节,我们揭示了怎样在区块链上创建合约。现在我们来处理剩下的编译器输出,合约元数据或者说合约信息。 当与不是你创建的合约交互时,你可能会想要文档或者查看源代码。合约作者被鼓励提供这样的可见信息,他们可以在区块链上登记或者借助第三方服务,比如说EtherChain。管理员API为所有选择登记的合约提供便利的方法来获取这个捆绑。

1
2
3
4
// get the contract info for contract address to do manual verification
var info = admin.getContractInfo(address) // lookup, fetch, decode
var source = info.source;
var abiDef = info.abiDefinition
这项工作的潜在机制是:

合约信息被可以公开访问的URI上传到可辨认的地方
任何人都可以只知道合约地址就找到是什么URI
仅通过2个步骤的区块链注册就可以实现这些要求。第一步是在被称作HashReg的合约中用内容散表注册合约代码(散表)。第二步是在UrlHint合约用内容散表注册一个url。这些注册合约是Frontier版本的一部分,已经参与到Homestead中。

要知道合约地址来查询url,获取实际合约元数据信息包,使用这一机制就足够了。

如果你是个尽职的合约创建者,请遵循以下步骤:

将合约本身部署到区块链
获取合约信息json文件
将合约信息json文件部署到你选择的任意url
注册代码散表 –>内容散表 –> url
JS API通过提供助手把这个过程变得非常容易。 调用admin.register从合约中提取信息,在指定文件中写出json序列,运算文件的内容散表,最终将这个内容散表注册到合约代码散表。一旦将那个文件部署到任意url,你就能用admin.registerUrl来注册url 和你区块链上的内容散表(注意一旦固定的内容选址模式被用作文件商店,url-hint不再必要了。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
source = "contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"
// compile with solc
contract = eth.compile.solidity(source).test
// create contract object
var MyContract = eth.contract(contract.info.abiDefinition)
// extracts info from contract, save the json serialisation in the given file,
contenthash = admin.saveInfo(contract.info, "~/dapps/shared/contracts/test/info.json")

// send off the contract to the blockchain
MyContract.new({from: primaryAccount, data: contract.code}, function(error, contract){
  if(!error && contract.address) {
    // calculates the content hash and registers it with the code hash in `HashReg`
    // it uses address to send the transaction.
    // returns the content hash that we use to register a url
    admin.register(primaryAccount, contract.address, contenthash)
    // here you deploy ~/dapps/shared/contracts/test/info.json to a url
    admin.registerUrl(primaryAccount, hash, url)
  }
});
测试合约和交易
你通常需要低级的测试策略,为交易和合约排除故障。这一章节介绍了一些你可以用到的排错工作和做法。为了测试合约和交易而不产生实际的后果,你最好在私有区块链上测试。这可以通过配置一个替代网络ID (选择一个特别的数字)和/或不能用的端点来实现。推荐做法是,为了测试你用一个替代数据目录和端口 ,这样就不会意外地和实时运行的节点冲突(假定用默认运行。在虚拟机排错模式开启geth,推荐性能分析和最高的日志冗余级别 :

1
geth --datadir ~/dapps/testing/00/ --port 30310 --rpcport 8110 --networkid 4567890 --nodiscover -
提交交易之前,你需要创建私有测试链。参阅测试网络。

1
2
3
4
5
6
// create account. will prompt for password
personal.newAccount();
// name your primary account, will often use it
primary = eth.accounts[0];
// check your balance (denominated in ether)
balance = web3.fromWei(eth.getBalance(primary), "ether");
1
2
3
4
5
6
7
8
9
10
// assume an existing unlocked primary account
primary = eth.accounts[0];
// mine 10 blocks to generate ether
// starting miner
miner.start(4);
// sleep for 10 blocks (this can take quite some time).
admin.sleepBlocks(10);
// then stop mining (just not to burn heat in vain)
miner.stop();
balance = web3.fromWei(eth.getBalance(primary), "ether");
创建交易之后,你可以用下面的命令来强制运行:

1
2
3
miner.start(1);
admin.sleepBlocks(1);
miner.stop();
你可以用以下命令查看即将发生的交易:

1
2
3
4
5
6
// shows transaction pool
txpool.status
// number of pending txs
eth.getBlockTransactionCount("pending");
// print all pending txs
eth.getBlock("pending", true).transactions
如果你提交合约创建交易,可以检查想要的代码是否实际上嵌入到当前的区块链:

1
2
3
4
txhash = eth.sendTansaction({from:primary, data: code})
//... mining
contractaddress = eth.getTransactionReceipt(txhash);
eth.getCode(contractaddress)
感谢朝夕团队Azure, Bob参与《Ethereum Homestead Documentation》的翻译和校验。

 

汪晓明

HPB芯链创始人,巴比特专栏作家。十余年金融大数据、区块链技术开发经验,曾参与创建银联大数据。主创区块链教学视频节目《明说》30多期,编写了《以太坊官网文档中文版》,并作为主要作者编写了《区块链开发指南》,在中国区块链社区以ID“蓝莲花”知名。

http://www.8btc.com/smart-contract-0804

Altcoins Talks - Cryptocurrency Forum


This is an Ad. Advertised sites are not endorsement by our Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction. Advertise Here Ads bidding Bidding Open


 

ETH & ERC20 Tokens Donations: 0x2143F7146F0AadC0F9d85ea98F23273Da0e002Ab
BNB & BEP20 Tokens Donations: 0xcbDAB774B5659cB905d4db5487F9e2057b96147F
BTC Donations: bc1qjf99wr3dz9jn9fr43q28x0r50zeyxewcq8swng
BTC Tips for Moderators: 1Pz1S3d4Aiq7QE4m3MmuoUPEvKaAYbZRoG
Powered by SMFPacks Social Login Mod