# 账户协议

TOP Network 采用账户模型,一切活动围绕账户进行。

用户账户由链上的地址唯一标识,操作账户需要私钥签名。

# 账户对象(Account Object)

账户是一个包含状态信息和逻辑的对象,包括余额、存储数据的属性和每个属性的操作(action)。

账户属性是用户自定义数据的对象,它作为一种键值(Key-Value)对被添加,用户余额(balance)是一种特殊属性。

键是一个任意字符串,其值可以是任何数据类型,比如整数、字符串、列表、HashMap 等。

一个操作(action)可以是一个系统级的功能,比如转账,或者一个由账户所有者部署的智能合约管理的定制操作。

账户对象及其方法可以通过新的属性和定制操作轻松灵活地扩展。

# 账户地址(Account Address)

# 账户地址类型

TOP Network账户地址类型如下表所示。

类型 说明
普通用户账户 独立账户:T80000。
用户合约账户 T30000,是独立账户的一种子账户。在部署合约的时候被创建。
系统合约账户 Beacon 系统合约账户:T20001 或 T20002;
shard系统合约账户:T20000。

例如:

  • 一个独立账户地址:T800002276a7d58218ac4978733e5cca927a7d86cb7c87

  • 一个 Shard 系统合约地址:T20000ML7oBZbitBCcXhrJwqBhha2MUimd6SM9Z6

# 特殊账户地址

账户地址 说明
T!0001Ebj8hBvoLdvcEEUwNZ423zM3Kh9d4nL1Ug 销毁地址,用于销毁治理奖励、零工作量奖励等。

# 账户地址格式

TOP地址编码格式:

0 1 (2,6) n @ subaddr
T 账号类型 ledger id 公钥地址 @ 后缀
格式 长度 说明
前缀标识 1字节 T,代表TOP。
账号类型(addr_type) 1字节 !:销毁账号;
0 或 8:普通账户;
2:shard 系统合约账户;
3:用户合约账户;
21 或 22:Beacon 系统合约账户;
a:Table block 账户。
ledger_id 4 Hex字符 由主链chain-id“0”和zone-index组成。

zone-index:
  • 0:共识 zone;
  • 1、2:Beacon zone;
  • 14:Archive zone;
  • 15:Edge zone。
public address - 公钥 Hash 的 Base58 编码。
sub_address - 可选,账户所属 table。
例如 Beacon 系统合约账户为"T-21-38NN9R9DoXrEhaEGKjGDzSmY1ZfgLqSetvQ@0",其中"@0"表示此系统账户在 Beacon 网络中处于 table 0。
此数据可将账户印射到对应的网络分片。

# 账户地址校验规则

  1. 第 [0] 位必须是"T"。

  2. 第 [1] 位需要满⾜账号类型之⼀。

  3. 第 [2-6] 4 位特殊字符串:

    • ledger_id 需要从 hex 转换为 uint32。

    • ledger_id(uint32)中包含⼀个账号版本信息。

      • ledger_id 算法:

        const std::String Text_ledger_id = account_addr.substr(2,4);//always 4 hex chars
        ledger_id = (uint16_t)xText_utl::hex2uint64(Text_ledger_id);
        
      • 从 ledger_id 获取账号 version:

        uint32_t version = (((uint32_t)ledger_id) << 8) | ((uint32_t)addr_type);
        
  4. 第 [7-end] 为公钥地址。

    • end 定义:

      • 如果没有'@'则 end 为结尾。
      • 如果有'@'则end为'@'-1。
    • 公钥地址校验:

      • 公钥地址:

        public_address = substr(account, 6, end) // 从第6位开始到end
        
      • 公钥地址推回公钥信息, 如果 ecdsa 可以解开公钥即可。

        ecdsa_address_decode(public_address.c_str(), version, HASHER_SHA2D, out) == 1;
        

# 账户空间的分片映射

TOP Network 中的账户分别被映射到不同的网络分片中进行管理,账户空间映射到分片的流程为:

account1

  • Beacon 系统合约账户:

    Snap139

  • shard 系统合约账户、普通用户账户和用户合约账户:

    Snap140

# 生成公私钥对算法

# 普通账户公私钥

私钥

使用 SHA-256 安全散列算法生成一个随机的 256 位的私钥,须以"0x"开头,例如:0x40fb93846c424a107ac854f52290043e4c57368f035e563f8dc0091be8cc1ff9。

公钥

使用 secp256k1 算法生成私钥对应的公钥,例如:0x045f04ab02ef604ee8861b32e5b07ba64070d901daffc5edb0f14a1fc2f27c3b193eff0398023b0ea1285abfd3e1cabc5d83575aa9681880184e8cfd207b02c57f。

账户地址

使用 ECDSA 数字签名算法将公钥转换成账户地址,并根据账户类型加上前缀标识"T8",例如:T800002276a7d58218ac4978733e5cca927a7d86cb7c87。

# 用户合约账户公私钥

公私钥

用户合约账户公私钥生成方法与普通账户相同。

账户地址

用户合约账户地址是将合约账户公钥和与其父账号的公钥逐位相加得到一组数组,再用 ECDSA 数字签名算法将数组转换成地址,并增加前缀标识"T30000",例如:T30000Mo9KPHMGZyyn8AwzUJx6dkdZxzUxhgE9hN。

生成用户合约账户地址示例:

 std::String      xecpubkey_t::to_address(const std::String & parent_addr,const char addr_type,const uint16_t ledger_id)
        {
            if(parent_addr.empty())
                return to_address(addr_type,ledger_id);

            uint8_t     temp_publickey_data[65];
            memcpy(temp_publickey_data, m_publickey_data, sizeof(temp_publickey_data));
            const int parent_addr_size = std::min((int)parent_addr.size(),65);
            for(int i = 0; i < parent_addr_size; ++i)
            {
                temp_publickey_data[i] += parent_addr[i];
            }
            
            return to_address(temp_publickey_data, addr_type, ledger_id);
        }

# keystore 文件

keystore 文件是您独有的、用于签署交易的私钥加密文件,允许您以加密的方式存储密钥。

这种方式兼具 安全性(一个攻击者需要账户 keystore 文件和您的密码才能盗取您的资产)和 易用性(您只需要 keystore 文件和密码就能使用资产)。

WARNING

如果您丢失了私钥,意味着您失去了签署交易的能力,同时意味着您的资产被永久的锁定在了您的账户里。

所以,请务必保管好您的私钥以及 keystore 文件!不要向他人透露您的私钥和 keystore 文件密码!

keystore 文件格式:

{
   "account_address" : "T800002276a7d58218ac4978733e5cca927a7d86cb7c87",
   "address" : "2276a7d58218ac4978733e5cca927a7d86cb7c87",
   "crypto" : {
      "cipher" : "aes-128-ctr",
      "cipherparams" : {
         "iv" : "09dade6d3ce2741794e49d65f8dd65a4"
      },
      "ciphertext" : "a45fd491c32ad271039883ed4084e18c57e12cd2c21af1522fe94ba8313634a3",
      "kdf" : "scrypt",
      "kdfparams" : {
         "dklen" : 32,
         "n" : 262144,
         "p" : 1,
         "r" : 8,
         "salt" : "9ed2bcb3349392765fec47bb20776cdb69e0e3a4d19d26da65f9371f83cfc2b5"
      },
      "mac" : "da83cf03746166c877bf319d6371c7f54d518c48c99b1782fdad56e343b812f6"
   },
   "hint" : "",
   "id" : "568498c9-889b-a26d-fa01-bc9a67ffc472",
   "key_type" : "owner",
   "public_key" : "9b473e552fd05ed2e39d4d3af18367cc98859d50a3eb93805e4706b6b86310c83a2c4e847b662266330cef4cfb7c595ad655ab6a18d86708fe7bc00b589a3a68",
   "version" : 3
}
参数 说明
account address 账户地址。 当 key_type 为"worker"时,此处为 worker key 所属账户地址。
address 以太坊账户地址。
crypto 私钥加密相关信息。
cipher 私钥加密算法。
cipherparams 上述 cipher 加密算法参数。
iv 向量。
ciphertext 加密后的私钥。
kdf 密钥生成函数,用于让您用密码加密 keystore 文件。
kdfparams 上述 kdf 算法需要的参数。
dklen 私钥加密算法的密钥长度。
n 进行加密解密运算的次数,值越大,可以增加暴力破解的成本,也会使签名速度变慢。
p 设置为 1 时只能串行运算,0 时为并行运算。串行运算可以增加安全性,也会影响签名速度。
r 加密的分组长度。
salt 随机生成的向量。
mac 消息认证码。
hint keystore 文件加密密码提示问题。
id uuid。
key_type owner key 或者 worker key。
public_key owner key 或者 worker key 的 public key。
version Keystore 文件的版本。

# 生成 keystore 文件的方法

  1. 生成原始私钥,并对原始数据进行 16 进制编码。

  2. 生成 32 字节的 salt

    CryptoPP::AutoSeededRandomPool::GenerateBlock
    
  3. 生成 AES 的 32 字节加密 key。

    其中:kdf 算法采用 scrypt,hash 算法采用 SHA3-256,再加上密码,生成加密 key。

  4. 生成 AES 的 16 字节的 iv

    CryptoPP::AutoSeededRandomPool::GenerateBlock
    
  5. 生成密文。加密算法采用 AES128,加密模式采用 CTR_Mode。

  6. 计算 32 字节 mac(message authentication code 消息认证码),将 aes_key 后 16 字节同加密后的密文(ciphertext)拼接,然后对拼接数据进行 hash(SHA3-256)计算。

  7. 将上述相关数据写入文件。