以太坊学习7--Web3j和智能合约

一、Web3j入门

以太坊推出了web3.js的nodejs库,但是对于学Java出身的而言非常的不习惯,在github中寻找到了Java版本的web3j。轻量级客户端与以太坊交互的Java库。
web3j github地址:https://github.com/web3j/web3j
web3j 文档地址:https://web3j.readthedocs.io/en/latest/

一个简单的例子:
1、创建一个maven项目
2、增加dependencies

<dependency>
   <groupId>org.web3j</groupId>
   <artifactId>core</artifactId>
   <version>3.3.1</version>
</dependency>

3、申请测试网络
(1)本地私链(geth)
本地搭建geth客户端或者搭建私链,这个优点在于全部是本地环境想干嘛干嘛,搭建私链可以看我的另两篇文章https://blog.csdn.net/m0_37739193/article/details/81056336https://blog.csdn.net/m0_37739193/article/details/81056921,但是这种方式比较麻烦。
(2)infura
是一个客户端,免费的节点钱包,比较方便。本次代码就基于infura来进行
infura官网地址:https://infura.io/
4、入门编码

import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.Web3ClientVersion;
import org.web3j.protocol.http.HttpService;

public class Web3Blog1_1 {
    public static void main(String[] args) throws Exception{
        Web3j web3 = Web3j.build(new HttpService("https://ropsten.infura.io/rq6f6P9wex66SUNzvnDz"));
        Web3ClientVersion web3ClientVersion = web3.web3ClientVersion().send();
        String clientVersion = web3ClientVersion.getWeb3ClientVersion();
        System.out.println(clientVersion);
    }
}

这里写图片描述
注:
1.代码地址为:https://mainnet.infura.io/rq6f6P9wex66SUNzvnDz
输出结果为:Geth/v1.8.13-patched-infura-omnibus-b59d4428/linux-amd64/go1.9.2
2.代码地址为:https://infuranet.infura.io/rq6f6P9wex66SUNzvnDz
输出结果为:Parity//v1.6.6-beta-8c6e3f3-20170411/x86_64-linux-gnu/rustc1.16.0
3.代码地址为:https://kovan.infura.io/rq6f6P9wex66SUNzvnDz
输出结果为:Parity//v1.10.6-stable-bc0d134-20180605/x86_64-linux-gnu/rustc1.26.1
4.代码地址为:https://rinkeby.infura.io/rq6f6P9wex66SUNzvnDz
输出结果为:Geth/v1.8.13-stable/linux-amd64/go1.9.2

NETWORK DESCRIPTION URL:
Mainnet production network https://mainnet.infura.io/rq6f6P9wex66SUNzvnDz
Ropsten test network https://ropsten.infura.io/rq6f6P9wex66SUNzvnDz
INFURAnet test network https://infuranet.infura.io/rq6f6P9wex66SUNzvnDz
Kovan test network https://kovan.infura.io/rq6f6P9wex66SUNzvnDz
Rinkeby test network https://rinkeby.infura.io/rq6f6P9wex66SUNzvnDz
IPFS gateway https://ipfs.infura.io
 

二、Transaction转账

为了完成以太坊交易,必须有几个先决条件
1、对方的以太坊地址
2、确定要转账的金额
3、自己地址的转账权限
4、大于转账金额的以太币,以太币转账其实就是提出一个交易,矿工会帮你计算,计算完成即达成交易,但是矿工计算需要支付一定的费用(GAS),支付过少,计算转账就有可能很慢或者不成功。
 
转账方式1:

import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.math.BigInteger;


public class TransactionTest {
    public static void main(String[] args) throws Exception {
        //设置需要的矿工费
        BigInteger GAS_PRICE = BigInteger.valueOf(22_000_000_000L);
        BigInteger GAS_LIMIT = BigInteger.valueOf(4_300_000);

        //调用的是kovan测试环境,这里使用的是infura这个客户端
        Web3j web3j = Web3j.build(new HttpService("https://kovan.infura.io/<your-token>"));
        //转账人账户地址
        String ownAddress = "0xD1c82c71cC567d63Fd53D5B91dcAC6156E5B96B3";
        //被转人账户地址
        String toAddress = "0x6e27727bbb9f0140024a62822f013385f4194999";
        //转账人私钥
        Credentials credentials = Credentials.create("xxxxxxxxxxxxx");
        //        Credentials credentials = WalletUtils.loadCredentials(
        //                "123",
        //                "src/main/resources/UTC--2018-03-01T05-53-37.043Z--d1c82c71cc567d63fd53d5b91dcac6156e5b96b3");

        EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
                ownAddress, DefaultBlockParameterName.LATEST).sendAsync().get();
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();

        //创建交易,这里是转0.5个以太币
        BigInteger value = Convert.toWei("0.5", Convert.Unit.ETHER).toBigInteger();
        RawTransaction rawTransaction = RawTransaction.createEtherTransaction(
                nonce, GAS_PRICE, GAS_LIMIT, toAddress, value);

        //签名Transaction,这里要对交易做签名
        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        String hexValue = Numeric.toHexString(signedMessage);

        //发送交易
        EthSendTransaction ethSendTransaction =
                web3j.ethSendRawTransaction(hexValue).sendAsync().get();
        String transactionHash = ethSendTransaction.getTransactionHash();

        //获得到transactionHash后就可以到以太坊的网站上查询这笔交易的状态了
        System.out.println(transactionHash);
    }
}

这里写图片描述

1、第27-31行,可以用两种方式获得地址的信任凭证,一种是直接使用私钥,一种是使用keystore文件
2、https://kovan.etherscan.io/tx/0x0d29fa7db4503f94fd6f4c7894a1fb21a628d96f48f408efc19554d812628a81
这个地址是可以查到你的这笔交易的
0x0d29fa7db4503f94fd6f4c7894a1fb21a628d96f48f408efc19554d812628a81是transactionHash
这个地址是测试地址,如果需要查询主网上的,删除kovan
 
转账方式2:

import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;

import java.math.BigDecimal;

public class TransactionTest2 {
    public static void main(String[] args) throws Exception {
        Web3j web3j = Web3j.build(new HttpService("https://kovan.infura.io/<your-token>"));
        // 被转人账户地址
        String toAddress = "0x6e27727bbb9f0140024a62822f013385f4194999";
        // 转账人私钥
        Credentials credentials = Credentials.create("xxxxxxxx");

        TransactionReceipt transactionReceipt = Transfer.sendFunds(
                web3j, credentials, toAddress,
                BigDecimal.valueOf(0.2), Convert.Unit.ETHER).send();

        System.out.println(transactionReceipt.getTransactionHash());
    }
}

这里写图片描述
注意:
这两种方式都是离线的,但是第二种代码量比较小。

备注:
如果在kovan环境中没有以太币的话,可以到https://gitter.im/kovan-testnet/faucet这里去要,直接注册账号之后,把你的账号地址发到群里就行了,过几分钟就会给你转钱的(每次只给你转3个eth且中间间隔为6天),主网的账号地址和kovan是一样的,但是里面的币是不一样的。
这里写图片描述
这里写图片描述
这里写图片描述
 

三、智能合约

1、首先我们需要一份写好的智能合约

pragma solidity ^0.4.18;

// Example taken from https://www.ethereum.org/greeter, also used in
// https://github.com/ethereum/go-ethereum/wiki/Contract-Tutorial#your-first-citizen-the-greeter

contract mortal {
    /* Define variable owner of the type address*/
    address owner;

    /* this function is executed at initialization and sets the owner of the contract */
    function mortal() { owner = msg.sender; }

    /* Function to recover the funds on the contract */
    function kill() { if (msg.sender == owner) suicide(owner); }
}

contract greeter is mortal {
    /* define variable greeting of the type string */
    string greeting;

    /* this runs when the contract is executed */
    function greeter(string _greeting) public {
        greeting = _greeting;
    }

    /* main function */
    function greet() constant returns (string) {
        return greeting;
    }
}

注意:智能合约的代码是solidity写的,有关solidity语法和使用可以查询solidity官网
https://solidity.readthedocs.io/en/v0.4.21/

2、进入https://remix.ethereum.org/ 网站,此网站是在线智能合约编译网站,所以比较简便,不需要安装truffle等工具
这里写图片描述
点击图示中details按钮,会出现智能合约的ABI和BIN
注:对已经部署好的智能合约也可以直接在https://etherscan.io/查询获得
这里写图片描述
这里写图片描述

这里写图片描述
将BYTECODE中的object复制下来保存成greeter.bin,点击ABI(Application Binary Interface:应用程序二进制接口,定义了智能合约提供的方法功能)旁边的复制按钮将其保存成greeter.abi

进入https://github.com/web3j/web3j/releases/tag/v3.3.1网站,下载web3j-3.3.1.tar,并解压。
这里写图片描述
进入目录bin下,将greeter.bin和greeter.abi,复制到目录下,在此目录命令行执行

web3j solidity generate --javaTypes greeter.bin greeter.abi -o src/ -p com.your.organisation.name

web3j solidity generate [–javaTypes|–solidityTypes] /path/to/.bin /path/to/.abi -o /path/to/src/main/java -p com.your.organisation.name
-o 是源代码要放的目录,-p是包名,–javaTypes也可以选择–solidityTypes
生成出来Greeter.java
这里写图片描述

import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.tx.Contract;
import org.web3j.tx.TransactionManager;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;

public class Greeter extends Contract {
    private static final String BINARY = "6060604052341561000f57600080fd5b6040516103203803806103208339810160405280805160008054600160a060020a03191633600160a060020a03161790559190910190506001818051610059929160200190610060565b50506100fb565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a157805160ff19168380011785556100ce565b828001600101855582156100ce579182015b828111156100ce5782518255916020019190600101906100b3565b506100da9291506100de565b5090565b6100f891905b808211156100da57600081556001016100e4565b90565b6102168061010a6000396000f30060606040526004361061004b5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b58114610050578063cfae321714610065575b600080fd5b341561005b57600080fd5b6100636100ef565b005b341561007057600080fd5b610078610130565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156100b457808201518382015260200161009c565b50505050905090810190601f1680156100e15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000543373ffffffffffffffffffffffffffffffffffffffff9081169116141561012e5760005473ffffffffffffffffffffffffffffffffffffffff16ff5b565b6101386101d8565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101ce5780601f106101a3576101008083540402835291602001916101ce565b820191906000526020600020905b8154815290600101906020018083116101b157829003601f168201915b5050505050905090565b602060405190810160405260008152905600a165627a7a723058209dd925f8a845985cc97b98b1d11e67cb72915d7316a7eeb4e28cec3f5f398c9f0029";

    protected Greeter(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
        super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
    }

    protected Greeter(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
        super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
    }

    public RemoteCall<TransactionReceipt> kill() {
        Function function = new Function(
                "kill",
                Arrays.<Type>asList(),
                Collections.<TypeReference<?>>emptyList());
        return executeRemoteCallTransaction(function);
    }

    public RemoteCall<String> greet() {
        Function function = new Function("greet",
                Arrays.<Type>asList(),
                Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {}));
        return executeRemoteCallSingleValueReturn(function, String.class);
    }

    public static RemoteCall<Greeter> deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, String _greeting) {
        String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.<Type>asList(new org.web3j.abi.datatypes.Utf8String(_greeting)));
        return deployRemoteCall(Greeter.class, web3j, credentials, gasPrice, gasLimit, BINARY, encodedConstructor);
    }

    public static RemoteCall<Greeter> deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, String _greeting) {
        String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.<Type>asList(new org.web3j.abi.datatypes.Utf8String(_greeting)));
        return deployRemoteCall(Greeter.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, encodedConstructor);
    }

    public static Greeter load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
        return new Greeter(contractAddress, web3j, credentials, gasPrice, gasLimit);
    }

    public static Greeter load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
        return new Greeter(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
    }
}

3、部署智能合约

import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Contract;

public class SmartContractTest {
    public static void main(String[] args) throws Exception {
        Web3j web3j = Web3j.build(new HttpService("https://kovan.infura.io/<your-token>"));
        Credentials credentials = Credentials.create("xxxxxxxxxxxxxxxxxxx");
        //部署智能合约
        Greeter greeter = Greeter.deploy(web3j,credentials,Contract.GAS_PRICE,Contract.GAS_LIMIT,"zx").send();
        System.out.println(greeter.getContractAddress());
        //调用智能合约
        System.out.println(greeter.greet().send());
    }
}

运行结果:
这里写图片描述
访问这个地址https://kovan.etherscan.io/address/0xbf3e83adf30e6f96d40dabe1730f7e77f3596c1b可以查看你部署的智能合约
这里写图片描述
注:这两个代码放在同一个包下
这里写图片描述

参考:
https://www.cnblogs.com/hongpxiaozhu/p/8581002.html
https://www.jianshu.com/p/fe92e06f2fea
(web3j官网中文版)http://blog.hubwiz.com/2018/07/10/web3j-index/


版权声明:本文为m0_37739193原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。