Java与智能合约交互(Web3j)- write函数

Posted MrLiuBai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java与智能合约交互(Web3j)- write函数相关的知识,希望对你有一定的参考价值。

说在前头

Web3是一种新兴的网络概念,由于某些原因导致我们能够接触到的相关技术知识实在有限,每当我遇见技术瓶颈总是不能找到充足的资料,这也让我萌生了填补这片空白知识的冲动。“Hello Web3” 这个专栏会尽力将我掌握的web3 知识分享给大家。如果分享的知识能帮助到大家,希望能够 关注点赞 支持作者!
本人已在github上发布Web3j工具,欢迎使用和star

Java与智能合约交互(Web3j)

之所以选择利用java与智能合约进行交互,完全是因为本人只会Java,并且Java是世界上最好的语言。

能干什么

  • 监控合约状态,读取合约的关键参数,可作为后台数据源。
  • 转账、授权等基础交互。
  • 实现例如抢购、提挖买等复杂交互。

代码分享

  1. 引入依赖
    <dependency>
        <groupId>org.web3j</groupId>
        <artifactId>core</artifactId>
        <version>5.0.0</version>
    </dependency>
  1. 新建Web3j对象
Web3j web3 = Web3j.build(new HttpService(rpcUrl));
  • rpcUrl变量是区块链网络节点的url链接,这些节点会提供很多标准的api方法通过该url进行调用,web3j模块就是在此api上进行封装。
  • 不同网络的rpcUrl可以在对应的区块链浏览器api文档上找到,百度关键字也很容易获取。
  1. 查询当前网络的gasPrice
// 获取gasPrice方法
BigInteger gasPrice = web3.ethGasPrice().send().getGasPrice();

/**
 * 获取当期的gasPrice,如果超过最大的限制,取最大限制
 *
 * @return 在区间内的gasPrice
 * @throws IOException 与节点交互出现异常
 */
public BigInteger getGasPriceWithLimit() throws IOException 
    BigInteger gasPrice = web3.ethGasPrice().send().getGasPrice();
    log.info("Gas price:  Gwei, Min gas price:  Gwei, Max gas price:  Gwei",
    Convert.fromWei(String.valueOf(gasPrice), Convert.Unit.GWEI),
    Convert.fromWei(String.valueOf(minGasPrice), Convert.Unit.GWEI),
    Convert.fromWei(String.valueOf(maxGasPrice), Convert.Unit.GWEI));
    // 超过最大限制返回最大的gasPrice
    if (maxGasPrice.compareTo(gasPrice) < 0) 
       return maxGasPrice;
    
    // 小于最小的限制返回最小的gasPrice
    if (minGasPrice.compareTo(gasPrice) > 0) 
       return minGasPrice;
    
       return gasPrice;

  • 注意,该方法会获取近几个区块的gasPrice均值,可能偏高。
  • 参考作者的getGasPriceWithLimit方法,如果你不是抢购之类紧急的需求,建议不要直接使用这个方法的返回值作为gasPrice,不然亏哭你!
  1. 查询当前账户交易笔数用于作为交易nonce
	/**
     * 获取交易数量
     *
     * @return 账户交易次数
     * @throws IOException 与节点交互失败
     */
    public BigInteger getTransactionCount() throws IOException 
        EthGetTransactionCount ethGetTransactionCount = web3.ethGetTransactionCount(
                ownerAddress, DefaultBlockParameterName.LATEST).send();
        return ethGetTransactionCount.getTransactionCount();
    
  • 以太坊虚拟机用这个nonce字段作为顺序来处理你的多个交易,默认一般nonce取账户的交易笔数
  • 本次交易nonce比上笔成功的交易还小的话会导致交易失败
  1. 查询当前区块链网络的链的chainId
long chainId = web3.ethChainId().send().getChainId().longValue();
  • 在以太坊经典从以太坊分叉出来后,为了防止双花攻击,Chain ID在EIP155被引入。
  • 通过在签名信息中加入Chain ID, 避免一个交易在签名之后被重复在不同的链上提交。
  1. 导入私钥(代码中的私钥切勿传到github公有仓库!!!)
Credentials credentials = Credentials.create(privateKey);
// 私钥对应的地址
String address = credentials.getAddress();
  1. 估算、签名、发送交易数据
    /**
     * 与合约交互
     *
     * @param contractAddress 交互合约地址
     * @param functionName    交互函数名称
     * @param value           携带的eth数量(单位Ether)
     * @param input           输入参数 eg:Arrays.asList(new Address("0x6dF655480F465DC36347a5616E875D155804F0c5"), new Uint256(10000000));
     * @param output          输出参数类型 eg: Arrays.asList(new TypeReference<Bool>());
     *                        类型映射关系
     *                        boolean - bool
     *                        BigInteger - uint/int
     *                        byte[] - bytes
     *                        String - string and address types
     *                        List - dynamic/static array
     *                        T - struct/tuple types
     * @return 交易hash
     * @throws Exception 与节点交互出现异常
     */
    public String writeContract(String contractAddress, String functionName, String value, List<Type> input, List<TypeReference<?>> output) throws Exception 
        // 转换value的单位
        BigInteger valueWei = Convert.toWei(value, Convert.Unit.ETHER).toBigInteger();
        // 生成需要调用函数的data
        Function function = new Function(functionName, input, output);
        String data = FunctionEncoder.encode(function);
        // 估算gasLimit
        BigInteger gasLimit = estimateGasLimit(contractAddress, data, valueWei);
        // 获取gasPrice
        BigInteger gasPrice = getGasPriceWithLimit();
        // 获取chainId
        long chainId = web3.ethChainId().send().getChainId().longValue();
        // 正式请求
        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(), gasPrice, gasLimit, contractAddress, valueWei, data);
        // 签名数据
        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, chainId, credentials);
        String hexValue = Numeric.toHexString(signedMessage);
        // 发送数据
        EthSendTransaction response = web3.ethSendRawTransaction(hexValue).send();
        // 查看是否有错误
        if (response.hasError()) 
            throw new Exception("trade hash: " + response.getTransactionHash() +
                    "\\nerror: " + response.getError().getMessage());
        
        log.info("function:  data: ", functionName, data);
        log.info("Gas fee:  ETH", Convert.fromWei(String.valueOf(gasLimit.multiply(gasPrice)), Convert.Unit.ETHER));
        log.info("Trade Hash: ", response.getTransactionHash());
        return response.getTransactionHash();
    
    
    /**
     * 估算GasLimit
     *
     * @param to    发送的地址
     * @param data  发送的数据
     * @param value 携带的eth数量(单位wei)
     * @return GasLimit
     * @throws Exception 与节点交互失败
     */
    public BigInteger estimateGasLimit(String to, String data, BigInteger value) throws Exception 
        if (gasLimit.intValue() != 0) 
            return gasLimit;
        
        Transaction testTransaction = Transaction.createFunctionCallTransaction(ownerAddress, null, null, null, to, value, data);
        EthEstimateGas response = web3.ethEstimateGas(testTransaction).send();
        // 查看是否有错误
        if (response.hasError()) 
            throw new Exception("error: " + response.getError().getMessage());
        
        return response.getAmountUsed();
    
  • writeContract函数的相关参数说明请参考上一篇文章《read函数》
  • estimateGasLimit函数用于计算本次执行的交易需要耗费的Gas数量,实际就是把你的交易数据传到链上模拟执行函数但不提交结果,将模拟执行花费的Gas作为请求结果返回
  • 当拿到交易所需的所有数据,需要用私钥对数据进行签名操作方可执行
  1. 调用写函数执行转账操作

    /**
     * 转账操作
     *
     * @param contractAddress 交互合约地址
     * @param recipient       接收转账地址
     * @param amount          转账数量
     * @return 交易hash
     * @throws Exception 与节点交互失败
     */
    public String transfer(String contractAddress, String recipient, String amount) throws Exception 
        List input = Arrays.asList(new Address(recipient)
                , new Uint256(new BigInteger(amount, 10)));
        List output = Arrays.asList(new TypeReference<Bool>() 
        );
        return writeContract(contractAddress, "transfer", input, output);
    

掌握这些知识点最好的方法是自己将代码跑起来,去链上获取你想获取的信息

欢迎关注本专栏,作者知无不言~

以太坊智能合约部署与交互

启动容器来执行geth命令

[email protected]:~# docker run -i blockchain101/ethereum-geth:1.6.5 geth attach http://45.32.252.88:8201
Welcome to the Geth JavaScript console!

instance: Geth/01/v1.6.5-stable/linux-amd64/go1.8
coinbase: 0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a
at block: 6064 (Wed, 02 Aug 2017 01:13:50 UTC)
 datadir: /ethcluster/779977/data/01
 modules: admin:1.0 eth:1.0 net:1.0 rpc:1.0 web3:1.0

查看我们的账户和余额

eth.getBalance(eth.accounts[0])

11000000000000000000
eth.getBalance(eth.accounts[1])
0
eth.accounts[0]
"0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a"
eth.accounts[1]
"0xe82e2f0a5abd8774767b9751659976f9c4f59181"

发起一笔交易

> 
eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(3,ether)}) "0x0075da712d26aea17d6647035107f509e13eaf3d113c1577db14d4cc4216caec"

查看交易细节

>  eth.getTransaction("0x0075da712d26aea17d6647035107f509e13eaf3d113c1577db14d4cc4216caec")

{
  blockHash: "0x3115703894dc6015c96ef4de3e5615f416498ca1f985902b38cd70e27dab8871",
  blockNumber: 1250,
  from: "0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a",
  gas: 90000,
  gasPrice: 18000000000,
  hash: "0x0075da712d26aea17d6647035107f509e13eaf3d113c1577db14d4cc4216caec",
  input: "0x",
  nonce: 0,
  r: "0x2aef2c1fa03a0fa4172d21e3383d8c0431d45ec855b9d16efdd5eb2de90c414c",
  s: "0xc4d530fb7902bf509fe56bfbea4861bf6cc16791afc9c9103c1a18f77407d1f",
  to: "0xe82e2f0a5abd8774767b9751659976f9c4f59181",
  transactionIndex: 0,
  v: "0x17cdb6",
  value: 3000000000000000000
}
> eth.getBalance(eth.accounts[1])

3000000000000000000

 

验证用户0的余额

> eth.getBalance(eth.accounts[0])

7999622000000000000

 

编写一个简单的合约

contract Sample {
    uint public value;

          function Sample(uint v){
                    value=v;
          }

          function set(uint v){
                    value=v;
          }

          function get() constant returns (uint){
                    return value;
          }
}

 

remix网页编译得到ABI接口和合约的二进制代码、

 abi=[{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},
{"constant":false,"inputs":[{"name":"v","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},
{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},
{"inputs":[{"name":"v","type":"uint256"}],"payable":false,"type":"constructor"}] [{ constant: true, inputs: [], name: "value", outputs: [{ name: "", type: "uint256" }], payable: false, type: "function" }, { constant: false, inputs: [{ name: "v", type: "uint256" }], name: "set", outputs: [], payable: false, type: "function" }, { constant: true, inputs: [], name: "get", outputs: [{ name: "", type: "uint256" }], payable: false, type: "function" }, { inputs: [{ name: "v", type: "uint256" }], payable: false, type: "constructor" }]

 

需要使用eth.contract来定义一个合约类

> sample=eth.contract(abi)

{
  abi: [{
      constant: true,
      inputs: [],
      name: "value",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: false,
      inputs: [{...}],
      name: "set",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "get",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      inputs: [{...}],
      payable: false,
      type: "constructor"
  }],
  eth: {
    accounts: ["0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a", "0xe82e2f0a5abd8774767b9751659976f9c4f59181"],
    blockNumber: 6225,
    coinbase: "0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a",
    compile: {
      lll: function(),
      serpent: function(),
      solidity: function()
    },
    defaultAccount: undefined,
    defaultBlock: "latest",
    gasPrice: 18000000000,
    hashrate: 0,
    mining: false,
    pendingTransactions: [],
    protocolVersion: "0x3f",
    syncing: false,
    call: function(),
    contract: function(abi),
    estimateGas: function(),
    filter: function(fil, callback),
    getAccounts: function(callback),
    getBalance: function(),
    getBlock: function(),
    getBlockNumber: function(callback),
    getBlockTransactionCount: function(),
    getBlockUncleCount: function(),
    getCode: function(),
    getCoinbase: function(callback),
    getCompilers: function(),
    getGasPrice: function(callback),
    getHashrate: function(callback),
    getMining: function(callback),
    getPendingTransactions: function(callback),
    getProtocolVersion: function(callback),
    getRawTransaction: function(),
    getRawTransactionFromBlock: function(),
    getStorageAt: function(),
    getSyncing: function(callback),
    getTransaction: function(),
    getTransactionCount: function(),
    getTransactionFromBlock: function(),
    getTransactionReceipt: function(),
    getUncle: function(),
    getWork: function(),
    iban: function(iban),
    icapNamereg: function(),
    isSyncing: function(callback),
    namereg: function(),
    resend: function(),
    sendIBANTransaction: function(),
    sendRawTransaction: function(),
    sendTransaction: function(),
    sign: function(),
    signTransaction: function(),
    submitTransaction: function(),
    submitWork: function()
  },
  at: function(address, callback),
  getData: function(),
  new: function()
}

 

合约的二进制代码赋值给SampleHEX方便使用

SampleHEX="0x6060604052341561000c57fe5b60405160208061013a833981016040528080519060200190919050505b806000819055505b505b60f9806100416000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633fa4f24514604e57806360fe47b11460715780636d4ce63c14608e575bfe5b3415605557fe5b605b60b1565b6040518082815260200191505060405180910390f35b3415607857fe5b608c600480803590602001909190505060b7565b005b3415609557fe5b609b60c2565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b600060005490505b905600a165627a7a72305820208c8101070c8ba5a9b32db2bf4b8062a9ba50bc2869c39ac2297938756540e80029"

 

把合约代码部署上链

> thesample=sample.new(1,{from:eth.accounts[0],data:SampleHEX,gas:3000000})

{
  abi: [{
      constant: true,
      inputs: [],
      name: "value",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: false,
      inputs: [{...}],
      name: "set",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "get",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      inputs: [{...}],
      payable: false,
      type: "constructor"
  }],
  address: undefined,
  transactionHash: "0xee74bcb4461c9712ec9aca96a5a3a4c3c64be1213854d519fc8e5432b554f7a1"
}

 

查看交易细节

> samplerecpt=eth.getTransactionReceipt("0xee74bcb4461c9712ec9aca96a5a3a4c3c64be1213854d519fc8e5432b554f7a1")

{
  blockHash: "0xddba16545af882835fb9a69a0e5f3b9287c61664837d5ea0068b38575cb665c5",
  blockNumber: 6246,
  contractAddress: "0x7504fa9d64ab290844b82660d43b310f8fba0276",
  cumulativeGasUsed: 141836,
  from: "0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a",
  gasUsed: 141836,
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  root: "0xd1093ecaca9cc0d10e82a533a15feccedf7ff5c79fb3ebd9366ec0b35dbef478",
  to: null,
  transactionHash: "0xee74bcb4461c9712ec9aca96a5a3a4c3c64be1213854d519fc8e5432b554f7a1",
  transactionIndex: 0
}

 

合约命名

> samplecontract=sample.at("0x7504fa9d64ab290844b82660d43b310f8fba0276")

{
  abi: [{
      constant: true,
      inputs: [],
      name: "value",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: false,
      inputs: [{...}],
      name: "set",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "get",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      inputs: [{...}],
      payable: false,
      type: "constructor"
  }],
  address: "0x7504fa9d64ab290844b82660d43b310f8fba0276",
  transactionHash: null,
  allEvents: function(),
  get: function(),
  set: function(),
  value: function()
}

 

 

合约查看功能函数get(),然后调用set()函数,再get()查看时已经改变了

samplecontract.get.call()
1

> samplecontract.set.sendTransaction(9, {from:eth.accounts[0], gas:3000000})

"0x822ee6fb4caceb7e844c533f7f3bc57806f7cb3676fb3066eb848cca46b2f38a"

> samplecontract.get.call()

9

 

我们再打开一个终端,打开cluster1的peer02的控制台,直接at到上一个终端部署的智能合约地址并进行set操作

[email protected]:~/ethereum-docker/ethereum-docker/ethereum-testnet-docker/dockercomposefiles#  docker run -i blockchain101/ethereum-geth:1.6.5 geth attach http://45.32.252.88:9201
Welcome to the Geth JavaScript console!


>  abi=[{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"v","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"v","type":"uint256"}],"payable":false,"type":"constructor"}]


> sample=eth.contract(abi)

SampleHEX="0x6060604052341561000c57fe5b60405160208061013a833981016040528080519060200190919050505b806000819055505b505b60f9806100416000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633fa4f24514604e57806360fe47b11460715780636d4ce63c14608e575bfe5b3415605557fe5b605b60b1565b6040518082815260200191505060405180910390f35b3415607857fe5b608c600480803590602001909190505060b7565b005b3415609557fe5b609b60c2565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b600060005490505b905600a165627a7a72305820208c8101070c8ba5a9b32db2bf4b8062a9ba50bc2869c39ac2297938756540e80029"

 

直接把合约地址赋值并进行set操作

samplecontract=sample.at("0x7504fa9d64ab290844b82660d43b310f8fba0276")

> samplecontract.get.call()

9

 

简单的方法来了。智能合约的部署需要编译,这里用在线编译: 
https://ethereum.github.io/browser-solidity/#version=soljson-v0.4.14+commit.c2215d46.js 
修改编译好的abi和对象名称:

这里在网上找了个代币的只能合约,可以进行充值、转账和查询,issue 函数可以向充值以太到合约账户,transfer 函数可以向其他账号发送token,getBalance 函数可以获取某个账号的token余额,代码如下:
pragma solidity ^0.4.2;
contract Token {
             address issuer;
             mapping (address => uint) balances;

             event Issue(address account, uint amount);
             event Transfer(address from, address to,uint amount);

             function Token() {
                 issuer = msg.sender;
             }

             function issue(address account, uintamount) {
                 if (msg.sender != issuer) throw;
                 balances[account] += amount;
             }

             function transfer(address to, uint amount){
                 if (balances[msg.sender] < amount)throw;

                 balances[msg.sender] -= amount;
                 balances[to] += amount;

                 Transfer(msg.sender, to, amount);
             }

             function getBalance(address account)constant returns (uint) {
                 return balances[account];
             }
         }
修改编译好的gas和对象名称:
varbrowser_untitled_sol_tokenContract =web3.eth.contract([{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"amount","type":"uint256"}],"name":"issue","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Issue","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Transfer","type":"event"}]);
var token =browser_untitled_sol_tokenContract.new(
   {
     from: web3.eth.accounts[0],
     data:0x6060604052341561000f57600080fd5b5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b6103d2806100616000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063867904b414610054578063a9059cbb14610096578063f8b2cb4f146100d8575b600080fd5b341561005f57600080fd5b610094600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610125565b005b34156100a157600080fd5b6100d6600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101d2565b005b34156100e357600080fd5b61010f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061035c565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561018057600080fd5b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b5050565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561021e57600080fd5b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055507fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef338383604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a15b5050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b9190505600a165627a7a723058204afe007a03446d43d13ac892e6dba9d032f540a11ff427d26c22560727cbea2f0029,
     gas: 4300000
   }, function (e, contract){
    console.log(e, contract);
    if (typeof contract.address !==undefined) {
         console.log(Contract mined! address: + contract.address +  transactionHash:  + contract.transactionHash);
    }
 })

 

此时输入合约部署的实例a_demotypes, 可以看到a_demotypes的详情。

控制台调用

> a_demotypes
{
  abi: [{
      constant: false,
      inputs: [{...}],
      name: "f",
      outputs: [{...}],
      payable: false,
      type: "function"
  }],
  address: "0x54ed7a5f5a63ddada3bfe83b3e632adabaa5fc2f",
  transactionHash: "0x69cde62bcd6458e14f40497f4840f422911d63f5dea2b3a9833e6810db64a1c9",
  allEvents: function(),
  f: function()
}

 

这里重点就是address表示的是合约的地址,你会发现这个和账号的信息结果一样,其实你也可以把这个合约地址看做是一个账号地址,后面我们外部调用到的就是这个合约地址。

充值
token.issue.sendTransaction(eth.accounts[0],100, {from: eth.accounts[0]});

发送 token
token.transfer(eth.accounts[1], 30, {from:eth.accounts[0]})

查看余额
token.getBalance()
控制台调用就不多说,和Java对象调用一样,直接调用即可

 

外部接口与智能合约交互 
以太坊对外提供的有很多接口JSON RPC接口,web3接口,这里我们用JSON RPC接口。 
相关API: https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendrawtransaction 
合约交互的原理 
合约的交互都是一次交易,而我们要做的就是把要调用的方法和参数按照api规定的以参数的形式向区块请求一次交易,ethereum接收到我们的请求后通过解析传递的参数来执行相关的合约代码。 
RPC接口给我们提供了俩个方法:eth_sendTransaction和eth_call。

eth_sendTransaction
Createsnew message call transaction or a contract creation, if the data field containscode.
Parameters

Object - The transaction object
from: DATA, 20 Bytes - The address the transaction is send from.
to: DATA, 20 Bytes - (optional when creating new contract) The address the transaction is directed to.
gas: QUANTITY - (optional, default: 90000) Integer of the gas provided for the transaction execution. It will return unused gas.
gasPrice: QUANTITY - (optional, default: To-Be-Determined) Integer of the gasPrice used for each paid gas
value: QUANTITY - (optional) Integer of the value send with this transaction
data: DATA - The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see Ethereum Contract ABI
nonce: QUANTITY - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce.

 

可以看到,如果我们创建的为合约时,我们只需要from,to(文档上写的是可选的,但是实际操作中没有to为null的话合约不能正常执行,建议还是加上,这个值就是前面我们部署合约后生成的合约address),data。Data的属性的值具体可以看Contract ABI。这里大概说下: 
Data的值相对来说不是固定的,具体怎么生成与合约的参数类型,参数数量都有关联。这里我们以部署的token合约的三个方法为例:

充值issue (address account, uint amount) 
这个方法有俩个参数,address充值账号,uint充值数量。 
根据Contract ABI,data值应该为方法名的sha3的前8个字节+参数的64字节,不够前面补充为0。 
这里方法名并不是issue (address account, uint amount)而是issue(address,uint256)的sha3值。

web3.sha3("issue(address,uint256)")
"0x867904b44b606800f6f10498e11292d04ea19bfc7fe4bc0f1695aa516381f73d"

 

我们往第一个账号充值10,这里的数据不是以太币,而是我们自己创建的代币。

 eth.accounts[0]
"0x0cc9684af605bae10de801218321c1336bb62946"

 

 

将10转换为16进制为

000000000000000000000000000000000000000000000000000000000000000a

 

那么data的数据为:

0x867904b4000000000000000000000000fdf57e81872562a6112656f961944ce821fdf7eb000000000000000000000000000000000000000000000000000000000000000a

技术分享图片

 

那么最后我们调用eth_sendTransaction方法传递参数JSON对象为:

{
    from:0xfdf57e81872562a6112656f961944ce821fdf7eb,
to:0x7fe133950fc010ce41322d88f64f1918b9abb3a3,
data: 0x867904b4000000000000000000000000fdf57e81872562a6112656f961944ce821fdf7eb000000000000000000000000000000000000000000000000000000000000000a
}

 

 

返回:此交易的hash值,此时该交易还没有执行,只是创建,还需要矿工通过挖矿才能完成。 
调用接口方法:

JsonRpcHttpClient client = newJsonRpcHttpClient(new URL(“http://127.0.0.1:8545”));
Object result = client.invoke(methodName,params, Object.class);

 

通过控制台查看第一个账号已有代币:

token.getbalance(eth.accounts[0])

 

执行调用接口代码。返回交易hash值:

0x2013764d1c3fea680f9015353497aa5f9f8d61580a3bd0524b3613b34329c095

 

此时控制台输入: 
技术分享图片

交易和充值一样,需要注意的是代币转出账号为from属性的值,代币转入账号为data属性里的值,to对应的是合约地址。 
eth_call

Executes anew message call immediately without creating a transaction on the block chain.
Parameters

Object - The transaction call object
from: DATA, 20 Bytes - (optional) The address the transaction is sent from.
to: DATA, 20 Bytes - The address the transaction is directed to.
gas: QUANTITY - (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.
gasPrice: QUANTITY - (optional) Integer of the gasPrice used for each paid gas
value: QUANTITY - (optional) Integer of the value send with this transaction
data: DATA - (optional) Hash of the method signature and encoded parameters. For details seeEthereum Contract ABI
QUANTITY|TAG - integer block number, or the string "latest", "earliest" or "pending", see the default block parameter

 

这个方法返回一条信息给我们,相当于数据库的查询功能,参数也是三个,from,to,data,数据格式都是一样的。 
查询getBalance(address account) 
查询方法hash码:

 web3.sha3("getBalance(address)")
"0xf8b2cb4f3943230388faeee074f1503714bff212640051caba01411868d14ae3"

 

查询我们上一步充值的账号,那么传递的参数data为:

0xf8b2cb4f000000000000000000000000fdf57e81872562a6112656f961944ce821fdf7eb

技术分享图片

eth_call方法最后参数为:

{
       from: 0xfdf57e81872562a6112656f961944ce821fdf7eb,
to:0x7fe133950fc010ce41322d88f64f1918b9abb3a3,
data:0xf8b2cb4f000000000000000000000000fdf57e81872562a6112656f961944ce821fdf7eb
}

 

注意,这个方法需要俩参数,处理一个JSONobject外,还有一个字符串参数,这俩可以为“”或者”latest”, “earliest” or “pending” 
调用接口返回一个16进制字符串: 
0x0000000000000000000000000000000000000000000000000000000000000071就是该账号的代币数量,转换为十进制为:113,与控制查询一致。

这就是一个智能合约的交互过程。是不是很简单啊。 

 






















以上是关于Java与智能合约交互(Web3j)- write函数的主要内容,如果未能解决你的问题,请参考以下文章

使用Java+Web3j和Ethereum网络交互:获取Ethereum信息

java使用web3j,部署智能合约在测试链上,并调用(万字详细教程)

java使用web3j,部署智能合约在测试链上,并调用(万字详细教程)

java 调用区块链 发布和调用智能合约

智能合约编译

智能合约编译