以太坊开发文档10 - 智能合约

写一份合同

以Ethereum特定的二进制格式(以太坊虚拟机(= EVM)字节码)签署合同。但是,契约通常是用一些高级语言编写的,如可靠性,然后编译成字节代码,上传到区块链中。

请注意,其他语言也存在,特别是LLL传统的穆坦(早期类似C语言)已不再正式维护。

语言资源

密实度

文档和教程
例子
编译器

Contract / Dapp开发环境和框架


编制合同

以Ethereum特定的二进制格式(以太坊虚拟机(= EVM)字节码)签署合同。但是,契约通常是用一些高级语言编写的,如可靠性,然后编译成字节代码,上传到区块链中。

对于前沿版本,geth通过系统调用来支持solidity 编译,Christian R.和Lefteris K. solc的命令行编译。您可以尝试使用Solidity实时编译器(由Christian R)或CosmoMix 编译

如果启动geth节点,则可以检查solidity编译器是否可用。这是什么情况,如果不是:

> eth.compile.solidity("")
eth_compileSolidity method not available: solc (solidity compiler) not found
    at InvalidResponse (<anonymous>:-57465:-25)
    at send (<anonymous>:-115373:-25)
    at solidity (<anonymous>:-104109:-25)
    at <anonymous>:1:1

在找到安装方法之后solc,请确保它在路径中。如果eth.getCompilers()仍然没有找到它(返回一个空的数组),你可以solc使用th solc标志在命令行上设置一个可执行文件的自定义路径

geth --datadir ~/frontier/00 --solc /usr/local/bin/solc --natspec

您也可以通过控制台在运行时设置此选项:

> admin.setSolc("/usr/local/bin/solc")
solc v0.9.32
Solidity Compiler: /usr/local/bin/solc
Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com> (c) 2014-2015
true

让我们拿这个简单的合同来源:

> source = "contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"

这个合同提供了一个一元方法:用一个正整数来调用a,它返回a * 7

您已准备好geth使用eth.compile.solidity以下代码在JS控制台中编译solidity代码

> contract = eth.compile.solidity(source).test
{
  code: '605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056',
  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.jsgeth通过RPC / IPC 连接到任何浏览器中的应用程序

以下示例显示了如何geth通过JSON-RPC进行接口以使用编译器。

./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 { function multiply(uint a) returns(uint d) { return a * 7; } }"],"id":1}' http://127.0.0.1:8100

一个源的编译器输出将为您提供每个代表单个合同的合同对象。实际返回值eth.compile.solidity是合同名称 - 合同对象对的映射。由于我们的合同的名称是testeth.compile.solidity(source).test将给你的测试合同的合同对象包含以下字段:

编译器输出的直接结构(进入codeinfo)反映了两种完全不同的部署路径编译后的EVM代码通过合同创建事务处理发送到区块链,而其余的(info)理想情况下将作为可公开验证的元数据补充区块链上的代码。

如果您的来源包含多个合同,则输出将包含每个联系人的条目,则可以使用合同名称作为属性名称检索相应的合同信息对象。您可以通过检查最新的GlobalRegistrar代码来尝试此操作:

contracts = eth.compile.solidity(globalRegistrarSrc)

创建和部署合同

既然您已经拥有了一个未锁定的账户以及一些基金,您可以通过发送一个事务到evm代码作为数据的空白地址来在区块链上创建一个合约很简单,呃?

primaryAddress = eth.accounts[0]
MyContract = eth.contract(abi);
contact = MyContract.new(arg1, arg2, ...,{from: primaryAddress, data: evmCode})

所有二进制数据都以十六进制格式进行序列化。十六进制字符串始终有一个十六进制的前缀0x

请注意,arg1, arg2, ...如果它接受任何合约构造函数的参数。

另外请注意,这一步需要您付费执行。from一旦你的交易进入一个区块,你账户上的余额(你作为发件人放在现场)将根据虚拟机的天然气规则减少。稍后更多。过了一段时间,你的交易应该包含在一个块中,以确认它所带来的状态是一个共识。您的合同现在位于区块链上。

异步的做法是这样的:

MyContract.new([arg1, arg2, ...,]{from: primaryAccount, data: evmCode}, function(err, contract) {
  if (!err && contract.address)
    console.log(contract.address); 
});

GAS和交易成本

那么你是如何支付这一切的?在交易对象之下,交易指定了一个气体限制和一个气体价格,两者都可以在交易对象中直接指定。

气体限制是为了保护您免受多余的代码运行,直到您的资金枯竭。的产品gasPrice,并gas表示你愿意支付执行事务所卫的最高金额。你指定什么gasPrice是由矿工使用排名交易列入区块链。这是一个单位天然气的价格,其中虚拟机操作价格。

运行合同所产生的天然气支出将由您在交易中指定的价格在您的账户中购买gasPrice如果您没有足够的资源来完成代码的运行,则处理将中止,所有中间状态更改将回滚到事务前快照。毕竟使用到执行停止点的气体,所以你帐户的乙醚余额将会减少。这些参数可以对交易对象的字段进行调整gasgasPricevalue字段用于正常账户之间的以太转账交易。换句话说,在任何两个账户之间可以有正常的(即外部控制的)或合同。如果您的合同资金耗尽,您应该看到资金不足的错误。

为了测试和玩契约,你可以使用测试网络,或者建立一个私有节点(或集群),这个节点可能与其他所有节点隔离。如果你那么我的,你可以确保你的交易将被包含在下一个块。您可以看到待处理的交易:

eth.getBlock("pending", true).transactions

您可以通过数字(高度)或哈希来检索块:

genesis = eth.getBlock(0)
eth.getBlock(genesis.hash).hash == genesis.hash
true

使用eth.blockNumber获得当前区块链高度和“最新”魔术参数来访问当前的头(最新的块)。

currentHeight = eth.blockNumber()
eth.getBlock("latest").hash == eth.getBlock(eth.blockNumber).hash
true

与合同交互

eth.contract可以用来定义一个契约,它将遵守ABI定义中描述的契约接口

var Multiply7 = eth.contract(contract.info.abiDefinition);
var myMultiply7 = Multiply7.at(address);

现在,在abi中指定的所有函数调用都可以在合同实例上使用。你可以在合同实例和链上调用这些方法sendTransaction(3, {from: address})或者调用call(3)它们。两者之间的区别在于call,在您的计算机上本地执行“空运行”,而sendTransaction实际上将您的交易提交给区块链,其执行结果最终将成为全球共识的一部分。换句话说,call如果你只对回报价值感兴趣,而使用,sendTransaction如果你只关心合同状态的“副作用”。

在上面的例子中,没有副作用,因此sendTransaction只能燃烧气体,增加宇宙的熵。所有“有用的”功能通过call以下方式公开

myMultiply7.multiply.call(6)
42

现在假设这个合同不是你的,你需要文档或者查看源代码。这可以通过提供合同信息包并将其注册到区块链中来实现。所述adminAPI提供方便的方法来为选择了注册任何合同取该束。要了解它是如何工作的,请阅读合同元数据或阅读本文档的合同信息部署部分。

// 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

合同信息(元数据)

在前面的章节中,我们解释了如何在区块链上创建合约。现在我们处理编译器输出的其余部分,即合同元数据或合同信息。这个想法是

  • 合同信息被上传到url可公开访问的地方
  • 任何人都可以找出什么url是只知道合同地址

通过使用2步区块链注册表,这些要求非常简单。第一步是在称为合同的合同中注册合同代码(散列)和内容散列HashReg第二步在UrlHint合约中注册一个带有内容哈希的URL 这些简单的注册合同将成为前沿命题的一部分。

通过使用这种方案,知道合同的地址来查找URL并获取实际的合同元数据信息包就足够了。继续阅读,了解为什么这是好的。

所以如果你是一个认真的合同创造者,其步骤如下:

  1. 将合约本身部署到区块链
  2. 获取合同信息json文件。
  3. 将合同信息json文件部署到您选择的任何网址
  4. 注册codehash - >内容哈希 - >网址

JS API通过提供帮助程序使这个过程非常简单。调用admin.register从契约中提取信息,在给定的文件中写出它的json序列化,计算文件的内容哈希,并最终将这个内容哈希注册到契约的代码哈希。将该文件部署到任何网址之后,您就可以使用admin.registerUrl在区块链中注册带有内容哈希值的网址。(请注意,如果将固定的内容寻址模型用作文档存储,则不再需要url-hint。)

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)
  }
});

NatSpec

本节将进一步阐述如何使用协议NatSpec构建合同和事务。Solidity实现智能注释doxigen样式,然后可用于生成代码的各种外观元文档。一个这样的用例是生成用于交易确认的自定义消息,客户可以提示用户。

所以我们现在multiply7用合适的注释来扩展合同,指定一个自定义的确认信息(通知)。

contract test {
   /// @notice Will multiply `a` by 7.
   function multiply(uint a) returns(uint d) {
       return a * 7;
   }
}

该评论具有在交易确认消息被呈现给用户时将被评估的反引号之间的表达。然后,参照方法调用参数的变量根据用户(或用户的dapp)发送的实际交易数据进行实例化。NatSpec对确认通知的支持完全在gethNatSpec依靠abi定义以及userDoc组件来生成适当的确认。因此,为了访问该合同,合同需要如上所述注册其合同信息。

让我们看一个完整的例子。作为一个非常认真的智能合同开发者,您首先根据上面建议的步骤创建您的合同并进行部署:

source = "contract test {
   /// @notice Will multiply `a` by 7.
   function multiply(uint a) returns(uint d) {
       return a * 7;
   }
}"
contract = eth.compile.solidity(source).test
MyContract = eth.contract(contract.info.abiDefinition)
contenthash = admin.saveInfo(contract.info, "~/dapps/shared/contracts/test/info.json")
MyContract.new({from: primary, data: contract.code}, function(error, contract){
  if(!error && contract.address) {
    admin.register(primary, contract.address, contenthash)
    // put it up on your favourite oldworld site:
    admin.registerUrl(contentHash, "http://dapphub.com/test/info.json")
  }
});

请注意,如果我们使用像swarm这样的内容寻址存储系统,第二步是不必要的,因为contenthash(确定性地转换为)内容本身的唯一地址。

只是简单地使用文件url方案(不完全是云,但会告诉你它是如何工作的),而不需要部署,这是一个痛苦的例子。

admin.registerUrl(contentHash, "file:///home/nirname/dapps/shared/contracts/test/info.json")

现在你已经成为一名开发者,所以交换席位,并假装你是一个正在向臭名昭着的multiply7合同发送交易的用户

您需要使用--natspec标志启动客户端,以启用智能确认和contractInfo提取。您也可以使用admin.startNatSpec()在控制台上进行设置admin.stopNatSpec()

geth --natspec --unlock primary console 2>> /tmp/eth.log

现在在控制台上输入:

// obtain the abi definition for your contract
var info = admin.getContractInfo(address)
var abiDef = info.abiDefinition
// instantiate a contract for transactions
var Multiply7 = eth.contract(abiDef);
var myMultiply7 = Multiply7.at(address);

现在尝试发送一个实际的交易:

> myMultiply7.multiply.sendTransaction(6, {from: eth.accounts[0]})
NatSpec: Will multiply 6 by 7. 
Confirm? [y/n] y
>

当这个交易被包含在一个幸运的矿工计算机上的某个地方时,6将被乘以7,结果被忽略。任务完成。

如果交易没有拿起,我们可以看到:

eth.pendingTransactions

这会累积所有已发送的交易,即使是被拒绝的交易,也不会被包含在当前的采矿块(交易状态)中。后者可以表示为:

eth.getBlock("pending", true).transactions()

如果您确定拒绝交易的指数,您可以使用修改的气体限制和天然气价格(两个可选参数)重新发送:

tx = eth.pendingTransactions[1]
eth.resend(tx, newGasPrice, newGasLimit)

测试合同和交易

通常您需要采取低级别的测试和调试合同和交易策略。本节介绍一些您可以使用的调试工具和实践。为了测试合同和交易而没有实际的后果,你最好在私人区块链上进行测试。这可以通过配置替代网络ID(选择唯一的整数)和/或禁用对等点来实现。建议您在测试时使用另一个数据目录和端口,这样您甚至不会意外地与您正在运行的节点发生冲突(假定使用默认值运行)。geth在虚拟机调试模式下启动配置文件和最高日志冗余级别推荐的:

geth --datadir ~/dapps/testing/00/ --port 30310 --rpcport 8110 --networkid 4567890 --nodiscover --maxpeers 0 --vmdebug --verbosity 6 --pprof --pprofport 6110 console 2>> ~/dapp/testint/00/00.log

在您提交任何交易之前,您需要在您的私人链上使用一些以太网,为此您需要一个帐户。请参阅采矿帐户部分

// create account. will prompt for password
personal.newAccount("mypassword");
// 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");
// assume an existing unlocked primary account
primary = eth.accounts[0];

// mine 10 blocks to generate ether 

// starting miner
miner.start(8);
// sleep for 10 blocks.
admin.sleepBlocks(10);
// then stop mining (just not to burn heat in vain)
miner.stop()  ;
balance = web3.fromWei(eth.getBalance(primary), "ether");

创建交易后,您可以使用以下几行强制处理它们:

miner.start(1);
admin.sleepBlocks(1);
miner.stop()  ;

你可以检查你的未决交易

// shows transaction pool
txpool.status
// number of pending txs
eth.getBlockTransactionCount("pending");
// print all pending txs
eth.getBlock("pending", true).transactions

如果您提交了合同创建交易,您可以检查所需的代码是否实际插入到当前区块链中:

txhash = eth.sendTansaction({from:primary, data: code})
//... mining
contractaddress = eth.getTransactionReceipt(txhash);
eth.getCode(contractaddress)

注册商服务

前沿链带有一些非常基本的底层服务,大部分是注册服务商。注册商由三个部分组成。

  • GlobalRegistrar将名称(字符串)关联到帐户(地址)。
  • HashReg将散列关联到散列(将任何对象映射到“内容”散列。
  • Url提示将内容散列关联到内容位置的提示。只有当内容存储不是内容寻址时才需要,否则内容散列已经是内容地址。如果使用它,从url获取的内容应该散列到内容哈希。为了检查内容的真实性,可以检查是否验证。

创建并部署GlobalRegistrar,HashReg和UrlHint

如果注册服务商合同在区块链中没有进行硬编码(它们尚未写入),则注册服务商需要在每个链上至少部署一次。

如果您位于主要的生命链上,全球主要注册商的地址在最新的客户端进行硬编码,因此您无需执行任何操作如果你想改变这个,或者你在一个私有链上,你至少需要部署这些合约一次:

primary = eth.accounts[0];

globalRegistrarAddr = admin.setGlobalRegistrar("", primary);
hashRegAddr = admin.setHashReg("", primary);
urlHintAddr = admin.setUrlHint("", primary);

你需要挖掘或等待,直到所有的TX都拿起。初始化新地址的注册商,并检查其他注册商的名称是否解析到正确的地址:

registrar = GlobalRegistrar.at(globalRegistrarAddr);
primary == registrar.owner("HashReg");
primary == registrar.owner("UrlHint");
hashRegAddr == registrar.addr("HashReg");
urlHintAddr registrar.addr("UrlHint");

和下面的返回正确的代码:

eth.getCode(registrar.address);
eth.getCode(registrar.addr("HashReg"));
eth.getCode(registrar.addr("UrlHint"));

从第二次起,在同一链上以及在其他节点上,只需使用GlobalRegistrars地址种子,剩下的就通过它处理。

primary = eth.accounts[0];
globalRegistrarAddr = "0x225178b4829bbe7c9f8a6d2e3d9d87b66ed57d4f"

// set the global registrar address
admin.setGlobalRegistrar(globalRegistrarAddr)
// set HashReg address via globalRegistrar
hashRegAddr = admin.setHashReg()
// set UrlHint address via globalRegistrar
urlHintAddr = admin.setUrlHint()

// (re)sets the registrar variable to a GlobalRegistrar contract instance 
registrar = GlobalRegistrar.at(globalRegistrarAddr);

如果这是成功的,如果注册服务商返回地址,您应该能够检查以下命令:

registrar.owner("HashReg");
registrar.owner("UrlHint");
registrar.addr("HashReg");
registrar.addr("UrlHint");

和下面的返回正确的代码:

eth.getCode(registrar.address);
eth.getCode(registrar.addr("HashReg"));
eth.getCode(registrar.addr("UrlHint"));

使用注册服务

可以在契约和dapps之间提供有用的接口。

全球注册商

要预定一个名字,请用以下方法注册一个帐号地址:

registrar.reserve.sendTransaction(name, {from:primary})
registrar.setAddress.sendTransaction (name, address, true, {from: primary})

你需要等待交易被拿起(或强制挖掘他们,如果你在一个私人链中)。要检查您查询注册商:

registrar.owner(name)
registrar.addr(name)

HashReg和UrlHint

HashReg和UrlHint可以与以下abis一起使用:

hashRegAbi = '[{"constant":false,"inputs":[],"name":"setowner","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_key","type":"uint256"},{"name":"_content","type":"uint256"}],"name":"register","outputs":[],"type":"function"}]'
urlHintAbi = '[{"constant":false,"inputs":[{"name":"_hash","type":"uint256"},{"name":"idx","type":"uint8"},{"name":"_url","type":"uint256"}],"name":"register","outputs":[],"type":"function"}]'

设立合同实例:

hashReg = eth.contract(hashRegAbi).at(registrar.addr("HashReg")));
urlHint = eth.contract(UrlHintAbi).at(registrar.addr("UrlHint")));

将内容哈希与关键哈希关联:

hashReg.register.sendTransaction(keyhash, contenthash, {from:primary})

将一个url关联到内容哈希:

urlHint.register.sendTransaction(contenthash, url, {from:primary})

检查分辨率:

contenthash = hashReg._hash(keyhash);
url = urlHint._url(contenthash);

示例脚本

下面的示例脚本演示了本教程中讨论的大多数功能。您可以通过运行JSRE作为geth js script.js 2>>geth.log如果你想在本地的私人链上运行这个测试,那么开始geth:

geth --maxpeers 0 --networkid 123456 --nodiscover --unlock primary js script.js 2>> geth.log

请注意,networkid可以是任意的非负整数,0总是活网。

personal.newAccount("")

primary = eth.accounts[0];
balance = web3.fromWei(eth.getBalance(primary), "ether");
personal.unlockAccount(primary, "00");
// miner.setEtherbase(primary)

miner.start(8); admin.sleepBlocks(10); miner.stop()  ;

// 0xc6d9d2cd449a754c494264e1809c50e34d64562b
primary = eth.accounts[0];
balance = web3.fromWei(eth.getBalance(primary), "ether");

globalRegistrarTxHash = admin.setGlobalRegistrar("0x0");
//'0x0'
globalRegistrarTxHash = admin.setGlobalRegistrar("", primary);
//'0xa69690d2b1a1dcda78bc7645732bb6eefcd6b188eaa37abc47a0ab0bd87a02e8'
miner.start(1); admin.sleepBlocks(1); miner.stop();
//true
globalRegistrarAddr = eth.getTransactionReceipt(globalRegistrarTxHash).contractAddress;
//'0x3d255836f5f8c9976ec861b1065f953b96908b07'
eth.getCode(globalRegistrarAddr);
//...
admin.setGlobalRegistrar(globalRegistrarAddr);
registrar = GlobalRegistrar.at(globalRegistrarAddr);

hashRegTxHash = admin.setHashReg("0x0");
hashRegTxHash = admin.setHashReg("", primary);
txpool.status
miner.start(1); admin.sleepBlocks(1); miner.stop();
hashRegAddr = eth.getTransactionReceipt(hashRegTxHash).contractAddress;
eth.getCode(hashRegAddr);

registrar.reserve.sendTransaction("HashReg", {from:primary});
registrar.setAddress.sendTransaction("HashReg",hashRegAddr,true, {from:primary});
miner.start(1); admin.sleepBlocks(1); miner.stop();
registrar.owner("HashReg");
registrar.addr("HashReg");

urlHintTxHash = admin.setUrlHint("", primary);
miner.start(1); admin.sleepBlocks(1); miner.stop();
urlHintAddr = eth.getTransactionReceipt(urlHintTxHash).contractAddress;
eth.getCode(urlHintAddr);

registrar.reserve.sendTransaction("UrlHint", {from:primary});
registrar.setAddress.sendTransaction("UrlHint",urlHintAddr,true, {from:primary});
miner.start(1); admin.sleepBlocks(1); miner.stop();
registrar.owner("UrlHint");
registrar.addr("UrlHint");

globalRegistrarAddr = "0xfd719187089030b33a1463609b7dfea0e5de25f0"
admin.setGlobalRegistrar(globalRegistrarAddr);
registrar = GlobalRegistrar.at(globalRegistrarAddr);
admin.setHashReg("");
admin.setUrlHint("");

/ ///

admin.stopNatSpec();
primary = eth.accounts[0];
personal.unlockAccount(primary, "00")

globalRegistrarAddr = "0xfd719187089030b33a1463609b7dfea0e5de25f0";
admin.setGlobalRegistrar(globalRegistrarAddr);
registrar = GlobalRegistrar.at(globalRegistrarAddr);
admin.setHashReg("0x0");
admin.setHashReg("");
admin.setUrlHint("0x0");
admin.setUrlHint("");


registrar.owner("HashReg");
registrar.owner("UrlHint");
registrar.addr("HashReg")
registrar.addr("UrlHint");


/
eth.getBlockTransactionCount("pending");
miner.start(1); admin.sleepBlocks(1); miner.stop();

source = "contract test {\n" +
"   /// @notice will multiply `a` by 7.\n" +
"   function multiply(uint a) returns(uint d) {\n" +
"      return a * 7;\n" +
"   }\n" +
"} ";
contract = eth.compile.solidity(source).test;
txhash = eth.sendTransaction({from: primary, data: contract.code});

eth.getBlock("pending", true).transactions;

miner.start(1); admin.sleepBlocks(1); miner.stop();
contractaddress = eth.getTransactionReceipt(txhash).contractAddress;
eth.getCode(contractaddress);

multiply7 = eth.contract(contract.info.abiDefinition).at(contractaddress);
fortytwo = multiply7.multiply.call(6);

/

// register a name for the contract
registrar.reserve.sendTransaction(primary,  {from: primary});
registrar.setAddress.sendTransaction("multiply7", contractaddress, true, {from: primary});


admin.stopNatSpec();
filename = "/info.json";
contenthash = admin.saveInfo(contract.info, "/tmp" + filename);
admin.register(primary, contractaddress, contenthash);
eth.getBlock("pending", true).transactions;
miner.start(1); admin.sleepBlocks(1); miner.stop();

admin.registerUrl(primary, contenthash, "file://" + filename);
eth.getBlock("pending", true).transactions;
miner.start(1); admin.sleepBlocks(1); miner.stop();



// retrieve contract address using global registrar entry with 'multply7'
contractaddress = registrar.addr("multiply7);
// retrieve the info using the url 
info = admin.getContractInfo(contractaddress);
multiply7 = eth.contract(info.abiDefinition).at(contractaddress);
// try Natspec
admin.startNatSpec();
fortytwo = multiply7.multiply.sendTransaction(6, { from: primary });


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值