您正在查看: Ethereum 分类下的文章

Heco系统合约分析

官方文档:https://docs.hecochain.com/#/consensus?id=%e7%b3%bb%e7%bb%9f%e5%90%88%e7%ba%a6
合约源代码:https://github.com/HuobiGroup/huobi-eco-contracts

系统合约

目前验证人的管理,均由系统合约完成,目前的系统合约有:

  • Proposal 负责管理验证人的准入资格,管理验证人提案和投票;
  • Validators 负责对验证人进行排名管理、质押和解质押操作、分发区块奖励等;
  • Punish 负责对不正常工作的活跃验证人进行惩罚操作;

区块链调用系统合约

  • 每个块结束的时候,会调用Validators合约,将区块中所有交易的手续费分发给active validator;
  • 发现validator没有正常工作的时候,会调用Punish合约,对validator进行惩罚;
  • 每个epoch结束的时候,会调用Validators合约,根据排名,更新active validator;

质押

任何账户,都可以对validator进行任意数量的质押操作,每个validator的最小质押量是32HT。 如果想取回已质押的HT,需要按照如下操作进行:

  1. 发送调用Validators合约,发送针对某一个validator的解质押(unstake)的声明交易;
  2. 等待86400个块之后,调用Validators合约,发送提取质押(withdrawStaking)的交易,将所有在此validator的质押取回;

惩罚措施

每当发现验证人没有按照预先设定进行出块的时候,就会在这个块结束时,自动调用Punish合约,对验证人进行计数。当计数达到24时,罚没验证人的所有收入。当计数达到48时,将验证人移除出活跃验证人列表,同时取消验证人资格。

合约初始化

系统合约会在第一区块时被全部初始化
(github code)
(github code2)

// Initialize all system contracts at block 1.
if header.Number.Cmp(common.Big1) == 0 {
    if err := c.initializeSystemContracts(chain, header, state); err != nil { // 在此初始化三个系统合约
        log.Error("Initialize system contracts failed", "err", err)
        return err
    }
}

编译合约

修改solc的依赖版本

 // Configure your compilers
  compilers: {
    solc: {
      version: "^0.6.1", // 修改为该版本以上,不然会报找不到对应的版本,此时会下载0.6.12版本

编译合约

truffle compile

此时会在./build/contracts下生成各个合约对应的abi json文件

参考

https://ethereum.stackexchange.com/questions/234/what-is-an-abi-and-why-is-it-needed-to-interact-with-contracts
https://ethereum.stackexchange.com/questions/1437/do-i-need-a-compiled-contract-just-to-get-the-abi-definition
https://zhuanlan.zhihu.com/p/26089385

Heco 编译及多节点部署

运行环境

测试环境
OS: Ubuntu 18.04
Go: go1.15.6 linux/amd64

源代码

git clone https://github.com/HuobiGroup/huobi-eco-chain.git

当前测试 master 分支

编译

cd huobi-eco-chain/
make geth

编译完成后,生成文件在./build/bin/geth

创建 geth.toml

参考 https://github.com/HuobiGroup/huobi-eco-chain-docs/blob/master/dev/deploy.md

[Eth]
SyncMode = "fast"
DiscoveryURLs = []
TrieCleanCacheRejournal= 300000000000

[Eth.Miner]
GasFloor = 8000000
GasCeil = 8000000
GasPrice = 0
Recommit = 3000000000
Noverify = false

[Eth.Ethash]
CacheDir = "ethash"
CachesInMem = 2
CachesOnDisk = 3
CachesLockMmap = false
DatasetDir = "/data/heco/data/.ethash"
DatasetsInMem = 1
DatasetsOnDisk = 2
DatasetsLockMmap = false
PowMode = 0

[Eth.TxPool]
Locals = []
NoLocals = false
Journal = "transactions.rlp"
Rejournal = 3600000000000
PriceLimit = 1
PriceBump = 10
AccountSlots = 16
GlobalSlots = 4096
AccountQueue = 64
GlobalQueue = 1024
Lifetime = 10800000000000

[Node]
DataDir = "/data/heco/data"
InsecureUnlockAllowed = true
NoUSB = true
IPCPath = "geth.ipc"
HTTPHost = "0.0.0.0"
HTTPPort = 8545
HTTPCors = ["*"]
HTTPVirtualHosts = ["*"]
HTTPModules = ['eth', 'net', 'web3']

WSHost = "0.0.0.0"
WSPort = 8546
WSModules = ['eth', 'net', 'web3']

GraphQLVirtualHosts = ["localhost"]


[Node.P2P]
MaxPeers = 50
NoDiscovery = false

ListenAddr = ":32668"
EnableMsgEvents = false

[Node.HTTPTimeouts]
ReadTimeout = 30000000000
WriteTimeout = 30000000000
IdleTimeout = 120000000000

预先创建一个账户地址

使用MetaMask或者其他工具预先创建一个账户地址,并保存好私钥,后面这个地址会配置在初始的Validators中,并用做boot节点启动挖矿coinbase地址

创建genesis.json

参考https://docs.hecochain.com/#/genesis
根据需要修改特定的字段,常规的比如

  • chainId 修改为自己的独有链id
  • extraData 修改为预先创建的地址
  • alloc中的0xdaf88b74fca1246c6144bc846aaa3441ed095191修改为初始资金接收地址,balance根据业务需要,计算相应预先发行数量(精度18位)的16进制

对于共识参数,根据需要再做修改吧

"congress": {
    "period": 3, // 出块间隔(秒)
    "epoch": 200 // 出块顺序刷新间隔(块数)
}

初始化并启动geth

将上面修改好的geth.tomlgenesis.json放到规划好的位置,我们此时假设为/data/heco

初始化geth

在上面目录/data/heco中执行数据初始化

./geth --datadir ./data init genesis.json

我们将链相关数据保存在/data/heco/data下,方便磁盘数据管理,以及后期磁盘空间升级及其迁移

首次启动geth

./geth --config geth.toml --nodiscover

启动后,进入ipc导入我们预先生成的地址私钥,作为此节点的coinbase
从另一个终端进入

./geth attach ipc:/data/heco/data/geth.ipc

在geth命令行中导入预先生成地址的私钥,123456为钱包密码,在此目录创建password文件,并将密码保存在该文件,后面后台启动会需要

personal.importRawKey("e9bc9ae610535514。。。56a272f69fd59d7986b9f4f49c025cc","123456")

并设置该地址为无限期解锁,因为后面需要使用该私钥进行出块签名。0为无限期

personal.unlockAccount("0x48F155527f2。。。7e84692AA0025C0","123456",0)

此时geth命令行中输入eth,查看

> eth
{
  accounts: ["0x48F155527f2。。。7e84692AA0025C0"],
  blockNumber: 4412,
  coinbase: "0x48F155527f2。。。7e84692AA0025C0",

accountscoinbase都为预先生成的地址

后台运行geth

此时关闭前面运行的geth,重新后台运行

nohup ./geth --config geth.toml --unlock "0x48F155527f2。。。7e84692AA0025C0" --password ./password --mine --nodiscover --etherbase 0 2>> ./geth.log &

此时查看geth.log文件

tail -f geth.log

即可看到geth 已经正常单节点出块

测试查询

查看前面预先设置的初始资金接收地址

eth.getBalance("0x48F155527。。。。692AA0025C0")

可查看到,该地址已经存入了balance设置对应的代币

测试转账

eth.sendTransaction({from: "0x48F155527f25EB1。。。。4692AA0025C0" , to: "0x5849cce8b6ea2。。。。7436b628df34217", value: web3.toWei(10,"ether")})

执行完,查看from和to双方账户资金转移正常

MetaMask扩展配置

Chrome浏览器安装好扩展后,进入设置->网络->添加网络,输入相关信息

  • 网络名称 -> 任意信息,只是本地备注
  • 新增 RPC URL -> 该geth运行所在服务器访问地址+端口(端口是上面geth.toml配置的Node->HTTPPort)
  • 链ID -> 上面genesis.json中配置的chainId
  • 符号(选填)-> 该节点的系统代币的符号
  • 屏蔽管理器 URL(选填)-> 汉化翻译不准确,其实就是区块浏览器的地址,后期可以自己拿Etherscan自己部署一套

此时就可以通过MetaMask扩展进行地址代币管理了

多节点部署

如果只是本地测试,单点足以满足需求的话,可以不必继续部署
在其他需要部署节点的服务器上准备好geth节点程序以及上面同样的geth.toml和genesis.json文件,(同一机器多个geth运行实例,修改对应端口和data目录,流程同理)
然后照搬上面的流程的初始化并启动geth后台运行geth
执行完成后会报

WARN [02-24|15:35:05.457] Block sealing failed                     err="unauthorized validator"

是因为此节点的coinbase并不在初始的Validators中,先忽略,等下后面跟进合约动态添加到Validators

数据链接

由于节点启动时关闭了自发现,所以需要我们主动去连接下
两边节点分别查下节点信息

admin.nodeInfo

将enode存下来,将ip地址127.0.0.1修改为当前节点对应的可其他节点访问的地址
节点A

enode://219a81569be3b9f3e261f40e0edbf4893ccde5bc94df6525135eacc337122ef060bad7f220d90622349ab189b87106f26637520f272e5ae73f9797cd6506ddd4@172.17.116.158:32668?discport=0

节点B

enode://425ef46887ac2cb08076bc78e54ba11fb280c9cb2ff6a7bc173001933cfcd2b98b9f49afa3ac3857cc5e49ad01e64595a07c8c2053416390459409da7920a1c9@172.17.116.159:32668?discport=0

然后分别再对方节点上增加自己的节点
节点A执行

admin.addPeer("enode://425ef46887ac2cb08076bc78e54ba11fb280c9cb2ff6a7bc173001933cfcd2b98b9f49afa3ac3857cc5e49ad01e64595a07c8c2053416390459409da7920a1c9@172.17.116.159:32668?discport=0")

节点B执行

admin.addPeer("enode://219a81569be3b9f3e261f40e0edbf4893ccde5bc94df6525135eacc337122ef060bad7f220d90622349ab189b87106f26637520f272e5ae73f9797cd6506ddd4@172.17.116.158:32668?discport=0")

此时A节点正常出块,B节点正常从A节点同步区块,其他节点同理加入

基于ETH的链修改调研

调研背景

目前有个项目计划基于ETH做链修改,完成类似DPOS版本的ETH,
主要是为了整合现有的ETH生态,以及一些商业需求。
也为后面EOS商业链中间层组件(IBC Relayer)做储备。

现有三方项目

https://github.com/HuobiGroup/huobi-eco-chain
https://github.com/meitu/go-ethereum
https://github.com/mingjingc/dpos-go-ethereum
https://github.com/TTCECO/gttc
https://github.com/linapex/ethereum-dpos-chinese

参考文档

https://zhuanlan.zhihu.com/p/38013479
https://www.jianshu.com/p/d99c0a5afbe8

让区块链浏览器收录你的 ERC20 代币

如果想让其他浏览器显示新发的代币,需要主动提交代币信息,对方审核通过后,即可显示。

打开网址https://etherscan.io,在此处输入 token 的智能合约地址,如下图:

搜索后,如下图:

按下图指示,点击“verify and publish”来发布代码,

由于读者已经发布了代码,这里就找了几张图贴出来,以供参考。

步骤一:

步骤二:

步骤三:

若出现如下图,即表示收录成功。

转载:https://my.oschina.net/u/3050295/blog/1982504

Web3j send ERC20 Token Demo

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script src="../../libs/web3.js_v1.0.0-beta.35/web3.min.js"></script>
</head>
<body>

  <h1>Send ERC20 Token</h1>

  <h2>Notes</h2>
  <ul>
    <li>Use MetaMask</li>
    <li>To check ERC20 balance, use <a href="../getERC20TokenBalance/" target="_blank">getERC20TokenBalance</a></li>
  </ul>

  <h2>Token Address</h2>
  <input type="text" id="token-address" size="80" oninput="onAddressChange()"></input>
  <p>e.g. 0x2A65D41dbC6E8925bD9253abfAdaFab98eA53E34</p>

  <h2>Recipients Address</h2>
  <input type="text" id="to-address" size="80"></input>
  <p>e.g. 0x8Df70546681657D6FFE227aB51662e5b6e831B7A</p>

  <h2>Decimals</h2>
  <input type="number" id="decimals" size="40" readonly></input>

  <h2>Amount</h2>
  <input type="number" id="amount" size="40"></input>

  <div><button id="send" onclick="send()">Send ERC20 Token</button></div>

  <h2>Result</h2>
  <span id="result"></span>

  <script>

    var web3js;
    var account;

    function getERC20TokenBalance(tokenAddress, walletAddress, callback) {
      let minABI = [
        {"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"type":"function"},
        {"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"}
      ];
      let contract = new web3js.eth.Contract(minABI, tokenAddress);
      contract.methods.balanceOf(walletAddress).call((error, balance) => {
        contract.methods.decimals().call((error, decimals) => {
          console.log(balance);
          console.log(decimals);
          balance = balance / (10**decimals);
          callback(balance);
        });
      });
    }

    function getERC20TokenContract(tokenAddress) {
      let minABI = [
        {"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"type":"function"},
        {"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},
        {"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"type":"function"}
      ];
      return new web3js.eth.Contract(minABI, tokenAddress);
    }

    function getERC20TokenDecimals(callback) {
      window.tokenContract.methods.decimals().call((error, decimals) => {
        callback(decimals);
      });
    }

    function onAddressChange(e) {
      let tokenAddress = document.getElementById('token-address').value;
      if(tokenAddress != "") {
        window.tokenContract = getERC20TokenContract(tokenAddress);
        getERC20TokenDecimals((decimals) => {
          document.getElementById('decimals').value = decimals;
        });
      }
    }

    function transferERC20Token(toAddress, value, callback) {
      window.tokenContract.methods.transfer(toAddress, value).send({from: account})
      .on('transactionHash', function(hash){
        callback(hash);
      });
    }

    function send() {
      var toAddress = document.getElementById('to-address').value;
      var decimals = web3js.utils.toBN(document.getElementById('decimals').value);
      var amount = web3js.utils.toBN(document.getElementById('amount').value);
      var sendValue = amount.mul(web3js.utils.toBN(10).pow(decimals));
      console.log(sendValue.toString());
      transferERC20Token(toAddress, sendValue, (txHash) => {
        document.getElementById('result').innerText = txHash;
      });
    }

    window.onload = function() {
      web3js = new Web3(Web3.givenProvider);
      console.log(web3js.version);
      var accountInterval = setInterval(function() {
        web3js.eth.getAccounts((error, address) => {
          if (address[0] !== account) {
            account = address[0];
            console.log(account);
          }
        });

      }, 300);
    }

  </script>

</body>
</html>

https://github.com/piyolab/sushiether/commit/e35e949b5d898d357f9bd1d4e3f35b679974f192