搭建ETH私链并创建ERC20 Token

2018-03-12 21:09:34

1、下载ETH钱包

下载 Ethereum Wallet或Mist其一即可,二者功能相同。

2、安装geth

Mac下使用:

brew install geth

其余平台参考安装方法

3、编辑创世区块配置文件

genesis.json:(参考示例

ludis@MacBook  ~  cd Desktop/ethereum
ludis@MacBook  ~/Desktop/ethereum cat > genesis.json
{
  "config": {
    "chainId": 33,
    "homesteadBlock": 0,
    "eip155Block": 0,
    "eip158Block": 0
  },
  "nonce": "0x0000000000000033",
  "timestamp": "0x0",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "gasLimit": "0x8000000",
  "difficulty": "0x100",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "coinbase": "0x3333333333333333333333333333333333333333",
  "alloc": {}
}
^c

4、初始化

初始化区块链,并且创建一个文件夹来存储区块数据。由于第一步下载的钱包,其默认的区块数据存储在~/Library/Ethereum目录,所以初始化时,将存储区块数据的文件夹指向该目录,这样钱包就和我们创建的私链绑定起来:

geth init genesis.json --datadir ~/Library/Ethereum

启动私链

geth --networkid 10 --rpc --rpcapi "admin,debug,eth,miner,net,personal,shh,txpool,web3" rpcaddr "0.0.0.0" --rpccorsdomain "*" --nodiscover  --dev console

终端另开一个窗口,连接本地私链节点,开始挖矿。miner.start(1)传的参数为挖矿所用的线程。

geth attach 'http://127.0.0.1:8545'
miner.start(1)

如果提示挖矿账户错误,则需要设置挖矿账户为自己的钱包地址:

miner.setEtherbase("7df9a875a174b3bc565e6424a0050ebc1b2d1d82")

5、启动钱包

当私链启动后,打开钱包,这是会显示连接到私链成功。点击LAUNCH APPLICATION进入钱包后,创建一个账户即可。

如果打开钱包,显示在同步节点数据,则说明本地私链启动失败,或与钱包绑定失败。

6、创建ERC20 token

创建一个基于ERC20规范的token,合约代码如下:

pragma solidity ^0.4.19;

  // ----------------------------------------------------------------------------------------------
  // Sample fixed supply token contract
  // Enjoy. (c) BokkyPooBah 2017. The MIT Licence.
  // ----------------------------------------------------------------------------------------------

   // ERC Token Standard #20 Interface
  // https://github.com/ethereum/EIPs/issues/20

contract ERC20Interface {
      // 获取总的支持量
    function totalSupply() constant returns (uint256 totalSupply);

      // 获取其他地址的余额
    function balanceOf(address _owner) constant returns (uint256 balance);

      // 向其他地址发送token
    function transfer(address _to, uint256 _value) returns (bool success);

      // 从一个地址想另一个地址发送余额
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success);

      //允许_spender从你的账户转出_value的余额,调用多次会覆盖可用量。某些DEX功能需要此功能
    function approve(address _spender, uint256 _value) returns (bool success);

      // 返回_spender仍然允许从_owner退出的余额数量
    function allowance(address _owner, address _spender) constant returns (uint256 remaining);

      // token转移完成后出发
    event Transfer(address indexed _from, address indexed _to, uint256 _value);

      // approve(address _spender, uint256 _value)调用后触发
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

//继承接口后的实例

contract FixedSupplyToken is ERC20Interface {
    string public constant symbol = "XC"; //单位
    string public constant name = "X Coin"; //名称
    uint8 public constant decimals = 18; //小数点后的位数
    uint256 _totalSupply = 100000000000000000000000000000; //发行总量

    // 智能合约的所有者
    address public owner;

    // 每个账户的余额
    mapping(address => uint256) balances;

    // 帐户的所有者批准将金额转入另一个帐户。从上面的说明我们可以得知allowed[被转移的账户][转移钱的账户]
    mapping(address => mapping (address => uint256)) allowed;

    // 只能通过智能合约的所有者才能调用的方法
    modifier onlyOwner() {
        assert(msg.sender == owner);
        _;
    }

    // 构造函数
    function FixedSupplyToken(string _symbol,string _name) public {
        owner = msg.sender;
        balances[owner] = _totalSupply;
        //symbol = _symbol;
        //name = _name;
    }

    function totalSupply() constant public returns (uint256 totalSupply) {
        totalSupply = _totalSupply;
    }

    // 特定账户的代币余额
    function balanceOf(address _owner) constant public returns (uint256 balance) {
        return balances[_owner];
    }

    // 转移余额到其他账户
    function transfer(address _to, uint256 _amount) public returns (bool success) {
        if (balances[msg.sender] >= _amount && _amount > 0 && balances[_to] + _amount > balances[_to]) {
            balances[msg.sender] -= _amount;
            balances[_to] += _amount;
            // balances[交易所地址] += 1;
            Transfer(msg.sender, _to, _amount);
            return true;
        } else {
            return false;
        }
    }

    //从一个账户转移到另一个账户,前提是需要有允许转移的余额
    function transferFrom(
        address _from,
        address _to,
        uint256 _amount
    ) public returns (bool success) {
        if (balances[_from] >= _amount
            && allowed[_from][msg.sender] >= _amount
            && _amount > 0
            && balances[_to] + _amount > balances[_to]) {
            balances[_from] -= _amount;
            allowed[_from][msg.sender] -= _amount;
            balances[_to] += _amount;
            Transfer(_from, _to, _amount);
            return true;
        } else {
            return false;
        }
    }

    //允许账户从当前用户转移余额到那个账户,多次调用会覆盖
    function approve(address _spender, uint256 _amount) public returns (bool success) {
        allowed[msg.sender][_spender] = _amount;
        Approval(msg.sender, _spender, _amount);
        return true;
    }

    //返回被允许转移的余额数量
    function allowance(address _owner, address _spender) constant public returns (uint256 remaining) {
        return allowed[_owner][_spender];
    }
}

7、创建项目

1. 创建一个node项目,来实现合约的编译、部署功能:

ludis@MacBook  ~  cd Desktop/contract
ludis@MacBook  ~  /Desktop/contract npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (contract)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/ludis/Desktop/contract/package.json:

{
  "name": "contract",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes)

一路回车后初始化了一个node项目,接着安装所需的依赖:

ludis@MacBook  ~/Desktop/contract cnpm i -S ganache-cli solc web3

创建以下目录结构:

.
├── build
├── compile.js
├── contracts
├── deploy.js
├── node_modules
└── package.json

将第六步的合约命名为FixedSupplyToken.sol保存在contracts文件夹

2. compile.js为编译合约用的脚本:

//compile.js 赋值编译智能合约

//require('库名/路径')
const solc = require('solc');
//fs: file system
const fs = require('fs');

const path = require('path');


//1.读取智能合约
//readFileSync 同步读取文件

//__dirname =>返回当前路径
console.log(__dirname);
//resolve路径拼接的方法
const filepath = path.resolve(__dirname,'contracts','FixedSupplyToken.sol');
const contractFile = fs.readFileSync(filepath,'utf8');

// console.log(contractFile);

//通过solc库来编译智能合约

var output = solc.compile(contractFile,1);
// console.log(output);
//output = {contracts:'',errors:'',sourceLiser:''}
const contracts = output.contracts;
// console.log(contracts);
//for in 操作: contract = 对象 中的 属性名
for (let contract in contracts) {
    // console.log(contract)
        //contracts[contract]
        //写文件 fs
        // console.log(contract);
    let newfilepath = path.resolve(__dirname, 'build', contract.replace(':','') + '.json');
    // console.log(newfilepath);
    //fs.writeFileSync(目标路径, 写入内容)
    //对象, tostring()方法, 返回是一个[object object] 字符串
    //JSON.stringify => 将对象装换为字符串
    fs.writeFileSync(newfilepath, JSON.stringify(contracts[contract]));
}
//console.log(contracts);

3. deploy.js为部署合约用的脚本:


const ganache = require('ganache-cli');
const Web3    = require('web3');
const fs = require('fs');
const path = require('path');

// web3 小写是实例, Web3 是类
// web3 的构造函数需要传入一个rpc 地址 => provider(http, websocket)
// 内存虚拟节点
// const web3 = new Web3(ganache.provider());
// 直接通过 http://127.0.0.1:8545链接到自己的节点
const web3 = new Web3('http://127.0.0.1:8545');

// 获取编译后的合约代码
const contractpath = path.resolve(__dirname,'build/FixedSupplyToken.json')
// 解析成为json对象
const contract = JSON.parse(fs.readFileSync(contractpath,'utf8'));

// 构建一个智能合约对象

let myContract = new web3.eth.Contract(JSON.parse(contract.interface));

web3.eth.getAccounts()
.then(accounts => {
  // web3.personal.unlockAccount(eth.accounts[0],"asdqwe123", 15000)
    myContract.deploy({
        data: '0x' + contract.bytecode,// bytecode
        arguments: ["MT", 'My TOKEN'] // 合约构造函数参数s
    })
    .send({
        from:accounts[0],
        gas: 1500000, // gas 消耗最大值
        gasPrice: web3.utils.toWei("0.0000002", "ether")
    })
    .then(d => { // 部署合约的返回结果是实例对象
        d.methods.name().call().then(r => {
            console.log(r);
        });
        // 合约方法调用
        // 合约实例.methods.方法名(方法参数).call()
        d.methods.totalSupply().call().then(r => {
            console.log(r);
        })
        // console.log(d)
    })
})

8、编译部署

编译: node compile.js,编译成功会在build文件夹生成编译完的json文件

部署: node deploy.js,部署时会提示钱包账户解锁问题,只需终端执行web3.personal.unlockAccount(eth.accounts[0],"password", 15000),然后再部署即可。

部署成功后终端挖矿页面会显示区块的打包信息,当合约部署成功时会显示合约地址。然后把合约地址添加到钱包的contracts->token中,就会显示该合约下的token数量。可以新建多个用户转账测试。

DAPP结合IPFS — 去中心化图床

内容:打造一款去中心化图床,用户可上传图片至IPFS上,文件hash保存在以太坊的区块上,以此实现永存的去中心化图床。 技术栈:依旧使用truffle框架快速构建项目truffle unbox react 1、什么是 IPFS 星际文件系统IPFS(InterPlanetary File System)是一个面向全球的、点对点的分布式版本文件系统,目标是为了补充(甚至是取代)目前统治互联网的超文本传输协议(HTTP),将所有具有相同文件系统的计算设备连接在一起。原理用基于内容的地址替代基于域名的地址,也就是用户寻找的不是某个地址而是储存在某个地方的内容,不需要验证发送者的身份,而只需要验证内容的哈希,通过这样可以让网页的速度更快、更安全、更健壮、更持久。 直白了说,就是类似BT下载的p2p文件存储、传输系统。 2、安装 IPFS IPFS官网下载对应系统的安装包(需要翻墙) 以Mac为例,终端执行: cd /Users/ludis/Downloads tar xvfz go-ipfs_v0.4.

Hyperledger Fabric 环境搭建

所有操作在centos7下完成,其他系统(ubuntu/macos..)类似,修改相应指令即可。 1. 安装Golang 在 golang官网 获取最新版本 https://dl.google.com/go/go1.10.1.linux-amd64.tar.gz 下载最新版本 wget https://dl.google.com/go/go1.10.1.linux-amd64.tar.gz 解压 sudo tar -C /usr/local -xzf go1.10.1.linux-amd64.tar.gz 设置环境变量vi ~/.bash_