web3j教程:java使用web3j开发以太坊智能合约交易

从广义上讲,有web3j支持三种类型的以太坊交易:html

  • 1.以太币从一方交易到另外一方
  • 2.建立一个智能合约
  • 3.与智能合约交易

为了进行这些交易,必须有以太币(以太坊区块链的代币)存在于交易发生的以太坊帐户中。这是为了支付gas成本,这是为支付参与交易的以太坊客户端的交易执行成本,支付了这个成本就能将结果提交到以太坊区块链上。得到以太币的说明下文会说到。java

此外,咱们还能够查询智能合约的状态。node

如何得到以太币Ether

要想得到以太币Ether你有两种途径能够选择:android

  • 1.本身开采挖矿
  • 2.从别人那里获取以太币

在私有链中本身挖矿,或者公共测试链(testnet)是很是简单直接的。可是,在主要的公有链(mainnet)中,它须要不少很明显的专用GPU时间,除非你已经拥有多个专用GPU的矿机,不然基本上不太可行。若是你但愿使用私有链,则在这个官方文档中有一些指导。ios

要购买以太币Ether,你须要经过交易所。因为不一样的地区有不一样的交易所,你还须要研究本身去哪儿合适。官方文档中包含多个交易所,是一个很好的参考。git

以太坊测试链(testnets)

针对Ethereum以太坊有许多专用测试网络或者叫测试链,他们由各类客户端支持。程序员

  • 1.Rinkeby:只支持geth客户端。
  • 2.Kovan:只支持Parity客户端。
  • 3.Ropsten:支持geth和Parity客户端。

对于开发,建议你使用RinkebyKoVan测试链。这是由于他们使用的工做量证实POA共识机制,确保交易和块可以一致并及时的建立。Ropsten测试链,虽然最接近公有链(Mainnet),可是由于它使用的工做量证实是POW共识机制,过去已受到攻击,对以太坊开发人员来讲每每有更多的问题。github

你能够经过Rinkeby测试链的Rinkeby Crypto Fauce请求以太坊币,具体怎么作能够看这里https://www.rinkeby.io/web

有关如何请求Kovan测试链的细节能够在这里找到。mongodb

若是你须要在Ropsten上的获得一些以太币,将你的钱包地址的消息发布到web3j gitter channel,而后会发送一些给你。

在testnet测试链或者私有链上挖掘

在ethereum以太坊测试链testnet中,挖掘难度低于公有链mainnet。这意味着你能够用普通的CPU,好比你的笔记本电脑来挖掘新的以太币。你须要作的是运行一个以太坊客户端,例如gethParity,开始作一些储备。进一步的资料可在他们的官方网站上得到。

一旦你开采了一些以太币,你就能够开始使用以太坊区块链了。

然而,如上所述,使用Kovan或者Rinkeby测试网络更简单些。

gas

当在Ethereum以太坊发生交易时,必须为执行该交易的客户端支付交易成本,将该交易的输出提交到以太坊区块链Ethereum blockchain。

此成本是经过gas来测量的,其中gas是用于在以太坊虚拟机中执行交易指令的数量。请参阅官方文档以获取更多信息。

当你使用以太坊客户端时,这意味着,有两个参数用来指示你但愿花费多少以太来完成传输:

  • gas price :气体价格,这是每单位gas中以太的消耗量。Web3j使用的默认价格为22000000000 wei(22×10-8 Ether)。这是在交易管理中定义的。
  • gas limit:气体最大量,这是你愿意在交易执行上花费的gas的最大总量。单个交易在一个以太坊区块中有多大的上限,一般将该值限制为小于6700000。当前的gas限制在这里查https://ethstats.net/

这两个参数共同决定了你愿意花费在交易成本上的最大量的以太币Ether。也就是说,你花费的gas不会超过gas price * gas limit。gas价格也会影响交易发生的速度,这取决于其余交易是否能为矿工提供更有利的gas价格。

你可能须要调整这些参数以确保交易能及时进行。

交易机制

当你用一些以太币Ether建立了一个有效的账户时,你可使用两种机制来与以太坊进行交易。

  • 经过以太坊ethereum客户端进行认证签名交易
  • 离线交易签名认证

这两种机制都是Web3j所支持的。

经过以太坊ethereum客户端进行认证签名交易

为了经过以太坊客户端进行交易,首先须要确保你正在使用的客户端知道你的钱包地址。最好是运行本身的以太坊客户端,好比geth/Parity,以即可以更方便的作到这一点。一旦你有一个客户端运行,你能够建立一个以太坊钱包,经过:

  • geth Wiki包含了geth支持的良好运行的不一样机制,例如导入私有密钥文件,并经过控制台建立新的以太坊账户。
  • 或者,你能够经过客户端使用JSON-RPC管理命令,例如用personal_newAccountgeth/Parity建立新以太坊帐户。

经过建立你的钱包文件,你能够经过web3j打开账户,首先建立支持geth/Parity管理命令的web3j实例:

Admin web3j = Admin.build(new HttpService());

而后,你能够解锁账户,并若是是成功的,就能够发送一个交易:

PersonalUnlockAccount personalUnlockAccount = web3j.personalUnlockAccount("0x000...", "a password").send();
if (personalUnlockAccount.accountUnlocked()) {
    // send a transaction
}

以这种方式发送的交易应该经过EthSendTransaction建立,使用Transaction类型:

Transaction transaction = Transaction.createContractTransaction(
              <from address>,
              <nonce>,
              BigInteger.valueOf(<gas price>),  // we use default gas limit
              "0x...<smart contract code to execute>"
      );

      org.web3j.protocol.core.methods.response.EthSendTransaction
              transactionResponse = parity.ethSendTransaction(ethSendTransaction)
              .send();

      String transactionHash = transactionResponse.getTransactionHash();

      // poll for transaction response via org.web3j.protocol.Web3j.ethGetTransactionReceipt(<txHash>)

其中nonce值得到方式,下文会提到。 有关此交易工做流的详细信息,请参阅 DeployContractITScenario

web3j支持的各类管理命令的进一步细节在Management APIs中。

离线交易签名认证Offline transaction signing

若是你不想管理本身的以太坊客户端,或者不想向以太坊客户端提供诸如密码之类的钱包详细信息,那么就经过离线交易认证签名。

离线交易签名认证容许你在web3j中使用你的以太坊钱包签署交易,容许你彻底控制你的私有凭据。而后,离线建立的交易能够被发送到网络上的任何以太坊客户端,只要它是一个有效的交易,它会将交易传播到其余节点。

若是须要,还能够执行进程外交易签名认证。这能够经过重写ECKeyPairsign方法来实现。

建立和使用钱包文件Ethereum wallet file

为了离线脱机交易,你须要有你的钱包文件或与私密钱包/帐户相关的公共和私人密钥。

web3j可以为你生成一个新的安全的以太坊钱包文件Ethereum wallet file,或者与也能够经过私钥来和现有的钱包文件一块儿工做。

建立新的钱包文件:

String fileName = WalletUtils.generateNewWalletFile(
        "your password",
        new File("/path/to/destination"));

加载凭据从钱包文件:

Credentials credentials = WalletUtils.loadCredentials(
        "your password",
        "/path/to/walletfile");

而后这些凭据会被用来签署交易,请参阅Web3安全存储定义钱包文件规范Web3 Secret Storage Definition

签署以太坊交易

要使脱机签名交易获得签署,须要设定一个RawTransaction类型。RawTransaction相似于前面提到的Transaction类型,可是它不须要经过具体的帐号地址来请求,由于能够从签名中推断出来。

为了建立和签署原生交易,交易的顺序以下:

  • 1.肯定交易发起者账户的下一个可用随机数nonce
  • 2.建立RawTransaction对象
  • 3.使用递归长度前缀编码(RLP即Recursive Length Prefix)对RawTransaction对象进行编码
  • 4.签署RawTransaction对象
  • 5.将RawTransaction对象发送到节点进行处理

nonce是一个不断增加的数值,用来惟一地标识交易。一个nonce只能使用一次,直到交易被挖掘完成,能够以相同的随机数发送交易的多个版本,可是一旦其中一个被挖掘完成,其余后续提交的都将被拒绝。

一旦得到下一个可用的nonce,该值就能够用来建立transaction对象:

RawTransaction rawTransaction  = RawTransaction.createEtherTransaction(
             nonce, <gas price>, <gas limit>, <toAddress>, <value>);

而后能够对交易进行签名和编码:

byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, <credentials>);
String hexValue = Numeric.toHexString(signedMessage);

其中凭据是根据建立和使用钱包文件加载的。

而后使用eth_SendRawTransaction发送交易:

EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
String transactionHash = ethSendTransaction.getTransactionHash();
// poll for transaction response via org.web3j.protocol.Web3j.ethGetTransactionReceipt(<txHash>)

有关建立和发送原始事务的完整示例,请参阅 CreateRawTransactionIT

交易随机数nonce

nonce是一个不断增加的数值,用来惟一地标识交易。一个nonce只能使用一次,直到交易被挖掘完成,能够以相同的随机数发送交易的多个版本,可是一旦其中一个被挖掘完成,其余后续提交的都将被拒绝。

能够经过eth_getTransactionCount方法得到下一个可用的nonce

EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
             address, DefaultBlockParameterName.LATEST).sendAsync().get();

     BigInteger nonce = ethGetTransactionCount.getTransactionCount();

而后可使用nonce建立你的交易对象:

RawTransaction rawTransaction  = RawTransaction.createEtherTransaction(
             nonce, <gas price>, <gas limit>, <toAddress>, <value>);

交易类型

web3j中的不一样类型的交易都使用TransactionRawTransaction对象。关键的区别是交易对象必须始终有一个地址,以便处理eth_sendTransaction请求的以太坊客户端知道要使用哪一个钱包来表明消息发送者并发送该交易。如上所述,对于离线签名认证签署的原始交易而言,这不是必须的。

接下来的部分概述了不一样交易类型所需的关键交易属性。下列属性对全部人都是不变:

  • Gas price 自然气气体价格
  • Gas limit 自然气气体限制
  • Nonce 随机数
  • from 发送地址

TransactionRawTransaction对象在全部后续示例中均可互换使用。

以太币从一方交易到另外一方

在双方之间发送以太币Ether须要交易对象的最少许的信息:

  • to :目的地钱包地址
  • value:价值,但愿发送到目的地的以太币数量
BigInteger value = Convert.toWei("1.0", Convert.Unit.ETHER).toBigInteger();
RawTransaction rawTransaction  = RawTransaction.createEtherTransaction(
             <nonce>, <gas price>, <gas limit>, <toAddress>, value);
// send...

可是,建议你使用TransferClass来发送以太币Ether,它负责对nonce管理和经过不断的轮询为你提供响应:

Web3j web3 = Web3j.build(new HttpService());  // defaults to http://localhost:8545/
Credentials credentials = WalletUtils.loadCredentials("password", "/path/to/walletfile");
TransactionReceipt transactionReceipt = Transfer.sendFunds(
        web3, credentials, "0x<address>|<ensName>",
        BigDecimal.valueOf(1.0), Convert.Unit.ETHER).send();

使用智能合约打包器smart contract wrappers

当使用下面列出的智能合约打包器时,将不得不手动执行从Solidity到本机Java类型的全部转换。使用Solidity smart contract wrappers是很是有效的,它负责全部的代码生成和转换。

建立一个智能合约

要部署新的智能合约,须要提供如下属性:

  • value :在智能合约中但愿存放的以太坊Ether量(若是没有提供默认为零)
  • data :十六进制格式化、编译的智能合约建立代码
// using a raw transaction
RawTransaction rawTransaction = RawTransaction.createContractTransaction(
        <nonce>,
        <gasPrice>,
        <gasLimit>,
        <value>,
        "0x <compiled smart contract code>");
// send...

// get contract address
EthGetTransactionReceipt transactionReceipt =
             web3j.ethGetTransactionReceipt(transactionHash).send();

if (transactionReceipt.getTransactionReceipt.isPresent()) {
    String contractAddress = transactionReceipt.get().getContractAddress();
} else {
    // try again
}

若是智能合约包含构造函数,则必须对关联的构造函数字段值进行编码,并将其附加到编译的智能合约代码中compiled smart contract code

String encodedConstructor =
             FunctionEncoder.encodeConstructor(Arrays.asList(new Type(value), ...));

// using a regular transaction
Transaction transaction = Transaction.createContractTransaction(
        <fromAddress>,
        <nonce>,
        <gasPrice>,
        <gasLimit>,
        <value>,
        "0x <compiled smart contract code>" + encodedConstructor);

// send...

与智能合约交易

要与现有的智能合约进行交易,须要提供如下属性:

  • to:智能合同地址
  • value:在智能合约中你但愿存放的以太币Ether量(若是智能合约接受以太币Ether的话)
  • data: 已编码的函数选择器和自变量参数

web3j负责函数编码,有关实现的进一步细节,请参阅应用程序二进制接口部分Application Binary Interface

Function function = new Function<>(
             "functionName",  // function we're calling
             Arrays.asList(new Type(value), ...),  // Parameters to pass as Solidity Types
             Arrays.asList(new TypeReference<Type>() {}, ...));

String encodedFunction = FunctionEncoder.encode(function)
Transaction transaction = Transaction.createFunctionCallTransaction(
             <from>, <gasPrice>, <gasLimit>, contractAddress, <funds>, encodedFunction);

org.web3j.protocol.core.methods.response.EthSendTransaction transactionResponse =
             web3j.ethSendTransaction(transaction).sendAsync().get();

String transactionHash = transactionResponse.getTransactionHash();

// wait for response using EthGetTransactionReceipt...

不管消息签名的返回类型如何,都不可能从事务性函数调用返回值。可是,使用过滤器捕获函数返回的值是可能的。详情请参阅过滤器和事件部分。

查询智能合约状态

这种功能是由eth_call经过JSON-RPC调用来实现的。

eth_call容许你调用智能合约上的方法来查询某个值。此函数没有关联交易成本,这是由于它不改变任何智能合约方法的状态,它只返回它们的值:

Function function = new Function<>(
             "functionName",
             Arrays.asList(new Type(value)),  // Solidity Types in smart contract functions
             Arrays.asList(new TypeReference<Type>() {}, ...));

String encodedFunction = FunctionEncoder.encode(function)
org.web3j.protocol.core.methods.response.EthCall response = web3j.ethCall(
             Transaction.createEthCallTransaction(<from>, contractAddress, encodedFunction),
             DefaultBlockParameterName.LATEST)
             .sendAsync().get();

List<Type> someTypes = FunctionReturnDecoder.decode(
             response.getValue(), function.getOutputParameters());

注意:若是一个无效的函数调用被执行,或者获得一个空null返回结果时,返回值将是一个Collections.emptyList实例。

  • web3j教程,主要是针对java和android程序员进行区块链以太坊开发的web3j开发详解。
  • 以太坊教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。

汇智网原创翻译,转载请标明出处。原文访问其官方博客

相关文章
相关标签/搜索