EOS 基础

admin
admin 2020年06月09日
  • 在其它设备中阅读本文章

EOS,可以理解为 Enterprise Operation System,即为商用分布式应用设计的一款区块链。EOS 是一个新的高性能区块链平台,可以快速部署高性能和高安全性的区块链应用。有以下特色:

  • 支持免费交易
  • 极快出块速度(0.5s)
  • 短的区块确认(1.5s)
  • WASM 智能合约
  • 账户权限管理
  • 上下文无关交易并行验证

一、EOS 主网启动

初始超级节点和创世节点选取、众筹活动代币冻结

  1. 在 EOS 主网上线之前,超级节点社区 将从成员里面挑选一批志愿参与 上线候选池(Go-Live Pool)的技术精英。这个上线候选池中应该有大概 50 人。
  2. 在上线之前,从候选池随机选出 22 个人组建一个 上线小队(Go-Live team)。然后,他们开始建立高度加密互通的 VPN 连接,以阻挡 DDOS 攻击及其他黑客攻击。
  3. EOS Token 的众筹活动(以太坊 ERC-20 代币合约)截止在 2018 年的 6 月 2 日 22:00,在这一刻,代币将被冻结。最后一刻会生成一个包含所有 EOS Token 数量的快照。
  4. 快照需要被 上线小队 22 名成员中的 15 人确认,随机选择 22 人中的 1 人作为 创世节点 ,其余为初始 超级节点 。创世节点随机一个 EOS 密钥(创世密钥)。

创世节点引导创建 EOS 区块链

  1. 根据之前的包含 EOS Token 数量的快照和创世密钥建立 EOS 区块链上第一个区块(创世区块)。
  2. 利用创世密钥和创世区块,创世节点启动,构建 初始 EOS 区块链网络 ,自动根据创世密钥创建 eosio 系统账号。
  3. 创世节点安装 21 个指定的初始超级节点,这些节点将负责进行首次超级节点投票选举。
  4. 创世节点将系统的算力分配给这 21 个超级节点。
  5. 将系统账号的权限从创世节点交给 21 个超级节点。接下来,创世节点将发布自己刚才使用的创世密钥(现在已无用)。

超级节点选举、初始超级节点替换

  1. 初始超级节点连接初始 EOS 区块链网络,并且对系统账户以及余额进行验证,然后开始生成区块。一旦 21 个指定节点都已经上线,EOS 区块链网络初步启动成功,后面参与进来的人就可以相互连接了。
  2. 上线小队 中的剩余成员可以连接 EOS 区块链,并将自己置于超级节点候选人的位置上。
  3. EOS Token 持币人也可以接入 EOS 区块链,并且用代币来换选票,用选票来进行超级节点的投票。
  4. 初始超级节点的职责只有一个:组织选举,直到 21 个新超级节点产生。21 个当选超级节点将进行几轮的区块生产来组织整个 EOS 主网,而上线小队中剩余的超级节点以候选者身份接入 EOS 主网,他们同样也可以参与 EOS 主网运行。

就此主网已经启动就绪,已当选的 21 个超级节点对主网的普通交易进行处理,开始无尽的候选者选举、当选超级节点等流程。启动过程可以总结为两步:

  1. 创建,配置和启动创世节点
  2. 从单一超级节点过渡到 21 个超级节点

二、账户模型

相比其它的区块链简单的公私钥账户模型,EOS 提供了完善的账户体系,包括自定义账户标识和权限管理等等。

1、钱包

  • 钱包是一个用来存储私钥的地方
  • 使用钱包之前,需要先打开解锁钱包
  • 打开之后的钱包,有两种状态:解锁状态 锁定状态
  • 钱包,是一个超高强度加密的文件。失去钱包密码的话,意味着你丢失了钱包中的私钥

2、账户

类似各个 APP 上用户名,代表你身份的唯一标识,有以下几个特点:

  • 是一个可读的字符串,创建之后,存储在区块链中。
  • 长度为 1 到 12 个字符,字符可以包括小写字母 a -z,数字 1 - 5 和点(.)(最后一个字符除外)。
  • 账户创建者自己选择命名。
  • 由排列组合得出共有 31×(32^n 从 0 到 11 的和)=2^60−1=1,152,921,504,606,846,975 个账户,10^18 级别
  • 私钥控制,且可以更换,所以可以更换私钥来确保账户安全

3、权限

当一个账户创建的时候,具备了两种基本权限。每个权限绑定到一个公钥上(单签名账户)或多个公钥上(多签名账户),除了绑定公钥也可以绑定到另一个有效的账户上:

  • owner:账号主权限, 声明的这个账号的归属。拥有这个权限的私钥需要严格保存,可以用来设置其他权限和其它所有操作。
  • active:活动权限,顾名思议,这个权限一般用来做一些转账、投票、发起事务等常规操作。
    除了以上两种默认权限,还可以对账户自定义新的权限。在 EOS 节点上,账户权限使用数组的方式存储,每一个元素包括三个字段:权限名称、父权限名称、允许的权限,权限可以链接到其父权限,形成以 owner 为根的权限树,这就是在 EOSIO 中允许分层权限的原因。

4、权限的阈值公钥的权重

一个权限的许可需要公钥的权重不小于权限的阈值

  • 单签名账户:就是权限的阈值和钥匙的权重都为 1 的一种账户类型。使用某个权限,只需要一把对应的私钥就行了
  • 多签名账户:一个权限绑定了多个公钥,权限的阈值和每一个公钥的权重可以自定义,使用某个权限,需要多个公钥的许可且权重和不小于阈值

注:这里的许可就是公钥对应的私钥签名

三、资源模型

EOS 网络有 3 种资源可用,即 RAM、CPU 和 NET,这些资源由 21 个超级节点决定和提供。

  • RAM(内存):RAM 是区块链账户和智能合约消耗的重要系统资源之一。RAM 充当永久性存储,用于存储帐户数据,它是有限的持久资源。
  • CPU(中央处理器):代表交易的处理时间,以微秒(μs)为单位。CPU 是一种临时系统资源,一段时间后可恢复
  • NET(带宽):代表传输交易所用带宽,以字节(byte)为单位。NET 也是一种临时系统资源,一段时间后可恢复

RAM

一般被称为内存,用于在 EOS 网络上存储账户相关数据(包括账户名、授权信息、合约代码、合约 abi 和智能合约数据)。RAM 是固定资源,买多少就拥有多少,有多少就可以用多少,没有分配问题,也比较容易计量。RAM 的买卖,实质上花 EOS 从系统账户处购买,而不是买方和卖方直接的交易。不论是购买 RAM,还是卖出 RAM,都是参与者与系统账户之间的交互,该过程将会收取手续费。RAM 的价格是基于 Bancor 算法,可简单理解为市场的供需模型,如果 RAM 供不应求,则买入 RAM 时就需要锁定更多的 EOS,同时,卖出 RAM 也能获得更多的 EOS。如果一笔交易使用的内存超过限制则会被拒绝,另外,对于不再使用的 RAM,可以销毁并卖出。EOS 采用全内存方案,将账户信息和账户数据直接放到超级节点计算机的 RAM 中,加快了合约的处理速度。

CPU 和 NET

通过抵押 EOS 获得,属于可恢复资源。账户每发起一笔交易时,就会减掉一部分 CPU 和 NET,当 CPU 与宽带资源耗尽之后,完全恢复需要 24 小时,这样就限制了账户一段时间内使用的资源总量。CPU 和 NET 是按照质押的 EOS 比例来分配的,每个账户所能获得的资源为:系统总资源 * 抵押代币 / 总的抵押代币。虽然说 CPU 和 NET 的完全恢复周期为 24h,但不代表需要等 24 小时才能使用,其实 EOS 的资源是每时每刻都在恢复的。对 CPU 的计量主要是交易消耗的时间,对 NET 的计量主要是交易的大小,EOS 有 21 个超级节点,每个节点的性能是不一样的,同样的交易,在不同的出块节点执行,消耗的 CPU 时间是不一样的,在这里以出块节点为准,节点将交易打包到区块时,会开一张收据,这张收据记录交易消耗的 CPU 和 NET,其它节点都认这个收据。

对于 EOS 区块链,可以看做一台电脑,所有的账户都可以使用它(执行交易),但是它资源是有限的,所以要限制。内存是静态的(账户一旦写入数据就一直存在,除非数据被销毁了),所以需要花钱购买,不用了可以卖掉获得一部分钱;而处理器和带宽是动态的(现在用掉了一会儿不用就会自动恢复),可以采用抵押的方式,按照抵押的比例获得对应比例的处理器和带宽。通过买卖内存和租用处理器带宽的机制,可以让开发应用的人购买和抵押 EOS 提供服务,供普通用户免费使用。

四、智能合约入门

使用 debian10 搭建环境,项目是 C ++ 构建的,依赖复杂,其它 Linux 平台使用需要安装好依赖包,不同的系统包名可能有所不同。为了方便和主机隔离,使用 docker 作为演示,需要先安装 docker。

1、环境构建

创建并运行 debian:10 镜像

docker run --name eos --restart always -p 8888:8888 -itd debian:10

连接 debian 终端

docker exec -it eos /bin/bash

换源(加速下载包)和更新 ,自带的实在是太慢了

/bin/bash -c 'echo -e "\
deb http://mirrors.163.com/debian/ buster main non-free contrib\n\
deb http://mirrors.163.com/debian/ buster-updates main non-free contrib\n\
deb http://mirrors.163.com/debian/ buster-backports main non-free contrib\n\
deb http://mirrors.163.com/debian-security/ buster/updates main non-free contrib" > /etc/apt/sources.list;\
apt update;\
apt upgrade -y;'

安装所需的包 ,还包括一些必备工具

apt install -y clang cmake git g++ nano curl pkg-config libusb-1.0-0-dev libgmp-dev doxygen libssl-dev libcurl4-openssl-dev libboost-all-dev zlib1g-dev graphviz

编译安装区块链 eos 项目 ,使用 2.0.6 版本

git clone https://github.com/EOSIO/eos.git -b v2.0.6 --depth 1 --recursive --shallow-submodules ~/eosio/eos
mkdir ~/eosio/eos/build && cd ~/eosio/eos/build && cmake .. && make -j$(nproc) && make install

编译安装智能合约 cdt 项目 ,使用 1.7.0 版本

git clone https://github.com/EOSIO/eosio.cdt.git -b v1.7.0 --depth 1 --recursive --shallow-submodules ~/eosio/eosio.cdt
mkdir ~/eosio/eosio.cdt/build && cd ~/eosio/eosio.cdt/build && cmake .. && make -j$(nproc) && make install

安装检查
程序会安装到目录 /usr/local/bin 下,主要包括 nodeos、keosd、cleos、eosio-cpp 等命令, 使用以下命令查看它们

ls -l /usr/local/bin

清理编译缓存 ,缓存很大

rm -r ~/eosio/eos/build ~/eosio/eosio.cdt/build

删除源代码 ,如果编译安装成功,可以删除代码

rm -r ~/eosio

2、钱包和相关秘钥生成

创建钱包,保存密码到~/eosio-wallet/passwd,记住钱包密码,后面需要用来解锁钱包

cleos wallet create -f ~/eosio-wallet/passwd
cat ~/eosio-wallet/passwd && echo

打开钱包 ,查看和解锁钱包

cleos wallet open
cleos wallet list //带有*号为已解锁钱包
cleos wallet unlock //密码为上一步创建时得到的密码

生成开发者密钥 ,创建一个密钥对,私钥用来签名交易,部署合约,公钥用作身份标识

cleos wallet create_key

导入系统密钥 (每一个 eos 链都有一个相同的系统密钥,用于链的治理和共识,它是众所周知的)

cleos wallet import

为了方便,使用这个私钥:5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3

密钥查看 ,命令:cleos wallet private_keys,确保钱包打开并解锁,并输入钱包密码,可以看到创建的开发者秘钥和导入的系统秘钥

password: [[
    "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
    "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"
  ],[
    "EOS7X2xejGqzG5uPVfgz85oks9G4YKDsE5W4Va7rBzyPbrRzpxHqj",
    "5KGvAGENQzPNjaZJ82ofdvMPZU2U6RhQA5PrSobF1v6FwHKvGfM"
  ]
]

3、单节点启动与测试账户生成

启动钱包服务,使用ps -aux命令查看钱包服务是否运行,如果没有,使用以下命令启动服务

keosd &

启动区块链节点服务 ,加载所有基本插件,更多参数信息查看官方文档

nodeos -e -p eosio \
--plugin eosio::producer_plugin \
--plugin eosio::producer_api_plugin \
--plugin eosio::chain_api_plugin \
--plugin eosio::http_plugin \
--plugin eosio::history_plugin \
--plugin eosio::history_api_plugin \
--filter-on="*" \
--access-control-allow-origin='*' \
--contracts-console \
--http-validate-host=false \
--verbose-http-errors >> nodeos.log 2>&1 &

环境检查 tail -f nodeos.log跟踪区块生产日志,按下ctrl + c以关闭日志;cleos get info 输出节点信息;cleos get account eosio,查看 eosio 系统账户,初始创建的默认系统账户,可以看到,公钥是之前导入的系统秘钥的公钥

# tail -f nodeos.log 
info  2020-06-11T07:09:29.903 nodeos    producer_plugin.cpp:2093      produce_block        ] Produced block 4db76dfa2ec0d5be... #5 @ 2020-06-11T07:09:30.000 signed by eosio [trxs: 0, lib: 4, confirmed: 0]
info  2020-06-11T07:09:30.402 nodeos    producer_plugin.cpp:2093      produce_block        ] Produced block b004a1a219686344... #6 @ 2020-06-11T07:09:30.500 signed by eosio [trxs: 0, lib: 5, confirmed: 0]
^C
# cleos get info
{
  "server_version": "c6a7ec0d",
  "chain_id": "cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f",
  "head_block_num": 38,
  "last_irreversible_block_num": 37,
  "last_irreversible_block_id": "000000252e6b2af2fe7a512f21df20bfc5dd1e2ed74f69a5c64d0a9539544fbf",
  "head_block_id": "00000026e8d449cb0057ab3f5a17bb9b2ceb6fc8baf7e0c5b672a6b698d79816",
  "head_block_time": "2020-06-11T07:39:40.000",
  "head_block_producer": "eosio",
  "virtual_block_cpu_limit": 207526,
  "virtual_block_net_limit": 1088100,
  "block_cpu_limit": 199900,
  "block_net_limit": 1048576,
  "server_version_string": "v2.0.6",
  "fork_db_head_block_num": 38,
  "fork_db_head_block_id": "00000026e8d449cb0057ab3f5a17bb9b2ceb6fc8baf7e0c5b672a6b698d79816",
  "server_full_version_string": "v2.0.6-c6a7ec0dd816f98a6840f59dca9fed04efd9f7a5"
}
# cleos get account eosio                                    
created: 2018-06-01T12:00:00.000
privileged: true
permissions: 
     owner     1:    1 EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
        active     1:    1 EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
memory: 
     quota:       unlimited  used:      2.66 KiB

创建测试账户 bob 和 alice,权限公钥使用之前获得的开发者公钥

cleos create account eosio bob EOS7X2xejGqzG5uPVfgz85oks9G4YKDsE5W4Va7rBzyPbrRzpxHqj
cleos create account eosio alice EOS7X2xejGqzG5uPVfgz85oks9G4YKDsE5W4Va7rBzyPbrRzpxHqj

如果不能创建需要先解锁钱包

4、hello 合约示例

创建 hello 合约

mkdir ~/hello && nano ~/hello/hello.cpp

使用下面的示例代码,使用ctrl + cctrl + c保存并退出

#include <eosio/eosio.hpp>

using namespace eosio;

class [[eosio::contract]] hello : public contract {
  public:
      using contract::contract;

      [[eosio::action]]
      void hi( name user ) {
         print( "Hello, ", user);
      }
};

编译 hello 合约 ,会在~/hello 目录下生成同名的 wasm 和 abi 文件

cd ~/hello && eosio-cpp -abigen -o hello.wasm hello.cpp

部署 hello 合约
首先为合约创建 hello 帐户,公钥使用之前获得的开发者秘钥

cleos create account eosio hello EOS7X2xejGqzG5uPVfgz85oks9G4YKDsE5W4Va7rBzyPbrRzpxHqj -p eosio@active

使用set contract子命令部署合约, 合约使用绝对路径

cleos set contract hello /root/hello -p hello@active

调用 hello 合约

cleos push action hello hi '["bob"]' -p bob@active

如果成功,将会输出 Hello, bob

# cleos push action hello hi '["bob"]' -p bob@active
executed transaction: 8e862b47e6cdfb78a0b941ce450a3bfd196fd1bf63351ebc376c1b7cf3d1b989  104 bytes  1331 us
#         hello <= hello::hi                    {"user":"bob"}
>> Hello, bob

5、token 合约示例

获取 token 合约,使用官方提供合约

git clone https://github.com/EOSIO/eosio.contracts -b v1.9.1 --depth 1 ~/eosio.contracts

编译 token 合约 ,注意查看是否生成 abi 和 wasm 文件

cd ~/eosio.contracts/contracts/eosio.token && eosio-cpp -I include -o eosio.token.wasm src/eosio.token.cpp --abigen

部署 token 合约
先创建 eosio.token 账户,公钥使用之前获得的系统秘钥,可能需要先解锁钱包

cleos create account eosio eosio.token EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

部署合约

cleos set contract eosio.token /root/eosio.contracts/contracts/eosio.token --abi eosio.token.abi -p eosio.token@active

调用 token 合约
创建代币,指定了代币发行者,合约所有者才能创建

cleos push action eosio.token create '[ "alice", "1000000000.0000 SYS"]' -p eosio.token@active

发行代币,代币所有者才能发行

cleos push action eosio.token issue '[ "alice", "100.0000 SYS", "memo" ]' -p alice@active

代币转账

cleos push action eosio.token transfer '[ "alice", "bob", "25.0000 SYS", "m" ]' -p alice@active

代币余额查看与验证

cleos get currency balance eosio.token bob SYS
cleos get currency balance eosio.token alice SYS

最终 alice 和 bob 的 SYS 分别是 75 和 25

root@2554169d285c:~/eosio.contracts/contracts/eosio.token# cleos push action eosio.token issue '[ "alice", "100.0000 SYS", "memo" ]' -p alice@active
executed transaction: 58214acda6e872c5003228132e0995107f9678d864bd48c9116cb8367cec8984  128 bytes  766 us
#   eosio.token <= eosio.token::issue           {"to":"alice","quantity":"100.0000 SYS","memo":"memo"}
warning: transaction executed locally, but may not be confirmed by the network yet         ] 
root@2554169d285c:~/eosio.contracts/contracts/eosio.token# cleos push action eosio.token transfer '[ "alice", "bob", "25.0000 SYS", "m" ]' -p alice@active
executed transaction: 34c9daf8587f53e778c19374b3b1dd40a106ad9dd345e5c6388c1c5819fd3a45  128 bytes  1131 us
#   eosio.token <= eosio.token::transfer        {"from":"alice","to":"bob","quantity":"25.0000 SYS","memo":"m"}
#         alice <= eosio.token::transfer        {"from":"alice","to":"bob","quantity":"25.0000 SYS","memo":"m"}
#           bob <= eosio.token::transfer        {"from":"alice","to":"bob","quantity":"25.0000 SYS","memo":"m"}
warning: transaction executed locally, but may not be confirmed by the network yet         ] 
root@2554169d285c:~/eosio.contracts/contracts/eosio.token# cleos get currency balance eosio.token bob SYS
25.0000 SYS
root@2554169d285c:~/eosio.contracts/contracts/eosio.token# cleos get currency balance eosio.token alice SYS
75.0000 SYS

6、注意事项:

  • 钱包保存在~/eosio-wallet目录中,节点数据数据保存在~/.local/share/eosio目录中,区块生成日志保存在~/nodeos.log文件中
  • 上面示例中将钱包密码保存到~/eosio-wallet/passwd 文件中,钱包密码非常重要,需要妥善保存
  • 如果数据和环境弄乱了,密码忘记了,可以清除相关数据重新开始
  • EOS 的账户是需要创建的,光在钱包生成秘钥是不行的
  • 交易不能执行,可能是钱包锁定了,重新解锁即可