1 - 什么是以太坊

介绍以太坊公链

目录

  1. 什么是以太坊
  2. 应用场景
  3. 趋势展望

内容

1.什么是以太坊

以太坊是一个全新开放的区块链平台,它允许任何人在平台中建立和使用通过区块链技术运行的去中心化应用。

1.1. 从名字说起

以太坊 == ethereum,

Ether,物理学里面有个以太,绝对静止的存在于空间的那种物质,尚未证明是否真实存在,在某领域里,它被称为虚空,空间…是一种能承载万物的东西,是一种目前还难以解释和观察的东西。

至于这个“坊”字,也不知道是谁先翻译叫起来的,反正现在大家都这么叫。

1.2 从功能说起

以太是区块链网络里很重要的元素 -- 燃料 -- 以太币,为计算付费。

1. 开发人员发布Dapp
2. 用户和Dapp交互
3. 挖矿得到报酬(维持网络稳定的)
4. 金融交易

1.3 从产出

  • 2014年9月份预售阶段发了6千万以太币,(募集到 31,531 个比特币)
  • 其中20%用于以太基金的运作,主要是给开发人员发工资。
  • 挖矿,大概每15秒左右出一个块,作为报酬,出块的人可以得到5个以太
  • 打包叔块的人会得到2-3个以太

自从Byzantium update升级后,现在挖矿和挖到叔块的人会分别得到3和0.625-2.625个以太。

1.4 FAQ

1.4.1 以太比会无限发行么?

不会, 当时预售的时候规定,每年的发行量是1800万上限(被挖到),计划在17年底,会切换共识算法从POW到Casper上,之后出块效率更高,需要的挖矿补贴更少, 具体发行多少,现在还未定。

1.4.2 以太币和比特币的对比?

如果后者,以太币是不可能实现的,在整个数字生态是互补关系,Ether应被视为“加密燃料”,其目的是为计算付费,而不是用作或被视为货币,资产,份额或其他任何东西。

技术层面的对比

1.2 账户

以太坊有两种类型的账户:

  • 外部账户(由私钥控制的)
  • 合约账户(由合约代码控制)。

另种账户都可以发起交易,后者被动发送。

外部所有的账户没有代码,人们可以通过创建和签名一笔交易从一个外部账户发送消息。每当合约账户收到一条消息,合约内部的代码就会被激活,允许它对内部存储进行读取和写入,和发送其它消息或者创建合约。

UTXOs的好处有: 更加私密(用户要是每笔交易都换一个地址,那么就很难找到其中两个地址的相关性);潜在的可扩展性。

账户的好处: 节省大量空间(每笔交易只有一个输入、一个输出、一个签名);更大的可替代性;简单(编码简单,不需要更为复杂脚本);

1.3 以太坊虚拟机EVM

1.4 挖矿

1.5 网络

目前有主链和测试链之分,测试网已经运行到3代了

测试链的区别:

主链:

  1. 应用场景
  • 金融服务:主要是降低交易成本,减少跨组织交易风险等。该领域的区块链应用将最快 成熟起来,银行和金融交易机构将是主力推动者。

  • 征信和权属管理:这是大型社交平台和保险公司都梦寐以求的,目前还缺乏足够的数据 来源、可靠的平台支持和有效的数据分析和管理。该领域创业的门槛极高,需要自上而 下的推动。

  • 资源共享:airbnb 为代表的公司将欢迎这类应用,极大降低管理成本。这个领域创业门 槛低,主题集中,会受到投资热捧。

  • 投资管理:无论公募还是私募基金,都可以应用区块链技术降低管理成本和管控风险。 虽然有 DAO 这样的试水,谨慎认为该领域的需求还未成熟

  • 物联网与供应链:物联网是很适合的一个领域,短期内会有大量应用出现,特别是租 赁、物流等特定场景。但物联网自身的发展局限将导致短期内较难出现规模应用。

  1. 趋势展望

一个是 技术领域也存在着周期律。这个周期目前看是7-8 年左右。或许正如人有“七年之 痒”,技术也存在着七年这道坎,到了这道坎,要么自身突破迈过去,要么往往就被新的技术 所取代。如果从比特币网络上线(2009 年1月)算起,到今年正是在坎上。因此,现在正是 相关技术进行突破的好时机。

为何恰好是7年? 7年按照产品周期来看基本是2-3个产品周期,所谓事不过三,经过2-3 个产品周期也差不多该有个结论了。

另外,最早出现的未必是先驱,也可能是先烈。创新固然很好,但过早播撒的种子,没有合 适的土壤,往往也难长大。技术创新与科研创新很不同的一点便是,技术创新必须立足于需 求,过早过晚都会错失良机。科研创新则要越早越好,最好像二十世纪那批物理巨匠们一 样,让后人吃了一百多年的老本。

最后,事物的发展往往是延续的、长期的。 新生事物大都不是凭空蹦出来的,往往是解决了 前辈未能解决的问题,或是出现了之前未曾出现过的场景。而且很多时候,新生事物会在历 史的舞台下面进行长期的演化,只要是往提高生产力的正确方向,迟早会出现在舞台上的一 天。














QA ?

2 - 搭建以太坊私链网络

利用puppeth搭建POA共识的以太坊私链网络

大量过时文章充斥于网络,本文基于官方go-tehereum 1.6.7版本整理而出,在geth1.6之后引入了一个puppeth工具,它就是用来初始一个私链创世块配置的。

准备工具环境

下载go-ethereum代码(go的开发环境准备,不在此文范围)

# 下载源码
go get github.com/ethereum/go-ethereum
cd $GOPATH/src/github.com/ethereum/go-ethereum

# 编译1.6.7版本的代码
git checkout -b v1.6.7 v1.6.7
make all

# 安装
sudo ln -s $PWD/build/bin/* /usr/local/bin/

# 检查是否安装OK
geth version
    Geth
    Version: 1.6.7-stable
    Git Commit: ab5646c532292b51e319f290afccf6a44f874372
    Architecture: amd64
    Protocol Versions: [63 62]
    Network Id: 1
    Go Version: go1.8.3
    Operating System: linux
    GOPATH=/home/xxp/go
    GOROOT=/home/xxp/Software/go

生成私链创世块的配置

  1. 创建账户
# 创建testnet目录
➜  mkdir testnet && cd testnet

# 创建3个普通账户,密码自定
➜  geth --datadir node0 account new
# 把密码记录到文件里,后面会频繁输入echo node0 > node0/password

➜  geth --datadir node1 account new
➜  echo node1 > node1/password
➜  geth --datadir node2 account new
➜  echo node2 > node2/password

如上打印的一串16进制的字符串就代表账户的userID(理解成网络中的IP地址),后面puppeth需要用到。

  1. 生成genesis文件

genesis文件定义了私链的第一个块生成,直接看操作吧(省略了些输出)

➜  testnet puppeth

Please specify a network name to administer (no spaces, please)
> testnet
Sweet, you can set this via --network=testnet next time!

INFO [08-21|23:04:14] Administering Ethereum network           name=testnet
WARN [08-21|23:04:14] No previous configurations found         path=/home/xxp/.puppeth/testnet

What would you like to do? (default = stats)
 1. Show network stats
 2. Configure new genesis
 3. Track new remote server
 4. Deploy network components
> 2

Which consensus engine to use? (default = clique)
 1. Ethash - proof-of-work
 2. Clique - proof-of-authority
> 2
# 设置5秒出一个块
How many seconds should blocks take? (default = 15)
> 5
# 输入有签名权限的账户
Which accounts are allowed to seal? (mandatory at least one)
> 0x799a8f7796d1d20b8198a587caaf545cdde5de13
> 0x1458eac314d8fc922029095fae20483f55726017
> 0x3ca60eb49314d867ab75a3c7b3a5aa61c3d6ef71
> 0x
# 输入有预留余额的账户
Which accounts should be pre-funded? (advisable at least one)
> 0x799a8f7796d1d20b8198a587caaf545cdde5de13
> 0x1458eac314d8fc922029095fae20483f55726017
> 0x3ca60eb49314d867ab75a3c7b3a5aa61c3d6ef71
> 0x


Specify your chain/network ID if you want an explicit one (default = random)
> 378                

Anything fun to embed into the genesis block? (max 32 bytes)
> 

What would you like to do? (default = stats)
 1. Show network stats
 2. Save existing genesis
 3. Track new remote server
 4. Deploy network components
> 2

Which file to save the genesis into? (default = testnet.json)
> genesis.json
INFO [08-21|23:05:36] Exported existing genesis block 

最后会在当前目录生成genesis.json文件。

启动私链网络

这里通过单机不同端口模拟多节点,

  • 默认geth一启动就会发出discovery,以发现其他节点,源码里内置了几个初始节点,可以通过--bootnode参数重置。如果真要不同节点互组网络的话,还需要主要时间同步,
  • 还可以通过--nodiscover参数,停掉自动发现,并利用admin.addPeer()或者static-node功能组成网络。
# 启动节点0, 关闭自动发现(防止不希望的节点进入)
geth --datadir node0 init genesis.json
geth --datadir node0 --port 30000 --nodiscover --unlock '0' --password ./node0/password console
# 不加console的话,可以通过geth attach ipc:node0/geth.ipc来访问

# 启动节点1,另起一个终端,通过不同端口模拟
geth --datadir node1 init genesis.json
geth --datadir node1 --port 30001 --nodiscover --unlock '0' --password ./node1/password  console

# 启动节点2,genesis.json里已经指定networkID,启动时无需指定了
geth --datadir node2 init genesis.json
geth --datadir node2 --port 30002 --nodiscover --unlock '0' --password ./node2/password  console

目前启动了三个节点,但都默认关闭了发现功能,需要手动添加peer节点。

在每个console输入admin.nodeInfo.enode, 把输出记录下来到static-nodes.json文件,我这里情况如下:

➜  testnet cat node0/static-nodes.json 
[
"enode://2ffb53ede7de8dabf8f12343a7b2aba6b09263a53d8db5b4669309c5913f72969ce469cf09299f13e9d6cba8a98e18ad43811439326d7152f21d2e03ddc6be17@[::]:30000?discport=0",
"enode://910d1bfcd763bb5157bc62f8b121eb21fb305d17e4e4437c0b094d3d6f2d72f1964b80eb8fa2cf6cd7d4cc2d44cfc1ed9b74275ea7fd42ab89b4d089023fb7d5@[::]:30001?discport=0",
"enode://acab97a2a287b740b5efc3af465ba7330b3d4948b05e26818822d1aee659ec1b8f54ee9501576dc08ea4021d7ede01431691a27310a7dcbda2437bcd3b9c451d@[::]:30002?discport=0"
]

把static-nodes.json文件放入各自(node0/,node1/, node2/)的admin.datadir目录下,并Ctrl+D终止掉console,并重新执行geth --datadir <dir> --prot <port> --nodiscover console,如果觉得这样麻烦的话,或者以后动态添加节点时候,可考虑在每个节点的console里输入admin.addPeer("nodeInfo_encode")来完成。

然后在console中可以如下验证,是否互相发现组成以太坊私链网络了

# 这样可看到其他节点的明细
> admin.peers
[{
    caps: ["eth/63"],
    id: "910d1bfcd763bb5157bc62f8b121eb21fb305d17e4e4437c0b094d3d6f2d72f1964b80eb8fa2cf6cd7d4cc2d44cfc1ed9b74275ea7fd42ab89b4d089023fb7d5",
    name: "Geth/v1.6.7-stable-ab5646c5/linux-amd64/go1.8.3",
    network: {
      localAddress: "[::1]:43928",
      remoteAddress: "[::1]:30001"
    },
    protocols: {
      eth: {
        difficulty: 1,
        head: "0xa43519868915a64d3798abf1867b7bc769d1239442c69ff1eca8e3dfcd13209b",
        version: 63
      }
    }
}, {
    caps: ["eth/63"],
    id: "acab97a2a287b740b5efc3af465ba7330b3d4948b05e26818822d1aee659ec1b8f54ee9501576dc08ea4021d7ede01431691a27310a7dcbda2437bcd3b9c451d",
    name: "Geth/v1.6.7-stable-ab5646c5/linux-amd64/go1.8.3",
    network: {
      localAddress: "[::1]:60310",
      remoteAddress: "[::1]:30002"
    },
    protocols: {
      eth: {
        difficulty: 1,
        head: "0xa43519868915a64d3798abf1867b7bc769d1239442c69ff1eca8e3dfcd13209b",
        version: 63
      }
    }
}]
> 
# 可看到此节点发现了另外两个节点
> net.peerCount
2
> 

目前整个testnet的目录结构如下

➜  tree
.
├── genesis.json
├── node0
│   ├── geth
│   │   ├── chaindata
│   │   │   ├── 000002.ldb
|   |   |   ├── ...
│   │   ├── lightchaindata
│   │   │   ├── 000001.log
│   │   │   ├── ...
│   │   ├── LOCK
│   │   └── nodekey
│   ├── geth.ipc
│   ├── history
│   ├── keystore
│   │   └── UTC--2017-08-21T15-03-25.499242705Z--799a8f7796d1d20b8198a587caaf545cdde5de13
│   └── static-nodes.json
├── node1
│   ├── geth
│   │   ├── chaindata
│   │   │   ├── 000002.ldb
│   │   │   ├── ...
│   │   │   └── MANIFEST-000007
│   │   ├── lightchaindata
│   │   │   ├── ...
│   │   │   └── MANIFEST-000000
│   │   ├── LOCK
│   │   └── nodekey
│   ├── geth.ipc
│   ├── history
│   ├── keystore
│   │   └── UTC--2017-08-21T15-03-35.020270645Z--1458eac314d8fc922029095fae20483f55726017
│   └── static-nodes.json
└── node2
    ├── geth
    │   ├── chaindata
    │   │   ├── 000002.ldb
    │   │   ├── ...
    │   │   └── MANIFEST-000007
    │   ├── lightchaindata
    │   │   ├── ...
    │   │   └── MANIFEST-000000
    │   ├── LOCK
    │   └── nodekey
    ├── geth.ipc
    ├── history
    ├── keystore
    │   └── UTC--2017-08-21T15-03-46.899273318Z--3ca60eb49314d867ab75a3c7b3a5aa61c3d6ef71
    └── static-nodes.json

15 directories, 56 files

开始挖矿

在每个节点的console输入如下,启动挖矿(共识记账):

# 这一步可以不需要,因为我们geth启动的时候,已经传入unlock参数了,因为console里执行unlock是有过期时间机制的,私网直接来。。。。
> personal.unlockAccount(eth.coinbase)
Unlock account 0x799a8f7796d1d20b8198a587caaf545cdde5de13
Passphrase: 
true
> 
# 这一个很重要,为后面的eth设置了默认账户
> eth.defaultAccount = eth.coinbase
"0x799a8f7796d1d20b8198a587caaf545cdde5de13"
> 
> miner.start()

通过钱包交易

通过mist钱包界面,来查看基本信息和进行交易,不过之前需要在console开启RPC, node0的console里如下

> admin.startRPC("127.0.0.1", 8545, "*", "eth,net,web3,admin,personal")

启动的mist钱包

mist --node geth --node-datadir ./node0 --rpc http://localhost:8545
# 不启动rpc的话,也可以如下直接通过ipc通信
# mist  --rpc ./node0/geth.ipc  --node-networkid 378 --node-datadir ./node0

mist界面

需要去node1上通过eth.accounts[0]拿到账号信息,填入红框,点击发送交易,输入密码即可

另外也可以通过console命令行来完成上面操作:

> 
> var sender = eth.accounts[0]
undefined
> var receiver = "0x1458eac314d8fc922029095fae20483f55726017"
undefined
> var amount = web3.toWei(10, "ether")
undefined
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x799a8f7796d1d20b8198a587caaf545cdde5de13
Passphrase: 
true
> eth.sendTransaction({from:sender, to:receiver, value: amount})
"0x97ca1f5fa27df083e14b2ffb82c2a60744aeae0f1a7b5e735ca4d0c05c16f7b6"
> 

写个合约

通过mist界面点击的“开发”->"Open Remix IDE", 会自动打开IDE工具,并且默认集成了一个投票的合约,合约内容不表

“create”按钮后面输入 10 -> 点击, 这时候需要输入密码解锁,因为需要支付一定的gas费用。

  • 这个合约就是个投票程序
  • 第一个部署的默认是主席身份,拥有指定别人投票的权利,因为我这里的mist是连接的node0,所以
  • 合约初始化(也就是界面点击“create”时),需要输入"投案"个数

在每个节点的的console里,通过eth.contract(ABI).at(Address);拿到合约对象,

  • 其中node0的节点需要用giveRightToVote给其他账户授权投票权限
  • ABI的信息,就是remix里Interface框对应的信息
  • 合约地址通过Copy address得到

> var b = eth.contract([{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"delegate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"winningProposal","outputs":[{"name":"_winningProposal","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"voter","type":"address"}],"name":"giveRightToVote","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"proposal","type":"uint8"}],"name":"vote","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"_numProposals","type":"uint8"}],"payable":false,"type":"constructor"}]).at('0xc825238a3348f0a679924796fcf1b1b64a8c1706')
undefined

> b.vote(9)
"0x1646e6547606a8ad0e183f1c9145eff08bbdfd860961d6c7d7367f5b70779cbd"
> 
> b.giveRightToVote('0x1458eac314d8fc922029095fae20483f55726017')
"0x2759928ad03a2ed5bc4b9c54531eb83e25c4a468e71682f67b160ad3328c8173"
> 
> b.giveRightToVote('0x3ca60eb49314d867ab75a3c7b3a5aa61c3d6ef71')
"0x46f756e613499f836e392011c8f6d7c23d378fd5a656bae775ecda8bf286c5b6"
> b.winningProposal()
9

其中值得注意的是b.vote() 其中的number不能超过create后传入初始参数10, 其实通过Web3 deploy框的信息也可以直接部署合约。

3 - 以太坊的truffle box开发实战

介绍以太坊私合约开发

整个过程主要演示chrome扩展 METAMASK, OpenZepplin库和truffle框架的使用。

搭建私连网络

主要参考之前的以太坊-私有链搭建初步实践, 这里只用单节点的网络。

还是先准备账户:

mkdir node0
# 会在node0/keystore目录里生成一个keyfile json文件
geth --datadir node0 account new

#利用puppeth生成genesis.json的过程不表,参考上边的链接
geth --datadir node0 init genesis.json

# 把刚才的账号的密码写入node0/password文件
# 启动私链,顺便开启console
echo node0 > node0/password
geth --datadir node0 --port 30000 --nodiscover --unlock '0' --password ./node0/password --mine --rpc --rpccorsdomain "*" --rpcapi "eth,net,web3,admin,personal" console

我们把这个账号的json文件导入到chorme插件metamask里,便于后面调试和演示

ubuntu系统上的chrome插件会有窗口消失的bug,在URL栏里打开chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/popup.html

truflle初始化项目

需要下载truffle命令号

npm install -g truffle

mkdir token && cd token

# 利用trulle下载token代笔示例
truffle unbox tutorialtoken

npm intall zeppelin-solidity

如上必要的依赖框架和库已经下载到了本地, 接下来就创建自己的代币合约

在contract目录创建TutorialToken.sol文件,内容如下:

pragma solidity ^0.4.11;


import 'zeppelin-solidity/contracts/token/StandardToken.sol';


/**
 * @title SimpleToken
 * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator. 
 * Note they can later distribute these tokens as they wish using `transfer` and other
 * `StandardToken` functions.
 */
contract TutorialToken is StandardToken {

  string public name = "TutorialToken";
  string public symbol = "SIM";
  uint256 public decimals = 18;
  uint256 public INITIAL_SUPPLY = 10000;

  /**
   * @dev Contructor that gives msg.sender all of existing tokens. 
   */
  function TutorialToken() {
    totalSupply = INITIAL_SUPPLY;
    balances[msg.sender] = INITIAL_SUPPLY;
  }

}

在以太坊里几乎所有操作都是当做交易来看的,部署合约就是一种交易,交易就要花钱(gas消耗),所以truffle做的是增量部署(少消耗gas),现在在migrations目录添加新的部署文件2_deploy_contracts.js

var TutorialToken = artifacts.require("./TutorialToken.sol");

module.exports = function(deployer) {
  deployer.deploy(TutorialToken);
};

一切准备就绪,编译,部署开始:

# 编译
➜  truffle compile
Compiling ./contracts/Migrations.sol...
Compiling ./contracts/TutorialToken.sol...
Compiling zeppelin-solidity/contracts/math/SafeMath.sol...
Compiling zeppelin-solidity/contracts/token/BasicToken.sol...
Compiling zeppelin-solidity/contracts/token/ERC20.sol...
Compiling zeppelin-solidity/contracts/token/ERC20Basic.sol...
Compiling zeppelin-solidity/contracts/token/StandardToken.sol...
Writing artifacts to ./build/contracts
# 根据truffle.js的配置进行部署
➜  truffle migrate
Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x65ccd2d6a4f4248466dd7887da7a2ac35d18c7ab0ec826cb25580bc785a2c3b8
  Migrations: 0xc64569558f90302f4b3884929ac5540c645674dc
Saving successful migration to network...
  ... 0xf9043ca886d352f05a05642047f63eed11d9b328fb815becc68baffc4d953d60
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying TutorialToken...
  ... 0x19350625474c36316046b103e671eaad45834a60c17a5b9c64cf96316754560f
  TutorialToken: 0x7f469dc1ec17c3b7c52a3ad74611cb4b7e6807e1
Saving successful migration to network...
  ... 0xe57ba56dd5f1b18d410577def8bc7089f7de56e8d8718c3098430995d4b81353
Saving artifacts...

4 - fomo3d-上线部署要点

介绍以太坊私合约fomo3d本地调试要点

fomo3d游戏一出,国内疯狂clone上线,这里谈下我上线的思路和部署方法(纯手动的^_^,落伍了)

通过原版合约地址,可以一层一层的拔下所有涉及到的合约代码。

目前据我统计共有8个合约,其中有两个闭源合约:

  • F3DexternalSettingsInterface
  • JIincInterfaceForForwarder

闭源合约不可怕,看明白什么功能,自己hack掉是不影响游戏本身的。

提前预警,合约的内容细节还是要自己研究的,没时间写太细,

其实这个游戏本身只需要2个合约就可以跑起来,且没实质影响,只是单纯改变了部分利益分配方式。

下面说明,我尽可能少改动原版的情况下,部署上线合约,移除p3d修改后的原版合约代码在这里

部署前的准备

我一般使用在线remix工具部署合约在自己的私链上调试,私链建议如下启动(一键解万忧的方式,推荐创世块采用POA共识-不消耗CPU),这样可以使用remix的debug功能

geth  \
    --datadir ./node0\
    --ws\
    --wsaddr 0.0.0.0\
    --wsapi "eth,net,web3,admin,personal,txpool,miner,clique,debug"\
    --wsport 8546\
    --wsorigins "*"\
    --rpc\
    --rpcapi "eth,net,web3,admin,personal,txpool,miner,clique,debug"\
    --rpccorsdomain "*"\
    --rpcaddr 0.0.0.0\
    --rpcport 8545\
    --rpcvhosts "*"\
    --mine\
    --etherbase 0xdbeb69c655b666b3e17b8061df7ea4cc2399df11\
    --unlock 0xdbeb69c655b666b3e17b8061df7ea4cc2399df11\
    --password ./password\
    --nodiscover\
    --maxpeers '50'\
    --networkid 378\
    --targetgaslimit 471238800\
    &

部署合约

按先后顺序如下部署

  1. p3d合约

真心不推荐部署带有p3d合约的游戏,这样项目方就可以吃掉本来要流到这里25%左右的流水资金了

我对p3d的合约内容还没有很深的研究,只知道它

  • 是一个自带“交易所”、发行总量为0的Token,
  • 通过Eth买入会自动增发,卖出会销毁
  • 买入和卖出都会扣掉10%的费用给仍持有Token的人
  • 每买一次都会使Token升值
  • 每卖一次会使Token降价

这个合约不需要改动,贴源码,编译后部署截图如下,点击红色记录下来部署后的合约地址

p3d

  1. 部署divies合约

这个合约专门往p3d持有者发分红的。

把刚才记录的p3d合约地址,替换到HourglassInterface后面的地址。如上贴源码,编译后部署Divies合约,

记录下divies的地址,并替换fomo3dlong.sol里的DiviesInterface地址

  1. 部署JIincForwarder合约

这个合约是管理流向社区2%的资金的,被fomo3dlong里调用, 这里需要hack,因为其中涉及到一个闭源的合约,既然知道它是管理2%资金流向的,那直接在fomo3dLong的合约如下hack

  • 把定义Jekyll_Island_Inc的地方,直接定义成一个普通地址 address reward = 0xxxxxxx;
  • 把调用Jekyll_Island_Inc的地方, 写成reward.transfer(_com);, 注意有两个地方调用(都要换),一个是游戏进行时调用,一个是本轮结束后调用
        // // community rewards
        // if (!address(Jekyll_Island_Inc).call.value(_com)(bytes4(keccak256("deposit()"))))
        // {
        //     // This ensures Team Just cannot influence the outcome of FoMo3D with
        //     // bank migrations by breaking outgoing transactions.
        //     // Something we would never do. But that's not the point.
        //     // We spent 2000$ in eth re-deploying just to patch this, we hold the 
        //     // highest belief that everything we create should be trustless.
        //     // Team JUST, The name you shouldn't have to trust.
        //     _p3d = _p3d.add(_com);
        //     _com = 0;
        // }

        reward.transfer(_com);

所以不需要部署这个合约,你只要想办法把流到这里的ETH,流到平台方就可以了。(流到开发者,我觉得也是可以的,哈哈~)

  1. 部署Team合约

这个合约利用多签技术限制了影响团队的操作,需要改的地方就是把这些地址全部换成自己的,

把下面这些地址,改成你自己的地址,最好把deployer地址写成你用来部署合约的那个地址,后面调用playbook合约的addGame需要这里的权限

address inventor = 0x18E90Fc6F70344f53EBd4f6070bf6Aa23e2D748C;
address mantso   = 0x8b4DA1827932D71759687f925D17F81Fc94e3A9D;
address justo    = 0x8e0d985f3Ec1857BEc39B76aAabDEa6B31B67d53;
address sumpunk  = 0x7ac74Fcc1a71b106F12c55ee8F802C9F672Ce40C;
address deployer = 0xF39e044e1AB204460e06E87c6dca2c6319fC69E3;

admins_[inventor] = Admin(true, true, "inventor");
admins_[mantso]   = Admin(true, true, "mantso");
admins_[justo]    = Admin(true, true, "justo");
admins_[sumpunk]  = Admin(true, true, "sumpunk");
admins_[deployer] = Admin(true, true, "deployer");

改完后,如上贴源码,编译后部署TeamJust合约,记录地址,替换playbook合约的TeamJustInterface地址

  1. 部署playerBook合约

很有意思的合约,这里就是上面说的整个游戏其实只需要两个合约中的一个。 不解读细节了,直接改吧

你会发现这里怎么还有个JIincForwarderInterface地址,第三步不是说不部署这个了么 ?

这里的主要是收取别人注册名字开启邀请返佣机制时需要支付的那0.01ETH的

知道了这个,就跟第3步一样加个reward收款地址吧,细节不标

如上贴源码,编译后部署PlayBook合约,记录下地址, 替换fomo3d合约里的PlayerBookInterface地址。

  1. 部署fomo3dLong合约

这个是另一个核心合约之一,这里也有个闭源合约,用来初始化控制时间的参数

直接注释掉,然后如下改动

uint256 private rndExtra_ = 30;     // 和rndInit一起控制第一轮游戏开始的初始时间的,单位是秒
uint256 private rndGap_ = 30;         // 和rndInit一起控制下轮游戏开始的初始时间的,单位是秒

还有两个改动点就是activatesetOtherFomo里加上自己的deployer地址,

额外把setOtherFomo里的往另一个游戏池子里输血的功能改到,因为我们没有其他的游戏,如第三步一样,换个收款码吧

otherF3D_.transfer(_long);

部署吧!!!

最后部署这一步,很有可能遇到errored: oversized data的错误,刷新remix页面即可。

合约设置

先setOtherFomo,然后再设置playbook里的addgame,最后activate即可。

页面

页面直接Ctrl+s下载原版界面,把最后的fomo3dLong的合约地址替换下,另外那个后台API,其实没什么,自己试下就知道了,然后就可以上线了。。。

fomo3d上线

感受

  • 合约debug难如上青天
  • 要替换一大堆合约里的地址,除了Interface类的要替成依赖的合约地址,其他的全可以写成你自己地址(即使都一样的也OK)就可以。

目前我们搞出来的定制版有:

  • 多级返佣模式, 可自定义级数
  • 空投fix版
  • 去除战队版
  • 移除p3d版本
  • 原版

5 - fomo3d-钱都去哪儿了

分析太坊私合约fomo3d的数据流向

fomo3d里有战队系统、邀请分佣机制、持key分红、空投系统、持p3d分红等玩法, 相信通过之前各类媒体的解读都有所了解。

下面通过分析合约代码,以讲解ETH数据流向的方式串下所有流程,让大家明明白白的知道自己的ETH都去了哪里。

以10ETH充币到fomod3d合约举例,分三种情况

  • 早期用户(游戏刚启动时的激进者)
  • 中期用户(为了赚分红、返佣的用户)
  • 晚期用户(为了赢48%大奖的人)

早期

当合约被激活后,开发者做了一个很“仇富”的举动,每个地址在合约收到100ETH之前,只能购买1ETH的keys,防止被资本大鳄收割本轮后面入场的玩家。这里有个小hack的点,就是提前多准备些小号,多个地址去投,也可以做到比别人便宜多的价格买到keys。

这个阶段以买入10ETH举例,你只会买到等同于1ETH价值的keys,其余9个ETH会直接进入你的收益里, 演示如下:

ethLimiter2

下面是实现此功能的代码

ethLimiter

代码里的规则(不限阶段)梳理:

  • 提款功能可以无限次提,不影响本轮接下来的分红收益,你的收益来自于你持有keys的分红。
  • 最低可以支付1e-09个Ether,当购买的Key数量大于或者等于1个时,倒计时会加30秒。
  • 当支付的eth不小于0.1时,会送一次“彩票”,买key支付的金额越大,中奖的奖金也越大,最大可中“彩票池”里额度的75%,直译过来这个功能叫空投。

中期

所有阶段的用户如果是直接打开的官网,充币买keys时会触发合约的这个接口,

buyXaddr

其中_affcode是值邀请人的地址,_team是指用户所有购买key所选的战队,默认的2是指蛇队。

如果是从别人的邀请进入的官网,要看邀请人给你发的是哪个链接,有三种形式的链接:

affiliate,

从上到下,分别会走buyXaddrbuyXidbuyXname的接口,比如我给人发了exitscam.me/xxp的邀请链接,被邀的人买keys时会触发如下接口:

affiliate2

这其中我个人会收到他买key总额度的10%佣金,这里还有个隐藏的点:

如果用户是直接从官网进入买key的,那同样会有10%佣金的产生,只不过是流向p3d的持有者。

晚期

当有人买key时,都会选择一个战队,默认会被勾选蛇队的,当买到keys数量不小于1个时,会使所选战队成为本轮的潜在获胜队。

说了这么多废话,回归正体,你的10ETH到底去了哪里???

如果支付10ETH时,选的是蛇队,你10个ETH里的5.6个会被持keys的人均分,1个看情况是给p3d的人还是给邀请你的人,还有1个必定会分给持有p3d的人,另外2个会进入大池子,其中0.2个会分给社区贡献人,0.1个会给TeamJust的另一个游戏合约,还有0.1个会流到“彩票池”里。

这里面根据你选的战队不通,分配比例不一样,具体看下的代码,执行这些ETH分配的是走distributeExternaldistributeInternal 出去的。

后面的PotSpit是本轮游戏结束后,如何分配大池子里的金额。

// Team allocation structures
// 0 = whales
// 1 = bears
// 2 = sneks
// 3 = bulls

// Team allocation percentages
// (F3D, P3D) + (Pot , Referrals, Community)
// Referrals / Community rewards are mathematically designed to come from the winner's share of the pot.
fees_[0] = F3Ddatasets.TeamFee(30,6);   //50% to pot, 10% to aff, 2% to com, 1% to pot swap, 1% to air drop pot
fees_[1] = F3Ddatasets.TeamFee(43,0);   //43% to pot, 10% to aff, 2% to com, 1% to pot swap, 1% to air drop pot
fees_[2] = F3Ddatasets.TeamFee(56,10);  //20% to pot, 10% to aff, 2% to com, 1% to pot swap, 1% to air drop pot
fees_[3] = F3Ddatasets.TeamFee(43,8);   //35% to pot, 10% to aff, 2% to com, 1% to pot swap, 1% to air drop pot

// how to split up the final pot based on which team was picked
// (F3D, P3D)
potSplit_[0] = F3Ddatasets.PotSplit(15,10);  //48% to winner, 25% to next round, 2% to com
potSplit_[1] = F3Ddatasets.PotSplit(25,0);   //48% to winner, 25% to next round, 2% to com
potSplit_[2] = F3Ddatasets.PotSplit(20,20);  //48% to winner, 10% to next round, 2% to com
potSplit_[3] = F3Ddatasets.PotSplit(30,10);  //48% to winner, 10% to next round, 2% to com

还有很多细节要分享,碍于时间有限,不过我会持续更新这里的

感想

  • 持有p3d的人和早期进入的才是最大的受益者
  • 后期进入的人只有通过拉人赚佣金的方式回本了
  • 这轮游戏应该是结束不了的: 总有人赔了,要拉人进来捞本,被拉的人周而复始。。。
  • 结束只有两个可能: 1. 合约有重大漏洞,资金被盗 2. 当大池子里48%的收益足以对整个以太网络发动51%攻击。。。
  • 矿工在背后偷着乐
  • 你们谁知道TeamJust的下个游戏的合约地址么? 我知道!!!

如果你也找到了,可以加我微信yiyemeishui, 加好友时请输入TeamJust的下个游戏合约地址,我们一起来票大的。。。

6 - 解读cosmos-sdk系列(1)

分析解读cosmos跨链方案

通过本系列,可以了解tendermint共识和cosmos-sdk架构的设计思想,并学习到如何通过Cosmos-SDK来快速开发自己的区块链应用。

cosmos团队把区块链分成了三层

  • 网络层 - p2p负责广播交易
  • 共识层 - 对哪些交易打包进块形成共识
  • 应用层 - 执行交易,负责交易结果落盘(状态一致)

这里的应用层可能会有误解,并非是Dapp层,对于SDK底层的Tendermint来说,除p2p网络和打包块共识外,其他都算是应用部分, 拿实现比特币公链的例子来讲,应用部分就是维护账户的UTXO数据库,如果对比以太的话,keystore账户和EVM虚机部分就是应用范畴,所以SDK内置了账户、质押、治理、权限等应用模块,可以帮助我们简单地实现底层链的开发。

可以把这几层简单理解成各节点通过同步交易集(块)日志,实现数据(状态)一致性。数据库的主从模式不也是同步binlog日志,各自执行(replay,回放)日志后,实现数据(状态)最终落盘,区块节点本身同步块的时候,默认就是去下载交易日志,把执行结果按照逻辑链的形式写入本地leveldb的,然后才能对外提供各类RPC服务。

tendermint共识

为后续更好的利用cosmos-sdk,要先了解下Tendermint。

Tendermint Core 提供了网络和共识层功能,而应用层要通过ABCI协议和Core互通消息msg,简单讲tendermint负责起一个replication engine进程,而应用层要运行一个state macheine进程,进程间通过ABCI消息来通信。

ABCI协议的消息体用protobuf定义在这里,app侧可以响应的request如下:

message Request {
  oneof value {
    RequestEcho echo = 2;
    RequestFlush flush = 3;
    RequestInfo info = 4;
    RequestSetOption set_option = 5;
    RequestInitChain init_chain = 6;
    RequestQuery query = 7;
    RequestBeginBlock begin_block = 8;
    RequestCheckTx check_tx = 9;
    RequestDeliverTx deliver_tx = 19;
    RequestEndBlock end_block = 11;
    RequestCommit commit = 12;
  }
}

ABCI的设计主要有以下几个特点:

  • 消息协议

    • 成对出现的消息: request/reponse
    • tendermint发起Request, app来响应
    • 使用protobuf定义
  • server/client

    • tendermint运行client
    • app侧运行server端
    • 可以由TSP(支持checkTx和DeliverTx消息的异步处理)、grpc两种方式实现
  • 区块相关

    • abci是面向连接的
    • tendermint会创建三个socket连接来和app通信,分别是Mempool, Cosensus, Query连接
      • Mempool连接: 钱包客户端发起交易,会首先进入钱包后台连接的节点的local mempool,该节点通过发送checkTx消息来通知app,去检验交易签名是否有效等等,如果OK节点则会p2p广播该交易到其他节点的mempool里。

      • Cosensus连接: 出块节点从mempool的交易集里选出一个块提案(proposer),之后会经过3阶段提交(pre-vote, pre-commit, commit)处理,这个块才能说达成共识(上链了),,只有块被commited了,app侧才会更新状态,比如改变某地址余额等等。app更新状态的时候,是通过Core发送BeginBlockDeliverTx ...EndBlock, Commit消息给app侧来完成的,任何写入操作都是通过此连接完成的。

      • Query连接: 主要负责一些和共识无关的查询操作,比如块信息,地址余额等等,主要用到Info, SetOption, Query消息

整个abci的信息流大致如下: abci

关于Tendermint和app间数据流的更多细节见下:

dataFlow

高清图地址

总结

可以理解成,Tendermint主要负责在BFT环境下同步app间的块交易日志,无论任何交易类型,只要交易块的执行结果是确定性的(唯一性),Tendermint就可以为我们形成区块的共识。

比如说,一个交易的内容在合约里创建一个真随机数,这种交易,tendermint是无法为我们形成共识的,因为多个节点的执行结果是不一样的, 因为这个结果是要在下个区块头的,这样就无法对下个区块形成共识了,所有节点都认为对方在恶意“搞分叉”了。

目前基于tendermint的项目有很多:

我个人看到好的是BigchainDB和超级账本Burrow项目,更多可以看这里

后续源码介绍,如何基于tendermint创建一个区块链

7 - farbic-搭建高并发交易网络

联盟链改造方案

针对每秒数千笔交易的场景,默认的CCVC(并发控制版本检查)会导致交易失败率的上升,其实不需要对基础网络本身做特殊设置,从合约代码入手可以解决,参考官方例子farbic-samples.

下载项目

基于目前最新的v1.0.3版本来说

git clone https://github.com/hyperledger/fabric-samples.git
cd fabric-samples/first-network

未完...

8 - fabric-示例集群化操作

介绍fabric示例中的集群化操作

fabric给出的cc样例都是跑在docker-compose上,这里介绍利用已有的docker-compose.yaml如何集群化运行。

准备样例CC

以官方fabric-samples项目里的balance-transfer为例,准备拆分运行在4个虚机里。

git clone https://github.com/hyperledger/fabric-samples.git
cd fabric-samples/balance-transfer

本CC的示例,主要由3个部分组成:

  • 2 CAs
  • 1 SOLO orderer
  • 4 peers (2 peers per Org)

其中artifacts目录里放置了:

  • cryptogen 工具生成的证书信息,后面运行时需要挂载到各自的peer节点里
  • configtxgen 工具生成的初始块 genesis.block 和 channel配置信息mychannell.tx

准备集群

根据上面的情况,下面准备四个虚机来集群化操作, 虚机规划信息如下:

  • air13, 192.168.10.78, 运行ca1,ca2, 这是我的本机
  • node0, 192.168.10.110, 运行orderer
  • node1, 192.168.10.114, 运行org1的peer0、peer1
  • node2, 192.168.10.115, 运行org2的peer0、peer1

每个虚机都预先安装docker和docker-compose

修改artifacts/docker-compose.yaml文件,在每个service下添加如下信息:

    extra_hosts:
      - "ca.org1.example.com:192.168.10.78"
      - "ca.org2.example.com:192.168.10.78"
      - "orderer.example.com:192.168.10.110"
      - "peer0.org1.example.com:192.168.10.114"
      - "peer1.org1.example.com:192.168.10.114"
      - "peer0.org2.example.com:192.168.10.115"
      - "peer1.org2.example.com:192.168.10.115"

此举的作用是在每个容器的/etc/hosts文件里,添加上面的映射,最终docker-compose.yaml文件放置gist上了.

最后一步,因为此CC样例运行时,需要挂载本地目录里一些提前生成好的证书,我们还需要把这么需要挂载的东西,同步到每个虚机里,如下操作:

rsync -az balance-transfer root@192.168.10.110:~/
rsync -az balance-transfer root@192.168.10.114:~/
rsync -az balance-transfer root@192.168.10.115:~/

集群化运行

ssh进入虚机,按照规划启动各自的服务

# 进入air13虚机
cd balance-transfer/artifacts/
docker-compose up --no-deps ca.org1.example.com ca.org2.example.com

# ssh进入node0虚机
cd balance-transfer/artifacts/
docker-compose up --no-deps orderer.example.com

# ssh进入node1虚机
cd balance-transfer/artifacts/
docker-compose up --no-deps peer0.org1.example.com peer1.org1.example.com

# ssh进入node2虚机
cd balance-transfer/artifacts/
docker-compose up --no-deps peer0.org2.example.com peer1.org2.example.com

如上fabric的区块链网络已经集群化运行了

运行APP并测试CC

在虚机air13上运行我们的前端APP,并测试在网络集群化后的的CC,

此外由于此APP样例的特殊性,还需要修改其他的地方

  • config.json文件里的orderer地址改为"grpcs://192.168.10.110:7050"
  • app/network-config.json里指定地址的地方需要改成这样

进入balance-transfer目录,如下执行:

$ npm isntall
$ PORT=4000 node app

这样前端APP就运行起来了,接下来利用测试脚本testAPIs.sh测试下我们的CC,运行前,还需要修改下脚本里的peer地址,修改后的文件在这里

运行脚本:

./testAPIs.sh
POST request Enroll on Org1  ...

{"success":true,"secret":"DbzvtdmATgve","message":"Jim2 enrolled Successfully","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDE4NzQ5MjUsInVzZXJuYW1lIjoiSmltMiIsIm9yZ05hbWUiOiJvcmcxIiwiaWF0IjoxNTAxODM4OTI1fQ.LUmfEstviquaD5k5oBMd9KUFaKF1s6ZMY8iO67dKiH0"}

。
。
。

GET query Installed chaincodes

["name: mycc, version: v0, path: github.com/example_cc"]

GET query Instantiated chaincodes

["name: mycc, version: v0, path: github.com/example_cc"]

GET query Channels

{"channels":[{"channel_id":"mychannel"}]}

Total execution time : 13 secs ...

本样例修改后的完整地址在这里

总结

目前集群化后网络里的peers ledger和orderer的数据还是存储在容器里,是否要考虑挂载出来,不然容器挂了再启动后,数据是否会乱了,后续研究下。

后续还会继续搞下CA,MSP和动态添加节点以及合约迭代升级等问题。

9 - farbic-区块链的生产集群化

介绍fabric示例中的生产中的集群化操作

默认社区的demo是基于docker-compose给出的,达到了“一键部署”的效果,但生产上考虑多节点的情况,还需要费些手脚,这里考虑用kompose结合k8s来做这件事。

k8s集群 1.7的初始化

每个节点都要安装docker的步骤,此处略过不表,这里主要介绍利用kubeadm初始化k8s集群,这里不考虑k8s集群本身的高可用,以前有文章专门介绍过。

apt-get update && apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet kubeadm
# 默认会自动安装这些包 ebtables kubeadm kubectl kubelet kubernetes-cni socat

如果你本机是centos的话,可以用如下命令安装

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
        https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
setenforce 0
yum install -y kubelet kubeadm
systemctl enable kubelet && systemctl start kubelet

上面的命令,需要翻墙才能跑通,没条件的可以去release项目自己编译deb包或者rpm包,如下运行

git clone https://github.com/kubernetes/release.git && cd release

# debian系如下
docker build --tag=debian-packager debian
docker run --volume="$(pwd)/debian:/src" debian-packager
# docker run -e "HTTPS_PROXY=127.0.0.1:8118" --net=host --volume="$(pwd)/debian:/src" debian-packager
# 默认debs包在目录debian/bin/stable/xenial下

# centos系的如下
cd rpm
./docker-build.sh
#默认rpm包在目录output/x86_64/下

必要依赖搞到手后,就可以简单的利用kubeadm启动集群了 在master节点上如下执行初始化,此过程会启动 etcd,controller-manager,scheduler,api-server组件

kubeadm init --kubernetes-version stable-1.7 --pod-network-cidr=10.244.0.0/16

在其他节点上执行join操作 现在必须要加上--node-name参数,不然报错误,这是个bug

kubeadm join --token 4cc663.c4d99a546c9f3974 192.168.10.78:6443 --node-name node-0

默认还需要启动pod network,我默认用的flannel。

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel-rbac.yml

另外默认master节点是不会被调度容器的,如下可放开限制

kubectl taint nodes --all node-role.kubernetes.io/master-

默认我们得到如下的集群状态

➜  ~ kubectl get no
NAME      STATUS    AGE       VERSION
air13     Ready     2h        v1.7.2
node-0    Ready     17m       v1.7.2

准备fabric的k8ss所需yaml文件

这里需要用到下载我改过的kompose工具,默认官方的对hostpath的处理,需要引入PV,PVC,虽然这样无可厚非,但对与现阶段的我增加了不必要的复杂度,就动手加了个--hostpaths的选项,

# 下载我改过的工具项目-kompose
git clone https://github.com/xiaoping378/kompose
cd kompose && make
# 如上会编译出kompose来,自己搞到$PATH里

这里以fabric-samples项目里的balance-transfer为例,演示一个完整的CC运行在k8s上。

下载fabric-samles项目,

git clone https://github.com/hyperledger/fabric-samples.git
cd fabric-samples/balance-transfer

kompose convert -f artifacts/docker-compose.yaml -d --hostpaths
# 如上会在当前目录出现批量的deployment和service yaml文件,这里需要针对hostpath的volumes稍作修改

sed -i 's/.\/channel/\/root\/balance-transfer\/artifacts\/channel/' *-deployment.yaml
#如上改成绝对路径,另外还需要保证各节点都要有channel目录
rsync -avz balance-transfer root@node-0:~/

因为ca, peer, orderer都需要从本地读取证书相关的信息,所以要把各节点利用nodeSelector特性绑定到指定的节点上,这一点以后得改掉,利用env来动态生成(待验证)

#比如让要在特定节点调度特定容器,需要如下操作
kubectl label node node0 ca=ture

还要在对应的deployment文件做如下操作

nodeSelector

-- 未完待续。。。