Wednesday, January 22, 2020

研究 cb compiler (一) 編譯 net/loveruby/cflat/ package

最近要來研究一下compiler
https://github.com/leungwensen/cbc-ubuntu-64bit
cb是 基於 c語言的 簡化版 ,目前好像有看到cbc 編譯 cb 文件,看了官網也只有 make 檔,先來研究
看到了build.properties build.xml ,makefile 裡面也有 ant
發現好像這部分的沒資料來幫忙補充一下
所以complier 裡面需要有 jdk 和 jre 才能把 net 重新 編譯一次
所以步驟就是
下載鏡像
docker pull leungwensen/cbc-ubuntu-64bit
開啟鏡像
docker run -t -i leungwensen/cbc-ubuntu-64bit
安裝 jdk
apt-get install openjdk-8-jdk
安裝 ant
apt-get install ant
安裝 javacc
apt-get install javacc
設置 java home
export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64/"
export PATH=JAVAHOME/bin:PATH
切到我們的目錄 我們動 complier 裡面的
cd net/loveruby/cflat/compiler/
Compiler.java
    private Options parseOptions(String[] args) {
        try {
            return Options.parse(args);
        }
        catch (OptionParseError err) {
            errorHandler.error(err.getMessage());
            errorHandler.error("Try \"cbc --helphhh\" for usage");
            System.exit(1);
            return null;   // never reach
        }
    }

我們動裡面的 help 選項
切到 /cbc 跟目錄
cd cbc-ubuntu-64bit/
rm -rf build
ant

沒意外會看到開始編譯。

看到我們修改後的資料囉,接下來應該會對整個 cb complier 研究一波。

Sunday, January 5, 2020

區塊鏈 初探 (三) 結合Spring Cloud Vue 完成一個 簡單交易功能

基本交易


基本上就是跟我們的上次看到的合約差不多

算是仿造一個吧
花了一點時間寫了個最基本的交易demo
vue.js
spring cloud
geth
這邊可以看到

編寫智能合約

這邊可以看到 我們在 一開始建構元的時候 給了自己1000元,也就是最先一開始發布合約的人,再來就是簡單的 transfer 可以指定對方帳戶進行轉入
pragma solidity 0.4.25;

contract Token {
    uint INITIAL_SUPPLY = 10000;
    mapping(address => uint) public balances;
    //mapping (address => uint) public balances;
    mapping(string => uint) balanceByName;
    event setBalanceEvent(string name,uint balance);
    
    function setBalance(string  memory _name, uint _balance) public {
        balanceByName[_name] = _balance;
        emit setBalanceEvent(_name,_balance);
    }
    function returnBalance (string memory _name)  public view returns (uint){
        return balanceByName[_name];
    }

    
    function Token() public {
        balances[msg.sender] = 1000;
    }
  
      // transfer token for a specified address
    function transfer(address _to, uint _amount) public {
        require(balances[msg.sender] > _amount);
        balances[msg.sender] -= _amount;
        balances[_to] += _amount;
    }
    
      // Gets the balance of the specified address
    function balanceOf(address _owner) public constant returns (uint) {
        return balances[_owner];
    }
}

spring cloud

這邊又是承接我們上次的教學,順便順一次坑

配置文件

可以看到重試機制那邊,當我們進行呼叫 method的時候,我們的fegin內建的負載平衡裡面有重試機制,這邊是因為我在交易的時候發生重複扣款,最後在這邊設置參數就ok了
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

spring:
  application:
    name: feign-consumer
  zipkin:
    base-url:  http://192.168.0.146:9411
    sender:
      type: rabbit
    rabbitmq:
      addresses: 192.168.99.100:5672
      password: guest
      username: guest
      queue: zipkin
feign:
  hystrix:
    enabled: true
ribbon:
  # 同一实例最大重试次数,不包括首次调用。默认值为0
  MaxAutoRetries: 0
  # 同一个微服务其他实例的最大重试次数,不包括第一次调用的实例。默认值为1
  MaxAutoRetriesNextServer: 0
  # 是否所有操作(GET、POST等)都允许重试。默认值为false
  OkToRetryOnAllOperations: false

server:
  port: 9001

consumer


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.ws.rs.core.Response;

import com.example.demo.entity.UserEntity;
import com.example.demo.entity.UserEntityEx;
import org.springframework.web.bind.annotation.ResponseStatus;

import org.springframework.http.HttpStatus;
@RestController

public class ConsumerController {
   private final Logger logger = LoggerFactory.getLogger(ConsumerController.class);
  @Autowired
     private HomeClient homeClient;

 
   @GetMapping("/hello/{id}/{id2}")
     public String hello(@PathVariable(name="id") Integer employeeId,@PathVariable(name="id2") Integer employeeId2) {
      System.out.print(employeeId2);
      
         String message = homeClient.home(employeeId,employeeId2);
         
         logger.info("[eureka-fegin][ConsumerController][hello], message={}", message);
  // log.info("[eureka-ribbon][EurekaRibbonConntroller][syaHello], message={}", message);
         return message ;
         
     }
   
  @GetMapping("/helloEX/{id}/{id2}")
     public String helloEX(@PathVariable(name="id") Integer employeeId,@PathVariable(name="id2") Integer employeeId2) {
      System.out.print(employeeId2);
      
         String message = homeClient.home2(employeeId,employeeId2);
         
         logger.info("[eureka-fegin][ConsumerController][hello], message={}", message);
  // log.info("[eureka-ribbon][EurekaRibbonConntroller][syaHello], message={}", message);
         return message ;
         
  }
  

  
  @GetMapping("/getBlance/{id}")
     public String getblance(@PathVariable(name="id") String employeeId) {
      //System.out.print(employeeId2);
      
         String message = homeClient.getblance(employeeId);
         
         logger.info("[eureka-fegin][ConsumerController][getBlance], message={}", message);
  // log.info("[eureka-ribbon][EurekaRibbonConntroller][syaHello], message={}", message);
         return message ;
         
  }
  
  @GetMapping("/transfer/{id}/{id2}/{price}")
  @ResponseStatus(HttpStatus.OK)
     public String transfer(@PathVariable(name="id") String employeeId,@PathVariable(name="id2") String employeeId2 ,@PathVariable(name="price") Integer price) {
      //System.out.print(employeeId2);
      
         String message = homeClient.transfer(employeeId,employeeId2,price);
         
         logger.info("[eureka-fegin][ConsumerController][transfer], message={}", message);
  // log.info("[eureka-ribbon][EurekaRibbonConntroller][syaHello], message={}", message);
         return message ;
         
     }
  @PostMapping("/user")
   public String aveUser(@RequestBody UserEntity user) {
    return homeClient.aveUser(user);
  }
  @PostMapping("/login")
  @ResponseStatus(HttpStatus.OK)
   public String  login( UserEntityEx user) {
    //String message = Response.status(Response.Status.OK).entity("Entity not found for UUID: " ).build().toString();
    logger.info("[eureka-fegin][ConsumerController][login], message={}", user.toString());
    //System.out.println(message);
    return user.toString();
  }

}

consumer homeclient

package com.example.demo.controller;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import com.example.demo.entity.UserEntity;

@FeignClient(value ="eureka-provider",fallbackFactory=HystrixClientFallbackFactory.class)
public interface HomeClient {
  @GetMapping("/")
  public String home(@RequestParam (value="id", required = false) Integer employeeId,@RequestParam (value="id2", required = false) Integer employeeId2) ;


  
  @GetMapping("/getblance")
  public String getblance(@RequestParam (value="id", required = false)  String employeeId) ;

  @GetMapping("/transfer")
  public String transfer(@RequestParam(value = "id", required = false)  String employeeId,
  @RequestParam(value = "id2", required = false)  String employeeId2 , @RequestParam(value = "price", required = false)  Integer price) ;
  
  
  
  @GetMapping("/ep1")
  public String home2(@RequestParam (value="id", required = false) Integer employeeId,@RequestParam (value="id2", required = false) Integer employeeId2) ;
  @PostMapping("/user")
  public String aveUser(@RequestBody UserEntity user);
}

provider

這邊要注意 我作弊一下,我們是呼叫一個method 然後他就要回傳,現實情況是不能這樣用的因為geth 不管是佈署合約 還是 交易 這些都是需要等待 被打包的,這段時間是不同步的所以應該是

正確呼叫方法應該是這樣,
我這邊作弊先在 一開始就完成 帳號的登入。也就是在 main 函數那邊
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.example.demo.entity.UserEntity;

import springfox.documentation.swagger2.annotations.EnableSwagger2;


import java.io.IOException;
import java.math.BigInteger;
import java.util.concurrent.ExecutionException;

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;

@SpringBootApplication
@EnableEurekaClient
@RestController
//@EnableSwagger2
public class EurekaServiceProviderApplication {
 private final static String PRIVATE_KEY = "eb239949bb46a187b4ff1645a13a9c0c146f7e0890d18426a639ae3add6b690a";
 private  static Web3j web3;
 private static Credentials credentials;
 private static Token exampleContract;
 private static Credentials getCredentialsFromPrivateKeyAddress() throws IOException, CipherException {
  final Credentials credentials = WalletUtils.loadCredentials("654321",
    "F:\\geth\\data\\keystore\\UTC--2020-01-03T05-58-33.219268500Z--21d10c5e114a959e6e92c6f5f0ffcbe7df7785f1");
  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(final Web3j web3j, final Credentials credentials) throws Exception {
  return Token.deploy(web3j, credentials, GAS_PRICE, GAS_LIMIT).send().getContractAddress();
 }

 private static Token loadContract(final String address, final Web3j web3j, final Credentials credentials) {
  return Token.load(address, web3j, credentials, GAS_PRICE, GAS_LIMIT);
 }

 private static void test(final String tmp) {
  System.out.println("hhhhhhhhhhhhhhhhhhhhh" + tmp);

 }

 private static void activateFilter(final Web3j web3j, final String contractAddress, final Token exampleContract) {
  final EthFilter filter = new EthFilter(DefaultBlockParameterName.EARLIEST, DefaultBlockParameterName.LATEST,
    contractAddress);
  exampleContract.setBalanceEventEventObservable(filter).subscribe(log -> test(log.toString()));
  // exampleContract.setBalanceEventEventObservable(filter).subscribe(log->
  // System.out.println("hello"+log.balance));

  web3j.ethLogObservable(filter).subscribe(log -> test(log.toString()));

 }

 @LoadBalanced
 @Bean
 RestTemplate restTemplate() {
  return new RestTemplate();
 }

 @Autowired
 private RestTemplate restTemplate;

 private final Logger logger = LoggerFactory.getLogger(EurekaServiceProviderApplication.class);

 @Value("${server.port}")
 String port;

 @GetMapping("/")
 public String home(@RequestParam(value = "id", required = false) final Integer employeeId,
   @RequestParam(value = "id2", required = false) final Integer employeeId2) {
  final String message = "Hello world" + port + employeeId + employeeId2;

  logger.info("[eureka-provide][EurekaServiceProviderApplication][home], message={}", message);
  return message;
 }

 @GetMapping("/ep1")
 public String home2(@RequestParam(value = "id", required = false) final Integer employeeId,
   @RequestParam(value = "id2", required = false) final Integer employeeId2) {
  final String message = "Hello world" + port + employeeId + employeeId2;

  logger.info("[eureka-provide][EurekaServiceProviderApplication][home], message={}", message);

  // restTemplate = new RestTemplate();
  final String fooResourceUrl = "http://feign-consumer2";
  final ResponseEntity<String> response = restTemplate.getForEntity(
    fooResourceUrl + "/hello2/" + employeeId.toString() + "/" + employeeId2.toString(), String.class);
  // assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
  System.out.println(response.getStatusCode().toString() + (HttpStatus.OK).toString());
  System.out.println(response.getBody() + "test");
  return response.getBody();
 }

 @RequestMapping("/test")
 public String test() {
  final String message = "Hello world ,port:" + port;
  logger.info("[eureka-provide][EurekaServiceProviderApplication][test], message={}", message);
  return message;
 }

 @PostMapping("/user")
 public String aveUser(@RequestBody final UserEntity user) {
  System.out.println(user.getId());
  System.out.println(user.getName());
  System.out.println(user.getAge());
  return "success!";
 }

 @GetMapping("/getblance")
 public String getblance(@RequestParam(value = "id", required = false) final String employeeId) throws Exception {
  final String message = "Hello world" + port + employeeId;

  System.out.println("ok");

  final Object ans = exampleContract.balanceOf(employeeId).send();
  System.out.println(message + ans.toString());
  logger.info("[eureka-provide][EurekaServiceProviderApplication][getblance], message={}",
    message + ans.toString());
  return ans.toString();
 }

 @GetMapping("/transfer")
 
 public String transfer(@RequestParam(value = "id", required = false) final String employeeId,
   @RequestParam(value = "id2", required = false) final String employeeId2,
   @RequestParam(value = "price", required = false) final Integer price) throws Exception {
  final String message = "Hello world" + port + employeeId + employeeId2 + price;
  final Object ans = exampleContract.transfer(employeeId2, BigInteger.valueOf(price)).send();

  logger.info("[eureka-provide][EurekaServiceProviderApplication][transfer], message={}",
    message );
  return message;
 }

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

  activateFilter(web3, Contract ,exampleContract);
  // Boolean result = exampleContract.isValid();
  // if(result == "false")
  //  return
  SpringApplication.run(EurekaServiceProviderApplication.class, args);
 }

}

呼叫 method

建議大家還是夾在 Post 比較 好看也比較安全
transfer
http://localhost:9010/feign-consumer/transfer/0x4d545c6c4f6e56c1defd223a8ff202dde9845b54/0x21d10c5e114a959e6e92c6f5f0ffcbe7df7785f1/1/?token=Bearer%20eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NzgyMTgzMDMsInVzZXJuYW1lIjoiamFjayJ9.q6dHI-rICqtIOlK8c_uGx3JalZEDetgc4iSCTYxanI8

getblance
http://localhost:9010/feign-consumer/getBlance/0x4d545c6c4f6e56c1defd223a8ff202dde9845b54/?token=Bearer%20eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NzgyMTgzMDMsInVzZXJuYW1lIjoiamFjayJ9.q6dHI-rICqtIOlK8c_uGx3JalZEDetgc4iSCTYxanI8

Zuul 跨域配置

加這個就沒有 要求跨域請求了
zuul:
  ignored-headers: Access-Control-Allow-Credentials, Access-Control-Allow-Origin
好了之後 寫一下 vue.js吧

Vue 畫面配置

90行收工
<template> <div> <!-- <h3>Smart contract</h3> <el-input v-model="inputvalue" placeholder="輸入交易 hash" > </el-input> <el-button type="primary" icon="el-icon-search" @click="test3()">query</el-button> --> <h1>ETH DEMO</h1> <h3>account</h3> <el-input v-model="token" placeholder="交易token" ></el-input> <el-select v-model="inputvalue" filterable placeholder="選擇帳戶"> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> <el-input v-model="inputvalue" placeholder="--" ></el-input> <el-button type="primary" icon="el-icon-search" @click="test1()">query</el-button> <h3>UserInfo</h3> <h3>account:</h3> <p>{{ userName }}</p> <h3>blance:</h3> <p>{{ blance }}</p> <h1>Transfer</h1> <h5>Transfer history</h5> <el-input v-model="inputvalue" placeholder="交易帳號" ></el-input> <el-input v-model="targetvalue" placeholder="目標帳號" ></el-input> <el-input v-model="price" placeholder="轉帳金額" ></el-input> <el-button type="primary" icon="el-icon-search" @click="test2()">transfer</el-button> </div> </template> <script> export default { data() { return { userName: "", blance : 0, options: [{ value: '0x21d10c5e114a959e6e92c6f5f0ffcbe7df7785f1', label: '0x21d10c5e114a959e6e92c6f5f0ffcbe7df7785f1' }, { value: '0x4d545c6c4f6e56c1defd223a8ff202dde9845b54', label: '0x4d545c6c4f6e56c1defd223a8ff202dde9845b54' }], value: '', inputvalue: '', targetvalue : '', price : '', token : '' }; }, methods: { test1() { console.log(this.value); this.$data.userName = this.$data.inputcopy this.getRequest("http://localhost:9010/feign-consumer/getBlance/"+this.$data.inputvalue+"/?token="+this.$data.token).then(resp => { this.$data.blance = resp.data; console.log(resp.data); }); }, test2(){ this.getRequest("http://localhost:9010/feign-consumer/transfer/"+this.$data.inputvalue+"/"+this.$data.targetvalue+"/"+this.$data.price+"/?token="+this.$data.token).then(resp => { console.log(resp.data); }); } } }; </script>

啟動

查詢餘額

這邊可以看到504
是我有對 axios 重新包裝


可以看到我們的交易已經成功了
然後挖礦一下


看到 都可以完成扣款,
最簡單的解釋方式什麼是智能合約就是
我跟你互相 簽同一張合約,所有的資料 都寫在上面 ,當然不同的資料就可以衍生出不同的互動方式 像是 以太坊 養貓?
可以來查看一下

我們一開始的合約

這裡面就是在儲存我們智能合約的 變數 都寫在 data 裡