04-web3j交易(Transactions)


文章是本人学习过程翻译,原文来自官方文档:https://web3j.readthedocs.io/en/latest/#

官网:https://web3j.io/

官方GitHub:https://github.com/web3j/web3j

官方demo:https://github.com/web3j/web3j/tree/master/integration-tests

文档版本v3.4.0。


一般来说,在以太坊支持三种类型的交易:

  • 交易以太币
  • 创建智能合约
  • 发起交易到智能合约

要进行这些交易,需要消耗gas,如果你只是查询合约的状态则不需要gas.

获取以太币(Obtaining Ether)

有两种获取以太币的方法:

  • 挖矿
  • 跟别人购买以太币

以太坊测试网(Ethereum testnets)

下面列出的是Ethereum的各种测试网络,支持各自的客户端:

  • Rinkeby (Geth only)
  • Kovan (Parity only)
  • Ropsten (Geth and Parity)

对于开发者,推荐使用Rinkeby/Kovan测试网络,因为它们使用POA共识机制,可以确保交易和区块可以及时被确认打包。

Ropsten测试网络很接近Mainet主网,同样使用POW共识机制,过去曾经被攻击过,使用它的开发者可能会遇到很多问题。

在Rinkeby网络获取以太币,请看here.

在Kovan网络获取以太币,请看here

在Ropsten网络获取以太币,你需要把你的钱包地址发送到web3j Gitter channel

在测试网/私有网挖矿(Mining on testnet/private blockchains)

在测试网/私有网挖矿没有主网那么高难度,只需要一台安装客户端节点的普通的电脑。

当你通过挖矿获得以太币后,你就可以发起交易了。

Gas

要进行交易,需要发起的账户花费一些gas,把交易的结果提交到以太坊的区块上。你需要指定两个参数,让客户端节点在处理交易时,知道你希望花费多少以太币来完成交易。

Gas price gas价格

web3j使用默认价格22,000,000,000 Wei (22 x 10-8 Ether).这个默认值定义在ManagedTransaction

Gas limit 最高gas

这个值一般要小于6,700,000, 可以在 https://ethstats.net/ 查看当前的gas limit.

这两个参数的设置会影响到交易被处理的速度,您可能需要调整这些参数,以确保及时交易发生。

发起交易查询当前gas price:

 Transfer transfer = new Transfer(web3j, transactionManager);
 BigInteger gasPrice = transfer.requestCurrentGasPrice();

交易机制(Transaction mechanisms)

当你创建了一个拥有以太币的账户后,你可以通过以下两种交易机制,和以太坊网络(私网/公网)交易:

  1. 通过以太坊客户端签名交易 - Transaction signing via an Ethereum client
  2. 线下签名交易 - Offline transaction signing

通过以太坊客户端签名交易

通过客户端交易,首先你要启动客户端(geth、parity),然后使用客户端来创建你的钱包账户:

  • Geth的控制台支持导入一个已经存在的私钥文件,或者创建一个新的账户。
  • Geth的控制台提供了json-rpc的命令来让你管理geth,比如 _personal_newAccount _ 来创建一个新的账户。

创建了账户后,你就可以使用web3j来连接客户端(ipc/http),这不需要提供秘钥,只需要保证客户端可以连接,连接后可以解锁账户、发起交易,代码如下:

Admin web3j = Admin.build(new WindowsIpcService("\\\\.\\pipe\\geth.ipc"));
        PersonalUnlockAccount personalUnlockAccount = web3j
                .personalUnlockAccount("0x053b2252a10356ec0ce1cfc587b909beee591409", "111").send();
        System.out.println(personalUnlockAccount.accountUnlocked());
        EthGetTransactionCount ethGetTransactionCount = web3j
                .ethGetTransactionCount("0x053b2252a10356ec0ce1cfc587b909beee591409", DefaultBlockParameterName.LATEST)
                .send();
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();
        Transaction transaction = Transaction.createEtherTransaction("0x053b2252a10356ec0ce1cfc587b909beee591409",
                nonce, GAS_PRICE, GAS_LIMIT, "0xc7d9fffaf663c5dfa31b096164cc843c01d0797a", BigInteger.valueOf(20L));
        org.web3j.protocol.core.methods.response.EthSendTransaction transactionResponse = web3j
                .ethSendTransaction(transaction).send();
        String transactionHash = transactionResponse.getTransactionHash();
        System.out.println(transactionHash);

更多实例请看 DeployContractIT和它的父类 Scenario

线下交易签名(Offline transaction signing)

如果你不想管理你的客户端节点,或者不想把提供钱包密码给客户端节点,线下交易签名比较适合你。

线下交易签名允许你使用web3j提供的钱包账户发起交易,你完全控制自己的私钥,交易发送到网络上的其它节点并传播。

通过覆盖签名方法ECKeyPair,来发起交易签名。

ECKeyPair:椭圆曲线算法生成秘钥对(Elliptic Curve SECP-256k1 generated key pair)

为了实现线下交易,你需要使用web3j生成安全的钱包账户,并这个账户交易。

创建钱包文件:

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

加载钱包文件:

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

credentials 用来签名交易。

查看完整的钱包文件规范:Web3 Secret Storage Definition

交易签名

线下交易使用 RawTransaction 对象来完成,一共有如下几步:

  1. 确定交易账户的下一个 nonce
  2. 创建 RawTransaction 对象
  3. 使用 RLP 编码 RawTransaction 对象
  4. 签名 RawTransaction 对象
  5. 发送 RawTransaction 对象给节点处理。

交易nonce

以太坊实战-再谈nonce使用陷阱:https://blog.csdn.net/wo541075754/article/details/79054937

  • nonce 值用来唯一标识账户的交易,一个值只能使用一次
  • nonce 值可以通过eth_getTransactionCount方法获取
  • 如果一个账户使用相同的 nonce通过发起多个交易,只有一个交易会被接受,(gas price)手续费高的会覆盖手续费低的交易,如果交易费一样,后发起的交易会被拒绝。
  • Transaction的 nonce 值可以为空,不指定 nonce 值由客户端自动排序赋值
  • RawTransaction的 nonce 值不能为空
Admin web3j = Admin.build(new HttpService());  // defaults to http://localhost:8545/

Credentials credentials = WalletUtils.loadCredentials("111", "E:\\develop\\geth\\data_dev\\keystore\\UTC--2018-05-05T06-50-18.813015000Z--053b2252a10356ec0ce1cfc587b909beee591409");
  BigInteger value = Convert.toWei("5", Convert.Unit.ETHER).toBigInteger();

// get the next available nonce
EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
             "0x053b2252a10356ec0ce1cfc587b909beee591409", DefaultBlockParameterName.LATEST).send();
BigInteger nonce = ethGetTransactionCount.getTransactionCount();

// create our transaction
RawTransaction rawTransaction  = RawTransaction.createEtherTransaction(
             nonce, GAS_PRICE, GAS_LIMIT,  "0xc7d9fffaf663c5dfa31b096164cc843c01d0797a", value);

// sign & send our transaction
byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
String hexValue = Numeric.toHexString(signedMessage);
EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
System.out.println(ethSendTransaction.getTransactionHash());

交易类型(Transaction types)

两种交易类型:

  • Transaction
  • RawTransaction

不管哪种交易类型,都需要下面的参数:

  • Gas price
  • Gas limit
  • Nonce
  • From

从一个账户发送以太币到另外一个账户

RawTransaction

BigInteger value = Convert.toWei("1.0", Convert.Unit.ETHER).toBigInteger();
RawTransaction rawTransaction  = RawTransaction.createEtherTransaction(
             <nonce>, <gas price>, <gas limit>, <toAddress>, value);

Transfer

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类型。庆幸的时,使用web3j的Solidity smart contract wrappers ,它可以帮你完成这些转换,你可以很方便地使用。

  1. 创建智能合约,有两个属性:
  • value - 你希望存到智能合约的以太币
  • 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
}

如果智能合约有构造函数,需要提供构造参数,必须编码这些参数并拼到合约代码后面:

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

  1. 发起交易到智能合约

与存在的智能合约交互,需要提供以下属性:

  • to - 智能合约地址
  • value - 你希望存到智能合约的以太币
  • data - 编码后的调用的函数和参数
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...

交易调用不可能直接得到返回值,要获取返回值,必须用Filters and Events

更多关于函数编码的介绍,请看Application Binary Interface

  1. 查询智能合约的状态

可以通过eth_call这个JSON-RPC call接口来查询合约状态,这个调用不用花费gas,因为它没有改变账户的状态。

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

注意:如果一个无效的函数调用,或一个空的结果,返回值将Collections.emptyList的实例()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容

  • 简介 不管你们知不知道以太坊(Ethereum blockchain)是什么,但是你们大概都听说过以太坊。最近在新...
    Lilymoana阅读 3,892评论 1 22
  • 1 伟大的开拓者-比特币 1) 比特币产生 2008年,中本聪(Satoshi Nakamoto)发表了一...
    金子_c38e阅读 7,345评论 0 18
  • 青城雨夜 半生浮云飘北庭,而立苍海落南郡。 莫道高处天云淡,却了狂生一世愿。
    无尽狂歌阅读 140评论 0 0
  • 褒扬 一心二意三色眼,四体五脏六腑健。 七窍八璞九肠通,十指千夫万众羡。 贬斥 一心二意三色晴,四肢五官...
    文字砌匠阅读 180评论 0 3
  • 出生,两岁那年。 不知哪里传来消息,一种类似于“笔仙儿”的无人算命,转到我们村。 相传,每个村只有一人,能端着锅拍...
    果慧_631e阅读 416评论 5 5