从广义上讲,有web3j支持三种类型的以太坊交易:
为了进行这些交易,必须有以太币(以太坊区块链的代币)存在于交易发生的以太坊账户中。这是为了支付gas成本,这是为支付参与交易的以太坊客户端的交易执行成本,支付了这个成本就能将结果提交到以太坊区块链上。获得以太币的说明下文会说到。
此外,我们还可以查询智能合约的状态。
要想获得以太币Ether你有两种途径可以选择: - 1.自己开采挖矿 - 2.从别人那里获取以太币
在私有链中自己挖矿,或者公共测试链( testnet
)是非常简单直接的。但是,在主要的公有链( mainnet
)中,它需要很多很明显的专用GPU时间,除非你已经拥有多个专用GPU的矿机,否则基本上不太可行。如果你希望使用私有链,则在这个 官方文档 中有一些指导。
要购买以太币Ether,你需要通过交易所。由于不同的地区有不同的交易所,你还需要研究自己去哪儿合适。 官方文档 中包含多个交易所,是一个很好的参考。
针对Ethereum以太坊有许多专用测试网络或者叫测试链,他们由各种客户端支持。
对于开发,建议你使用 Rinkeby
或 KoVan
测试链。这是因为他们使用的工作量证明 POA
共识机制,确保交易和块能够一致并及时的创建。 Ropsten
测试链,虽然最接近公有链( Mainnet
),但是因为它使用的工作量证明是 POW
共识机制,过去已受到攻击,对以太坊开发人员来说往往有更多的问题。
你可以通过 Rinkeby
测试链的 Rinkeby Crypto Fauce
请求以太坊币,具体怎么做可以看这里 https://www.rinkeby.io/ 。
有关如何请求 Kovan
测试链的细节可以在 这里 找到。
如果你需要在 Ropsten
上的得到一些以太币,将你的钱包地址的消息发布到 web3j gitter channel ,然后会发送一些给你。
在ethereum以太坊测试链 testnet
中,挖掘难度低于公有链 mainnet
。这意味着你可以用普通的CPU,比如你的笔记本电脑来挖掘新的以太币。你需要做的是运行一个以太坊客户端,例如 geth
或 Parity
,开始做一些储备。进一步的资料可在他们的官方网站上获得。
一旦你开采了一些以太币,你就可以开始使用以太坊区块链了。
然而,如上所述,使用 Kovan
或者 Rinkeby
测试网络更简单些。
当在Ethereum以太坊发生交易时,必须为执行该交易的客户端支付交易成本,将该交易的输出提交到以太坊区块链Ethereum blockchain。
此成本是通过gas来测量的,其中gas是用于在以太坊虚拟机中执行交易指令的数量。请参阅 官方文档 以获取更多信息。
当你使用以太坊客户端时,这意味着,有两个参数用来指示你希望花费多少以太来完成传输:
这两个参数共同决定了你愿意花费在交易成本上的最大量的以太币Ether。也就是说,你花费的gas不会超过 gas price * gas limit
。gas价格也会影响交易发生的速度,这取决于其他交易是否能为矿工提供更有利的gas价格。
你可能需要调整这些参数以确保交易能及时进行。
当你用一些以太币Ether创建了一个有效的帐户时,你可以使用两种机制来与以太坊进行交易。
这两种机制都是Web3j所支持的。
为了通过以太坊客户端进行交易,首先需要确保你正在使用的客户端知道你的钱包地址。最好是运行自己的以太坊客户端,比如 geth
/ Parity
,以便可以更方便的做到这一点。一旦你有一个客户端运行,你可以创建一个以太坊钱包,通过:
personal_newAccount
为 geth
/ 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
值获得方式,下文会提到。 有关此交易工作流的详细信息,请参阅 DeployContractIT 和 Scenario 。
web3j支持的各种管理命令的进一步细节在 Management APIs 中。
如果你不想管理自己的以太坊客户端,或者不想向以太坊客户端提供诸如密码之类的钱包详细信息,那么就通过离线交易认证签名。
离线交易签名认证允许你在web3j中使用你的以太坊钱包签署交易,允许你完全控制你的私有凭据。然后,离线创建的交易可以被发送到网络上的任何以太坊客户端,只要它是一个有效的交易,它会将交易传播到其他节点。
如果需要,还可以执行进程外交易签名认证。这可以通过重写 ECKeyPair 的 sign
方法来实现。
为了离线脱机交易,你需要有你的钱包文件或与私密钱包/账户相关的公共和私人密钥。
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
类型,但是它不需要通过具体的账号地址来请求,因为可以从签名中推断出来。
为了创建和签署原生交易,交易的顺序如下:
nonce
RawTransaction
对象 RawTransaction
对象进行编码 RawTransaction
对象 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
只能使用一次,直到交易被挖掘完成,可以以相同的随机数发送交易的多个版本,但是一旦其中一个被挖掘完成,其他后续提交的都将被拒绝。
可以通过 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中的不同类型的交易都使用 Transaction
和 RawTransaction
对象。关键的区别是交易对象必须始终有一个地址,以便处理 eth_sendTransaction
请求的以太坊客户端知道要使用哪个钱包来代表消息发送者并发送该交易。如上所述,对于离线签名认证签署的原始交易而言,这不是必须的。
接下来的部分概述了不同交易类型所需的关键交易属性。下列属性对所有人都是不变: - Gas price 天然气气体价格 - Gas limit 天然气气体限制 - Nonce 随机数 - from 发送地址
Transaction
和 RawTransaction
对象在所有后续示例中都可互换使用。
在双方之间发送以太币Ether需要交易对象的最少量的信息:
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();
当使用下面列出的智能合约打包器时,将不得不手动执行从Solidity到本机Java类型的所有转换。使用 Solidity smart contract wrappers 是非常有效的,它负责所有的代码生成和转换。
要部署新的智能合约,需要提供以下属性:
// 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...
要与现有的智能合约进行交易,需要提供以下属性:
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 实例。
汇智网原创翻译,转载请标明出处。原文访问 这里