- eos 所有的代币都通过智能合约控制,包括 EOS。目前所有的代币的交易方法应该都是基于 eosio.token 智能合约,所以操作方法一致。
-
eos 使用 action 来操作所有的链上数据,包括代币交易。action 需要签名,签名可以使用 ecc-secp256k1 或 ecc-secp256r1 曲线。 eos 通过 transaction(事务)来发送 action, action 可以有多个。
-
action 的调用方法为:智能合约账户名 + 函数接口名 + 接口参数
-
eos 使用 action 前需要先抵押一定的 eos 来换取 RAM,Network,CPU 资源。RAM 是持久资源,以当前市场价购买,卖出时也以当前市场价卖出;Network 和 CPU 资源限额24小时会重置,但只有新发起一笔操作后才会恢复。Network 和 CPU 资源赎回需要72小时后才能到手。发送 action 需要消耗Network 和 CPU。注册账户需要小量 RAM。抵押方法也是通过发送 transaction。cpu跟带宽的占用数据,只有在新发起一笔交易之后才会更新。
-
eos 使用前需要先注册账户,方法是提交特定的 action,将指定公钥与账户名绑定。之后所有的操作都用账户名而不是公钥。账户名限制“.12345abcdefghijklmnopqrstuvwxyz”,12个字符以内,不可重复。目前只可注册 12个字符长的账号,其他账号以后可能以拍卖形式出售。目前不可注册带”.”的账号,只有系统账号可以带”.”
-
eos 账户有权限等级体系,可以分配子权限给其他密钥。密钥派生一般使用 slip48 协议(对应 BTC 的 BIP44)。目前 eos 默认是 owner 和 active 两种权限,owner 可以更改 owner 和 active 对应的公钥,active 可以做除了更改公钥之外的所有操作。用户可自行增加下级权限。
-
eos使用小端存储。
-
eos transaction 格式:
{
expiration: '2018-10-07T16:29:39',
ref_block_num: 55567,
ref_block_prefix: 637338379,
max_net_usage_words: 0,
max_cpu_usage_ms: 0,
delay_sec: 0,
context_free_actions: [],
actions:
[ {
account: 'eosio.token',
name: 'transfer',
authorization: [ { "actor": "inita", "permission": "active"} ],
data: '202902f2520e91c3000000008093dd747011010000000000045359530000000000' } ], // 序列化后数据。原数据 { "from": "inita", "to": "initb", "quantity": "7.0000 SYS", "memo": ""}
transaction_extensions: [] },
signatures:
[ 'SIG_K1_KZQRFeGgN5EFctHBqhuV6u24Ewzme4TEnhnw8dEg5SVhkLes9kxcFWanYahdgt9N6LFJCKvREDabhGY5C1PGvhqj1uyJVD' ]
}
-
eos transaction 序列化:
eos 使用 eosjs-fcbuffer 进行序列化和反序列化,eosjs-fcbuffer 是 fcbuffer 的 fork 仓库,之前没太多人用。c++ 版本叫 fc。
序列化字段:
{
"expiration": 1538569524, // 需要转换为时间戳
"ref_block_num": 56170,
"ref_block_prefix": 3374189397,
"max_net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio.token",
"name": "transfer",
"authorization": [
{
"actor": "inita",
"permission": "active"
}
],
"data": { // 被序列化前的数据
"from": "inita",
"to": "initb",
"quantity": "7.0000 SYS",
"memo": ""
}
}
],
"transaction_extensions": []
}
这些字段对应的类型为:
{
"expiration": uint32,
"ref_block_num": uint16,
"ref_block_prefix": uint32,
"max_net_usage_words": varuint32,
"max_cpu_usage_ms": uint8,
"delay_sec": varuint32,
"context_free_actions": vector,
"actions": [ // vector
{
"account": name,
"name": name,
"authorization": [ // vector
{
"actor": name,
"permission": name
}
],
"data": { // data
"from": name,
"to": name,
"quantity": asset,
"memo": string
}
}
],
"transaction_extensions": vector
}
各字段的转换规则为:(小端存储)(根据 eosjs 和 eosjs-fcbuffer 源码得到)
- uint8,uint16,uint32:正整数
-
varuint32:变长正整数,1-4个字节。每个 byte 的 [0, 6] 个 bit用于存储数据,最后一个bit用于是否是最后一个bit(0 是 1 不是)
-
vector:列表,size(varuint32) + {data + data + …}(size 个)
-
name:base32 编码,定长8个字节。因为编码前 name 限制了长度为12,base32 占用5个bit,5 * 12 = 60 < 8 * 8
-
data:特殊类型,length(varuint32) + {from, to, quantity, memo}(length 长度)
-
asset:特殊类型,分为三个部分:amount,precision,symbol。
- amount 类型为 uint64,amount = value * 10 ^ precision,如7则为0700000000000000, 7.0000则为7011010000000000。
- precision 类型为 uint8,应小于18(否则amount 可能溢出)。
-
symbol 长度为固定 7 字节,ASCII 编码
-
string:varuint32(length) + UTF8encode(string)
序列化结果:
// expiration ref_block_num ref_block_prefix
34b5b45b 6adb 550b1ec9
// max_net_usage_words max_cpu_usage_ms delay_sec context_free_actions
00 00 00 00
// actions [{
// account
// name
// authorization [{
// actor
// permission
// }]
// }]
01 00a6823403ea3055 000000572d3ccdcd
01 000000000093dd74 00000000a8ed3232
// data: {
// from
// to
// quantity{amount, symbol}
// memo
// }
21
000000000093dd74
000000008093dd74
7011010000000000 04 53595300000000
00
transaction_extensions
00
// result:
34b5b45b6adb550b1ec9000000000100a6823403ea3055000000572d3ccdcd01000000000093dd7400000000a8ed323221000000000093dd74000000008093dd74701101000000000004535953000000000000
- eos 签名
mainChainId = aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906 // 主网 eos 链 chainId
jungleChainId = 038f4b0fc8ff18a4f0842a8f0564611f6e96e8535901dd45e43ac8691a1c4dca // 测试网 jungle eos 链 chainId
sysChainId = cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f // 本地 eos 链默认 chainId
privateKey = 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 // base 58
// mainnet header privateKey using compressed pubkey(01 if exist) checksum(sha256 ^ 2)
// 80 D2653FF7CBB2D8FF129AC27EF5781CE68B2558C41A74AF1F2DDCA635CBEEF07D AA08644A
chainIdBuf = mainChainId
buf = 34b5b45b6adb550b1ec9000000000100a6823403ea3055000000572d3ccdcd01000000000093dd7400000000a8ed323221000000000093dd74000000008093dd74701101000000000004535953000000000000 // transaction 序列化结果
packedContextFreeData = 0000000000000000000000000000000000000000000000000000000000000000 // context_free_actions 的对应数据,eos 官方库是写死的,暂无作用
// 待签名 data
signBuf = chainIdBuf + buf + packedContextFreeData
// aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e90692b4bc5bd2d8d970a04b000000000100a6823403ea3055000000572d3ccdcd01000000000093dd7400000000a8ed323227000000000093dd74000000008093dd747011010000000000045359530000000006e59388e59388000000000000000000000000000000000000000000000000000000000000000000
// 待签名 hash
hash = sha256(signBuf)
// f502c5b410ef5d08caf0dc9634c4cc83269487864c740be44fca6f50a0c58e94
// 签名
signature = ecc256k1(hash, privateKey)
// SIG_K1_KcKPUw1sZ38YYBgYuTKMZa4xvfnZf7pNob7DZ7H6CXfbcfD2o2XsGD1vo9rcMjHjZivvCiNTradUStEcxvuypKxFcdGxBN // base 58
// i, r, s, checksum( ripemd(i, r, s, 'K1')[0, 4] )
// 20 39DD0B8AAB045745C28D6B977AC3F23D9805300879C842F186C6CDF3484560F9 28C501782449287FEBB594EB181B5FC58B5242C976F0E2BFD5FAB174099A68CC AC8C898D
// lib/signature.js
// i = eccrecovery() + 4 + 27, 4 for compressed, 27 for compat // eosjs 是直接 + 4 + 27 的,网上资料不多,不太清楚4和27的含义。eccrecovery() 取值范围 [0, 3]
// 另一个例子:
// data: aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e9063cb8bc5b22e05aae1c75000000000100a6823403ea3055000000572d3ccdcd01000000000093dd7400000000a8ed323221000000000093dd74000000008093dd747011010000000000045359530000000000000000000000000000000000000000000000000000000000000000000000000000
// hash: 3b0f1e23e39eff71ef6b2f2670086c3f0894ebf17820a0c3aac5f2467d1dbe2f
// sig: SIG_K1_JyZ24w3Si9V8zt7axDVX3UNtStTVXWEd3diVwSyZMLMVHiRk3RyTi8LNv3qzWgKzz7LBb3uw7iLjMBhRHu7w4nXHWy1xVN
- eos 公钥格式
pubKey = 026B996BC305719A8F563B9E85C9C96A54D40E15F9851FFED5F6A0710504FB5362
checksum = ripemd160(pubKey)[0, 4] = 1b0e677c
eosPubKey = ‘EOS’ + base58(pubKey + checksum) = EOS5hsqHg5EftNtVjVaz39LPnYPEZboYRLbRKK6s4oQEpgsooJ5tP
-
eos 私钥格式(WIF,虽然公钥默认使用压缩公钥,但不会在私钥末尾加01,而标准WIF规范若使用压缩公钥是需要加01的)
priKey = D2653FF7CBB2D8FF129AC27EF5781CE68B2558C41A74AF1F2DDCA635CBEEF07D
checksum = sha256(sha256(80 + priKey)) = AA08644A
eosPriKey = base58(priKey + checksum) = 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
-
eos transactionId
对序列化交易报文进行sha256哈希,不需要添加chainid和packedContextFreeData
-
eos rpc push_transaction 提交格式
{
"compression": "none", // 暂不清楚用途
"packed_context_free_data": "", // hex data,目前为0
"packed_trx": "34b5b45b6adb550b1ec9000000000100a6823403ea3055000000572d3ccdcd01000000000093dd7400000000a8ed323221000000000093dd74000000008093dd74701101000000000004535953000000000000", // 交易序列化结果
"signatures":[ // 所有签名
"SIG_K1_KeH6wCsnUWRjrfGtqPpXFK4BJArfQ4FwuZARnpVyWkfgdrbrKMgH1msyvrvGyxABYj5UyNpMmipWhpyaruRaRFQbcuDBb4"
]
}