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

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 --networkid 65525 --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节点同步区块,其他节点同理加入

编译合约

修改solc的依赖版本

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

编译合约

truffle compile

此时会在./build/contracts下生成各个合约对应的abi json文件
其中的"abi"节点的内容,就是下面myetherwallet要用到的abi

合约设置

经过上面的操作,多节点网络已经运行了,下一步进行系统合约设置
以下合约操作通过https://www.myetherwallet.com/interface/interact-with-contract 工具进行交互

申请成为出块节点

选择执行createProposal
参数

  • dst 需要申请添加的地址
  • details 附加信息(选填)

对于genesis的预设地址,默认会在系统初始化时,自动添加到审核通过

审核出块申请

选择执行voteProposal,当前设计,合约签名地址需要为已经通过申请的用户,赞同总数或者拒绝总数,哪个先超过当前正在出块人数的 1/2+1,就执行对应的决定。这个不合理,应该是需要正在出块人中的1/2+1赞同才有效,那些之前通过审核,但不是正在出块的地址,没有审核资格。
参数

取消出块资质

选择执行setUnpassed,目前合约需要当前合约权限才能禁止掉其他人,不合理,后面进行合约修改,同样需要发起申请,通过1/2+1人通过,才会取消对应地址的资格。

创建validator地址

选择执行createOrEditValidator,需要先申请,通过后才允许创建
参数

  • feeAddr 该矿工领取奖励时的接收地址
  • moniker 昵称(选填)
  • identity 身份(选填)
  • website 网址(选填)
  • email 邮箱(选填)
  • details 附加信息(选填)

抵押系统代币

选择执行stake,需要创建完validator,才能进行抵押,抵押量不小于32 ether,后面挖矿奖励会根据当前用户抵押量所占总比例进行分配。

矿工主动提交所得,进行分配

链程序出块后,自动执行distributeBlockReward将打包所得手续费转到合约进行池分配(后期排查,节点程序是否可作恶,抽水),代币总量到合约后,合约根据currentValidatorSet中各自地址抵押总额,以及各自占比进行分配记账(validatorInfo[地址].hbIncoming),(对于合约分配逻辑,后面单独写文章讲解)

领取出块奖励

选择执行 withdrawProfits,参数validator为矿工地址,执行签名地址需要与创建时设置的feeAddr一致,领取时间间隔24小时(28800块)

参考

https://github.com/sigp/lighthouse/issues/2115

基于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

创建自己的数字货币(ERC20 代币)

本文从技术角度详细介绍如何基于以太坊 ERC20 创建代币的流程.

写在前面

本文所讲的代币是使用以太坊智能合约创建,阅读本文前,你应该对以太坊、智能合约有所了解,如果你还不了解,建议你先看以太坊是什么

代币 Token

如果不那么追求精确的定义,代币就是数字货币,比特币、以太币就是一个代币。
利用以太坊的智能合约可以轻松编写出属于自己的代币,代币可以代表任何可以交易的东西,如:积分、财产、证书等等。
因此不管是出于商业,还是学习很多人想创建一个自己的代币,先贴一个图看看创建的代币是什么样子。

今天我们就来详细讲一讲怎样创建一个这样的代币。

ERC20 Token

也许你经常看到 ERC20 和代币一同出现, ERC20 是以太坊定义的一个代币标准。
要求我们在实现代币的时候必须要遵守的协议,如指定代币名称、总量、实现代币交易函数等,只有支持了协议才能被以太坊钱包支持。
其接口如下:

contract ERC20Interface {

    string public constant name = "Token Name";
    string public constant symbol = "SYM";
    uint8 public constant decimals = 18;  // 18 is the most common number of decimal places

    function totalSupply() public constant returns (uint);
    function balanceOf(address tokenOwner) public constant returns (uint balance);
    function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

简单说明一下:

  • name : 代币名称
  • symbol: 代币符号
  • decimals: 代币小数点位数,代币的最小单位, 18 表示我们可以拥有 .0000000000000000001 单位个代币。
  • totalSupply () : 发行代币总量。
  • balanceOf (): 查看对应账号的代币余额。
  • transfer (): 实现代币转账交易,用于给用户发送代币(从我们的账户里)。
  • transferFrom (): 给被授权的用户使用,他可以从我们(参数 from)的账户里发送代币给其他用户(参数 to)。
  • allowance (): 返回授权花费的代币数。
  • approve (): 授权用户可代表我们花费的代币数。

编写代币合约代码

代币合约代码:

pragma solidity ^0.4.16;

interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }

contract TokenERC20 {
    string public name;
    string public symbol;
    uint8 public decimals = 18;  // 18 是建议的默认值
    uint256 public totalSupply;

    mapping (address => uint256) public balanceOf;  //
    mapping (address => mapping (address => uint256)) public allowance;

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Burn(address indexed from, uint256 value);


    function TokenERC20(uint256 initialSupply, string tokenName, string tokenSymbol) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);
        balanceOf[msg.sender] = totalSupply;
        name = tokenName;
        symbol = tokenSymbol;
    }


    function _transfer(address _from, address _to, uint _value) internal {
        require(_to != 0x0);
        require(balanceOf[_from] >= _value);
        require(balanceOf[_to] + _value > balanceOf[_to]);
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        Transfer(_from, _to, _value);
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    function transfer(address _to, uint256 _value) public returns (bool) {
        _transfer(msg.sender, _to, _value);
        return true;
    }

    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(_value <= allowance[_from][msg.sender]);     // Check allowance
        allowance[_from][msg.sender] -= _value;
        _transfer(_from, _to, _value);
        return true;
    }

    function approve(address _spender, uint256 _value) public
        returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

    function approveAndCall(address _spender, uint256 _value, bytes _extraData) public returns (bool success) {
        tokenRecipient spender = tokenRecipient(_spender);
        if (approve(_spender, _value)) {
            spender.receiveApproval(msg.sender, _value, this, _extraData);
            return true;
        }
    }

    function burn(uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);
        balanceOf[msg.sender] -= _value;
        totalSupply -= _value;
        Burn(msg.sender, _value);
        return true;
    }

    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] >= _value);
        require(_value <= allowance[_from][msg.sender]);
        balanceOf[_from] -= _value;
        allowance[_from][msg.sender] -= _value;
        totalSupply -= _value;
        Burn(_from, _value);
        return true;
    }
}

部署

在开发测试智能合约时,MetaMaskRemix Solidity IDE 是两个非常好用的工具,今天就用他们来完成部署。

  1. 安装和配置 MetaMask 请参考开发、部署第一个去中心化应用,不同的上本文选择了以太坊的测试网络 Ropsten,如果你没有余额请点击购买 buy,进入的网站可以送一些测试以太币给你,配置好之后,界面应该如下:

  2. 浏览器打开 Remix Solidity IDE,复制以上源码粘贴上,在右侧选项参考如图的设置:

    注意 Environment 和 Account 和 MetaMask 保持一致,然后选择合约 TokenERC20,填入你想要的发行量,名称及代号,就可以创建合约了。
    这时 MetaMask 会弹出一个交易确认框,点 SUBMIT。待合约部署交易确认之后,复制合约地址。

  3. 打开 Metamask 界面,切换到 TOKENS,点添加合约,出现如下对话框:

    填入刚刚复制的地址,点 ADD,这时你就可以看到你创建的代币了,如图:

    哈哈,你已经完成了代币的创建和部署 (正式网络和测试网络部署方法一样),可以在 Etherscan 查询到我们刚刚部署的代币。可以用它进行 ICO 了,从此走上人生巅峰(玩笑话,不鼓励大家发行无意义的代币)。

代币交易

由于 MetaMask 插件没有提供代币交易功能,同时考虑到很多人并没有以太坊钱包或是被以太坊钱包网络同步问题折磨,今天我用网页钱包来讲解代币交易。

  1. 进入网页钱包地址 , 第一次进入有一些安全提示需要用户确认。
  2. 进入之后,按照下图进行设置:
  3. 连接上之后,如图

    需要添加代币,填入代币合约地址。
  4. 进行代币转账交易

    在接下来的交易确认也,点击确认即可。
  5. 交易完成后,可以看到 MetaMask 中代币余额减少了,如图:

    代币交易是不是很简单,只要明白了交易流程,使用其他的钱包也是一样的道理。

转载自:https://learnblockchain.cn/2018/01/12/create_token/