Saturday, January 4, 2020

區塊鏈 初探 (二) 本機編寫 智能合約 與 java 執行

今天會用到的工具是
web3 呼叫 geth api
版本 3.5
solc 編寫智能合約

編寫智能合約

solc 可以直接把 合約檔案,大家可以在之前編寫環境 去那邊做 語法檢測 檢測完,再存入sol 檔


可以發現在上一層目錄 產生了兩個檔, 一個就是 bin二進制數據文件
另一個好像是 abi二進位制介面 可以去查一下。
好了之後 我們複製檔案到 我們的web3 目錄下 ,我們這邊直接執行
web3j solidity generate Token.bin Token.abi -p contract -o /

編譯好後 我們回到最上層目錄


這邊就可以看到我們用 web3 轉換出來的 合約檔案了,準備來引用一下。

spring boot

配置檔

這邊配置 3.5
<dependency>
    <groupId>org.web3j</groupId>
    <artifactId>core</artifactId>
    <version>3.5.0</version>
</dependency>

主程式

import java.io.IOException;
import java.math.BigInteger;

import org.web3j.crypto.CipherException;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.EthFilter;
import org.web3j.protocol.core.methods.response.Web3ClientVersion;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.gas.DefaultGasProvider;


public class web3test {
 //private final static String PRIVATE_KEY = "eb239949bb46a187b4ff1645a13a9c0c146f7e0890d18426a639ae3add6b690a";
    
 private static  Credentials getCredentialsFromPrivateKeyAddress() throws IOException, CipherException  {
     Credentials credentials = WalletUtils.loadCredentials( "654321","F:\\geth\\data\\keystore\\UTC--2020-01-03T05-58-33.219268500Z--21d10c5e114a959e6e92c6f5f0ffcbe7df7785f1");
     return credentials;
 }

//這邊兩個分別是 我們進到 我們geth 裡面的 data keystore
//裡面就是存放著我們一開始創建好的帳戶 我們要指定其中一 個然後設定目標帳戶
// private static Credentials getCredentialsFromPrivateKeyAddress() throws  Exception {
//     Credentials credentials = WalletUtils.loadCredentials("eb239949bb46a187b4ff1645a13a9c0c146f7e0890d18426a639ae3add6b690a", "0xDB24eE35b51cA44C60152628e5Cf32B5AE262364");
//     return credentials;
// }

 private static Credentials getCredentialsFromPrivateKey(){
  return Credentials.create(PRIVATE_KEY);
 }
 private final static BigInteger GAS_LIMIT = BigInteger.valueOf(210000000L);                 
 private final static BigInteger GAS_PRICE = BigInteger.valueOf(10000L);
 private static  String deployContract(Web3j web3j, Credentials credentials) throws Exception{
     return Token.deploy(web3j, credentials, GAS_PRICE, GAS_LIMIT).send().getContractAddress();
 }
 private static  Token loadContract(String address, Web3j web3j, Credentials credentials){
     return Token.load(address, web3j, credentials, GAS_PRICE, GAS_LIMIT);
 }
 private  static void activateFilter(Web3j web3j, String contractAddress,Token exampleContract){
     EthFilter filter = new EthFilter(DefaultBlockParameterName.EARLIEST, DefaultBlockParameterName.LATEST, contractAddress);
     exampleContract.setBalanceEventEventObservable(filter).subscribe(log-> System.out.println(log.toString()));
     exampleContract.setBalanceEventEventObservable(filter).subscribe(log-> System.out.println(log.balance));
     
     web3j.ethLogObservable(filter).subscribe(log -> System.out.println(log.toString()));

  }

 public static  void main(String[] args) throws Exception {
  
  Web3j web3 = Web3j.build(new HttpService());  // defaults to http://localhost:8545/
  Web3ClientVersion web3ClientVersion = web3.web3ClientVersion().sendAsync().get();
  
  String clientVersion = web3ClientVersion.getWeb3ClientVersion();
  Credentials credentials = getCredentialsFromPrivateKeyAddress();
  
  String deployedContract = deployContract(web3, credentials).toString();
  //Thread.sleep(30000);
  
  Token exampleContract = loadContract(deployedContract, web3, credentials);

  
  System.out.println("Deployed contract address; "+ deployedContract);
  System.out.println("----------------------------------------");
  

  activateFilter(web3, deployedContract ,exampleContract);

 
  Boolean result = exampleContract.isValid();
  System.out.println("ok"+result);

  exampleContract.setBalance("Alice", BigInteger.valueOf(4)).send();
  
  System.out.println("ok");

  Object ans = exampleContract.returnBalance("Alice").send();
 
  System.out.println("ok");

  System.out.println("user-defined message:   " + ans.toString());
  
  ans = exampleContract.returnBalance("Bob").send();
  System.out.println("user-defined message:   " + ans.toString());
  
  
  

 }
}

智能合約

package com.example.demo;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.web3j.abi.EventEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Event;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.protocol.core.methods.request.EthFilter;
import org.web3j.protocol.core.methods.response.Log;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.tx.Contract;
import org.web3j.tx.TransactionManager;

import rx.Observable;
import rx.functions.Func1;

/**
 * <p>Auto generated code.
 * <p><strong>Do not modify!</strong>
 * <p>Please use the <a href="https://docs.web3j.io/command_line.html">web3j command line tools</a>,
 * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the 
 * <a href="https://github.com/web3j/web3j/tree/master/codegen">codegen module</a> to update.
 *
 * <p>Generated with web3j version 3.5.0.
 */
public class Token extends Contract {
    private static final String BINARY = "608060405234801561001057600080fd5b506102f9806100206000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063361ab84f1461005157806338639f7b146100ce575b600080fd5b34801561005d57600080fd5b506100b8600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610141565b6040518082815260200191505060405180910390f35b3480156100da57600080fd5b5061013f600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001909291905050506101b5565b005b600080826040518082805190602001908083835b60208310151561017a5780518252602082019150602081019050602083039250610155565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020549050919050565b806000836040518082805190602001908083835b6020831015156101ee57805182526020820191506020810190506020830392506101c9565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020819055507f11fa40d38d8c12e6885f25dce1bff58b84fb756b4fbf3d70be7237df8c4569bc82826040518080602001838152602001828103825284818151815260200191508051906020019080838360005b8381101561028e578082015181840152602081019050610273565b50505050905090810190601f1680156102bb5780820380516001836020036101000a031916815260200191505b50935050505060405180910390a150505600a165627a7a72305820a7bd4530d4008911e0e21fee65b0d060ca18a1baff050318f117f181e7dab57a0029";

    public static final String FUNC_RETURNBALANCE = "returnBalance";

    public static final String FUNC_SETBALANCE = "setBalance";

    public static final Event SETBALANCEEVENT_EVENT = new Event("setBalanceEvent", 
            Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {}, new TypeReference<Uint256>() {}));
    ;

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

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

    public RemoteCall<BigInteger> returnBalance(String _name) {
        final Function function = new Function(FUNC_RETURNBALANCE, 
                Arrays.<Type>asList(new org.web3j.abi.datatypes.Utf8String(_name)), 
                Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {}));
        return executeRemoteCallSingleValueReturn(function, BigInteger.class);
    }

    public RemoteCall<TransactionReceipt> setBalance(String _name, BigInteger _balance) {
        final Function function = new Function(
                FUNC_SETBALANCE, 
                Arrays.<Type>asList(new org.web3j.abi.datatypes.Utf8String(_name), 
                new org.web3j.abi.datatypes.generated.Uint256(_balance)), 
                Collections.<TypeReference<?>>emptyList());
        return executeRemoteCallTransaction(function);
    }

    public List<SetBalanceEventEventResponse> getSetBalanceEventEvents(TransactionReceipt transactionReceipt) {
        List<Contract.EventValuesWithLog> valueList = extractEventParametersWithLog(SETBALANCEEVENT_EVENT, transactionReceipt);
        ArrayList<SetBalanceEventEventResponse> responses = new ArrayList<SetBalanceEventEventResponse>(valueList.size());
        for (Contract.EventValuesWithLog eventValues : valueList) {
            SetBalanceEventEventResponse typedResponse = new SetBalanceEventEventResponse();
            typedResponse.log = eventValues.getLog();
            typedResponse.name = (String) eventValues.getNonIndexedValues().get(0).getValue();
            typedResponse.balance = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue();
            responses.add(typedResponse);
        }
        return responses;
    }

    public Observable<SetBalanceEventEventResponse> setBalanceEventEventObservable(EthFilter filter) {
        return web3j.ethLogObservable(filter).map(new Func1<Log, SetBalanceEventEventResponse>() {
            @Override
            public SetBalanceEventEventResponse call(Log log) {
                Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(SETBALANCEEVENT_EVENT, log);
                SetBalanceEventEventResponse typedResponse = new SetBalanceEventEventResponse();
                typedResponse.log = log;
                typedResponse.name = (String) eventValues.getNonIndexedValues().get(0).getValue();
                typedResponse.balance = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue();
                return typedResponse;
            }
        });
    }

    public Observable<SetBalanceEventEventResponse> setBalanceEventEventObservable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) {
        EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress());
        filter.addSingleTopic(EventEncoder.encode(SETBALANCEEVENT_EVENT));
        return setBalanceEventEventObservable(filter);
    }

    public static RemoteCall<Token> deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
     
        return deployRemoteCall(Token.class, web3j, credentials, gasPrice, gasLimit, BINARY, "");
    }

    public static RemoteCall<Token> deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
        return deployRemoteCall(Token.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, "");
    }

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

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

    public static class SetBalanceEventEventResponse {
        public Log log;

        public String name;

        public BigInteger balance;
    }
}

我們來看更清楚一下
 
  Web3j web3 = Web3j.build(new HttpService());  // defaults to http://localhost:8545/
  Web3ClientVersion web3ClientVersion = web3.web3ClientVersion().sendAsync().get();
  
  String clientVersion = web3ClientVersion.getWeb3ClientVersion();
        
  Credentials credentials = getCredentialsFromPrivateKeyAddress();
        //登入
  
  String deployedContract = deployContract(web3, credentials).toString();
     //發布合約 hash
  
  Token exampleContract = loadContract(deployedContract, web3, credentials);
        // 讀入 合約 
  
  System.out.println("Deployed contract address; "+ deployedContract);
  System.out.println("----------------------------------------");
  
        //監聽器
  activateFilter(web3, deployedContract ,exampleContract);

     //調用 method
  Boolean result = exampleContract.isValid();
  System.out.println("ok"+result);

  exampleContract.setBalance("Alice", BigInteger.valueOf(4)).send();
  
  System.out.println("ok");

  Object ans = exampleContract.returnBalance("Alice").send();
 
  System.out.println("ok");

  System.out.println("user-defined message:   " + ans.toString());
  
  ans = exampleContract.returnBalance("Bob").send();
  System.out.println("user-defined message:   " + ans.toString());
這邊要注意創世塊的問題也就是 gaslimt ,然後還有版本問題
web3j 3.5 這個版本 ,裡面函數也就是我們編譯出來的 智能合約
ethLogObservable
4.0後有發生過優化可能要調整一下 其他編譯軟體 的版本才可以兼容。

啟動


成功!
後面應該會對這個合約再加強 一些東西 這個東西我就跟spring 放在一起了到現在我們已經產生一條私有鏈囉。
參考
https://stevenocean.github.io/2018/04/06/web3j-ethereum-token.html