dApp Docs/Dilithium-5 后量子签名开发者指南
Development reference. Not independently verified for production.

Dilithium-5 后量子签名开发者指南

数据来源:MSG Chain 代码库核实

主网状态: No-Go — 当前 MSGChain 主网裁决为 No-Go,以下内容反映代码实际状态,不代表生产可用。

MSG Chain 后量子密码学实战手册


第一章:概述

1.1 为什么后量子密码学至关重要

量子计算的发展对当前公钥密码体系构成了根本性威胁。Shor算法可以在多项式时间内解决大整数分解和离散对数问题,这意味着一旦足够强大的量子计算机问世:

这一威胁被称为"Q-Day"——预计可能在2030年前后到来。对于区块链网络而言,迁移到后量子密码学(PQC)是至关重要的,因为:

  1. 长期安全:区块需要长期不可篡改,今天签名的交易在10年后仍需安全
  2. "先捕获,后解密"攻击:攻击者可以现在收集加密数据,等量子计算机可用后再解密
  3. 共识安全:验证者密钥一旦被破解,攻击者可伪造区块,破坏整个网络

MSG Chain采取"PQ优先"(Post-Quantum First)策略,将Dilithium-5作为默认签名方案,从根本上解决了这些威胁。

1.2 Dilithium-5:NIST标准ML-DSA(FIPS 204)

CRYSTALS-Dilithium是由IBM、瑞士苏黎世联邦理工学院(ETH Zurich)等机构设计的格密码签名方案。2024年8月,NIST正式将其标准化为ML-DSA(Module-Lattice-Based Digital Signature Algorithm),编号FIPS 204。

Dilithium提供三个安全等级:

参数集 NIST安全等级 等效对称安全 公钥大小 签名大小
Dilithium-2 2 AES-128 1,312 B 2,420 B
Dilithium-3 3 AES-192 1,952 B 3,309 B
Dilithium-5 5 AES-256 2,592 B 4,595 B

MSG Chain选择Dilithium-5——最高的NIST安全等级,提供与AES-256相当的安全强度。

1.3 MSG Chain的PQ优先架构

MSG Chain是首个从创世块开始就原生使用Dilithium-5签名的区块链网络。架构特点:

+-----------------------------------------------------------+
| MSG Chain 节点 |
+-----------------------------------------------------------+
| 共识层 (CometBFT / Tendermint) |
| +-- 验证者密钥: Dilithium-5 |
| +-- 区块签名: Dilithium-5 |
| +-- DAR (确定性问责轮次): Dilithium-5 |
+-----------------------------------------------------------+
| 应用层 (Cosmos SDK) |
| +-- 交易签名: Dilithium-5 (默认) |
| +-- 地址派生: Dilithium-5 -> bech32(msg...) |
| +-- Secp256k1: 向后兼容 |
+-----------------------------------------------------------+
| 密钥管理层 |
| +-- priv_validator_key.json (Dilithium-5) |
| +-- node_key.json (Dilithium-5) |
| +-- Keyring (Cosmos SDK密钥环) |
+-----------------------------------------------------------+

1.4 Dilithium-5 vs Secp256k1 对比

特性 Secp256k1 Dilithium-5 差异说明
算法类型 ECDSA (椭圆曲线) ML-DSA (格密码) 完全不同的数学基础
量子抗性 否 是 Secp256k1可被Shor算法破解
公钥大小 33 B (压缩) 2,592 B Dilithium大约78倍
签名大小 70-72 B 4,595 B Dilithium大约64倍
签名速度 ~0.3ms ~0.8ms Dilithium慢约2.7倍
验证速度 ~1.0ms ~0.2ms Dilithium快约5倍
密钥生成速度 ~0.1ms ~0.5ms Dilithium慢约5倍
安全基础 ECDLP Module-SIS/LWE 量子安全 vs 非量子安全
NIST标准化 否 FIPS 204 官方PQC标准
MSG Chain支持 兼容模式 原生/默认 双轨并进 -> PQ-only

关键结论:Dilithium-5虽然密钥和签名更大、签名稍慢,但验证速度快5倍,且提供量子安全。对于MSG Chain而言,验证性能更为关键(全节点验证所有交易)。

1.5 适用场景


第二章:密码学基础

2.1 基于格的密码学

Dilithium的安全性建立在格密码学(Lattice-based Cryptography)之上。格是数学中一个基本概念:

定义:格是R^n空间中一个离散的加法子群。
形式化:L = { sum_i a_i * b_i | a_i in Z },其中b_i是线性无关的基向量

在二维空间中,格可以想象成平面上按一定规律重复排列的点阵。高维格中的"最近向量问题"(CVP)和"最短向量问题"(SVP)被认为是量子计算无法高效求解的难题。

2.2 Module-LWE与Module-SIS

Dilithium依赖两个核心困难问题:

Module-LWE (Learning With Errors)

定义:给定 (A, t = As + e),求s困难
其中:
A <- R_q^(k
k) 随机矩阵
s <- S_eta^k 秘密向量(小系数)
e <- S_eta^k 误差向量(小系数)
R_q = Z_q[X]/(X^n + 1) 多项式环

直观理解:给定一个线性方程组加上一些随机噪声,恢复原始解是困难的。量子计算机也无法有效求解。

Module-SIS (Short Integer Solution)

定义:给定A in R_q^(kl),求非零z使得Az = 0且||z||小

Module-SIS保证了签名的不可伪造性:攻击者无法为新的消息伪造有效签名。

2.3 Dilithium-5参数

Dilithium-5的具体参数:

参数 符号 Dilithium-5值 说明
模数 q 8,380,417 素数,约2^23
环维度 n 256 多项式环 X^256 + 1
模块秩 k 5 矩阵行数(安全等级5)
模块秩 l 4 矩阵列数
掩码比特 d 14 hint的压缩位数
挑战范围 gamma1 2^19 挑战范围
零化阈值 gamma2 (q-1)/88 拒绝采样阈值
小系数范围 eta 2 秘密/误差系数范围
挑战权重 tau 60 挑战向量中+-1的数量
beta beta 175 eta*tau
公钥大小 pk 2,592 B 按FIPS 204标准序列化
签名大小 sig 4,595 B 按FIPS 204标准序列化

参数含义详解:

2.4 密钥生成算法

Dilithium-5密钥生成过程(简化表达):

Algorithm: Dilithium.KeyGen()

Input:  随机种子 xi
Output: 公钥 pk, 私钥 sk

1. zeta <- H(xi)                     // 派生确定性随机数
2. rho, rho' <- H(zeta)              // 生成矩阵种子和掩码种子
3. A <- ExpandA(rho)                  // 用SHAKE-128展开矩阵A in R_q^(k*k)
4. s1, s2 <- ExpandS(rho')            // 从种子生成秘密向量
5. t = A*s1 + s2                      // 计算公钥 t
6. t1 = Power2Round(t, d)            // 压缩t为t1(高位)
7. t0 = t - t1*2^d                    // 保存低位供签名使用
8. pk = (rho, t1)                     // 公钥 = 矩阵种子 + t1
9. sk = (rho, rho', K, s1, s2, t0)   // 私钥包含所有必要状态
10. return (pk, sk)

关键点:

在Go SDK中,此过程封装为:

// 实际使用只需一行调用
privKey, err := dilithium.GenKeyV5(rand.Reader)

2.5 签名算法

Algorithm: Dilithium.Sign(sk, M)

Input:  私钥 sk, 消息 M
Output: 签名 sigma

1. mu <- H(M || pk)                    // 哈希消息+公钥防止密钥替换攻击
2. kappa <- 0                          // 重试计数器
3. repeat:
4.   y <- ExpandMask(rho', kappa)      // 产生掩码 y
5.   w = A*y                           // 计算承诺 w
6.   w1 = HighBits(w, 2*gamma2)        // 提取w的高位
7.   c <- H(mu || w1)                  // 生成挑战 c (权重tau)
8.   z = y + c*s1                      // 计算响应 z
9.   r1 = LowBits(w - c*s2, 2*gamma2)  // 检查是否可分解
10.  if ||z||_inf >= gamma1 - beta     // 拒绝采样条件1
11.    or ||r1||_inf >= gamma2 - beta  // 拒绝采样条件2
12.    kappa++ ; continue
13.  h <- MakeHint(-c*t0, w - c*s2 + c*t0)
14.  if count(h) > omega               // hint中1的数量限制
15.    kappa++ ; continue
16. until valid
17. sigma = (c, z, h)                  // 签名 = 挑战 + 响应 + hint
18. return sigma

拒绝采样(Rejection Sampling)是Dilithium的核心技术。因为z = y + c*s1可能泄露s1的信息,如果z太大(范数超过阈值),就丢弃并重试。平均需要约4次才能产生一个有效签名。这就是为什么签名比验证慢得多。

2.6 验证算法

Algorithm: Dilithium.Verify(pk, M, sigma)

Input:  公钥 pk, 消息 M, 签名 sigma
Output: 有效/无效

1. mu <- H(M || pk)
2. A <- ExpandA(rho)                   // 重建矩阵A
3. c, z, h <- sigma                    // 解构签名
4. w1' = UseHint(h, A*z - c*t1*2^d)
5. c' <- H(mu || w1')
6. if c' == c and ||z||_inf < gamma1 - beta
7.   return 有效
8. else
9.   return 无效

验证的优势:验证过程不需要知道t0(只用了t1),也不需要秘密参数。验证的计算量比签名小得多——这是签名方案中理想的性质,因为区块链上验证操作的频率远高于签名操作。

2.7 安全假设

Dilithium-5的安全性归约为两个格的困难问题:

Module-SIS(签名不可伪造性)

攻击者无法伪造签名,即使他们可以看到许多有效签名。这归约为:给定随机矩阵A in R_q^(kl),找到短向量z使得Az = 0是困难的。

归约路径:
签名伪造 -> Module-SIS问题 -> SVP_gamma问题 -> 格中困难问题

Module-LWE(密钥恢复)

攻击者无法从公钥恢复私钥。这归约为:给定(A, t = A*s1 + s2),恢复(s1, s2)是困难的。

参数安全性:

侧信道安全性

Dilithium的确定性变体(使用rand.Reader提供种子)对所有消息使用相同随机数,避免了DSA类方案中随机数重用的灾难性后果。

2.8 性能特征

在典型x86-64处理器(Intel Xeon 3.0GHz)上的性能数据:

操作 Dilithium-2 Dilithium-3 Dilithium-5
密钥生成 ~80,000 cycles ~120,000 cycles ~180,000 cycles
签名 ~350,000 cycles ~550,000 cycles ~780,000 cycles
验证 ~80,000 cycles ~110,000 cycles ~150,000 cycles
公钥大小 1,312 B 1,952 B 2,592 B
签名大小 2,420 B 3,309 B 4,595 B

MSG Chain环境实测数据

在MSG Chain验证者节点上的基准测试:

$ make test-quantum
=== RUN   TestDilithium5_KeyGen
--- PASS: TestDilithium5_KeyGen (0.0005s)
=== RUN   TestDilithium5_Sign
--- PASS: TestDilithium5_Sign (0.0008s)
=== RUN   TestDilithium5_Verify
--- PASS: TestDilithium5_Verify (0.0002s)
=== RUN   TestDilithium5_TxSigning
--- PASS: TestDilithium5_TxSigning (0.0031s)

交易大小影响

由于Dilithium-5签名大小为4,595字节,交易大小也会相应变化:

交易类型 Secp256k1 Dilithium-5 增加比例
MsgSend ~200 B ~4,800 B ~24x
MsgDelegate ~250 B ~4,850 B ~19x
MsgBeginRedelegate ~300 B ~4,900 B ~16x
MsgUndelegate ~250 B ~4,850 B ~19x

这意味着区块大小和Gas计算需要相应调整。MSG Chain的区块Gas限制已考虑Dilithium-5签名开销。


第三章:密钥生成与管理

3.1 生成Dilithium-5密钥对

Go SDK

package main

import (
    "crypto/rand"
    "encoding/hex"
    "fmt"
    "log"

    "github.com/msgchain/msgchain/crypto/dilithium"
)

func main() {
    // 生成Dilithium-5私钥
    privKey, err := dilithium.GenKeyV5(rand.Reader)
    if err != nil {
        log.Fatalf("密钥生成失败: %v", err)
    }

    // 提取公钥
    pubKey := privKey.PubKey()

    privHex := hex.EncodeToString(privKey.Bytes())
    pubHex := hex.EncodeToString(pubKey.Bytes())

    fmt.Printf("私钥 (hex): %s\n", privHex)
    fmt.Printf("私钥长度: %d 字节\n", len(privKey.Bytes()))
    fmt.Printf("公钥 (hex): %s\n", pubHex)
    fmt.Printf("公钥长度: %d 字节\n", len(pubKey.Bytes()))

    addr := pubKey.Address()
    fmt.Printf("地址: %s\n", addr.String())
    fmt.Printf("密钥类型: %s\n", privKey.Type())
    fmt.Printf("公钥类型: %s\n", pubKey.Type())
}

输出示例:

私钥 (hex): a1b2c3d4... (共约4,864字节)
私钥长度: 4864 字节
公钥 (hex): e5f6a7b8... (共2,592字节)
公钥长度: 2592 字节
地址: msg1q8lkvgzck8wkz3x9v6xkqf4u5a3d7e2n9jxc4p
密钥类型: dilithium-5
公钥类型: dilithium-5

Rust SDK

use msg_chain_crypto::dilithium::{DilithiumKeypair, DilithiumPublicKey};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let keypair = DilithiumKeypair::generate()?;

    let secret_bytes = keypair.secret_key_bytes();
    let public_bytes = keypair.public_key_bytes();

    println!("私钥 (hex): {}", hex::encode(&secret_bytes));
    println!("私钥长度: {} 字节", secret_bytes.len());
    println!("公钥 (hex): {}", hex::encode(&public_bytes));
    println!("公钥长度: {} 字节", public_bytes.len());

    let address = keypair.address();
    println!("地址: {}", address);

    let keypair_from_bytes = DilithiumKeypair::from_secret_key_bytes(&secret_bytes)?;
    assert_eq!(keypair.public_key_bytes(), keypair_from_bytes.public_key_bytes());
    Ok(())
}

Cargo.toml依赖:

[dependencies]
msg-chain-crypto = "0.3"
hex = "0.4"

Python SDK

from msgchain_sdk.crypto import Dilithium5

keypair = Dilithium5.generate()

private_key_hex = keypair.private_key.hex()
public_key_hex = keypair.public_key.hex()

print(f"私钥 (hex): {private_key_hex}")
print(f"私钥长度: {len(keypair.private_key)} 字节")
print(f"公钥 (hex): {public_key_hex}")
print(f"公钥长度: {len(keypair.public_key)} 字节")

address = keypair.address
print(f"地址: {address}")

keypair2 = Dilithium5.from_hex(private_key_hex)
assert keypair2.public_key.hex() == public_key_hex

安装:

pip install msgchain-sdk

TypeScript SDK

import { DilithiumKeypair } from '@msg-chain/crypto';

const keypair = DilithiumKeypair.generate();

const privateKeyHex = keypair.getPrivateKey('hex');
const publicKeyHex = keypair.getPublicKey('hex');

console.log(`私钥 (hex): ${privateKeyHex}`);
console.log(`私钥长度: ${keypair.getPrivateKey().length} 字节`);
console.log(`公钥 (hex): ${publicKeyHex}`);
console.log(`公钥长度: ${keypair.getPublicKey().length} 字节`);

const address = keypair.getAddress();
console.log(`地址: ${address}`);

const keypair2 = DilithiumKeypair.fromHex(privateKeyHex);
console.log(keypair2.getAddress());

安装:

npm install @msg-chain/crypto@alpha

3.2 密钥存储格式(JSON/Amino)

MSG Chain使用Cosmos SDK的Amino编码格式存储密钥。

priv_validator_key.json

验证者共识密钥存储在~/.msgchain/config/priv_validator_key.json:

{
  "address": "msg1q8lkvgzck8wkz3x9v6xkqf4u5a3d7e2n9jxc4p",
  "pub_key": {
    "type": "tendermint/PubKeyDilithium5",
    "value": "CukEAgDwuJ0AAQAAACAAgACAgP..."
  },
  "priv_key": {
    "type": "tendermint/PrivKeyDilithium5",
    "value": "A6cFAgDwuJ0AAAAAIA..."
  }
}

Amino注册类型:

import (
    "github.com/cosmos/cosmos-sdk/codec"
    "github.com/msgchain/msgchain/crypto/dilithium"
)

func RegisterDilithiumCrypto(cdc *codec.LegacyAmino) {
    cdc.RegisterConcrete(&dilithium.PubKeyDilithium5{},
        "tendermint/PubKeyDilithium5", nil)
    cdc.RegisterConcrete(&dilithium.PrivKeyDilithium5{},
        "tendermint/PrivKeyDilithium5", nil)
}

node_key.json

节点P2P身份密钥存储在~/.msgchain/config/node_key.json:

{
  "key": {
    "type": "tendermint/PrivKeyDilithium5",
    "value": "B8dFAgDwuJ0AAAAAIA..."
  }
}

3.3 BIP39助记词到Dilithium-5种子派生

Go实现

package main

import (
    "crypto/rand"
    "fmt"
    "log"

    "github.com/cosmos/cosmos-sdk/crypto/hd"
    "github.com/cosmos/go-bip39"
    "github.com/msgchain/msgchain/crypto/dilithium"
)

func DeriveDilithium5Key(mnemonic, passphrase string) (*dilithium.PrivKeyDilithium5, error) {
    seed := bip39.NewSeed(mnemonic, passphrase)
    master, ch := hd.ComputeMastersFromSeed(seed)
    derivedPriv, err := hd.DerivePrivateKeyForPath(master, ch, "44'/118'/0'/0/0")
    if err != nil {
        return nil, fmt.Errorf("BIP32派生失败: %w", err)
    }
    privKey, err := dilithium.GenKeyV5WithSeed(derivedPriv)
    if err != nil {
        return nil, fmt.Errorf("Dilithium-5密钥生成失败: %w", err)
    }
    return privKey, nil
}

func GenerateMnemonic() (string, error) {
    entropy := make([]byte, 32)
    _, err := rand.Read(entropy)
    if err != nil {
        return "", err
    }
    mnemonic, err := bip39.NewMnemonic(entropy)
    if err != nil {
        return "", err
    }
    return mnemonic, nil
}

func main() {
    mnemonic, err := GenerateMnemonic()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("助记词: %s\n", mnemonic)

    privKey, err := DeriveDilithium5Key(mnemonic, "")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("派生地址: %s\n", privKey.PubKey().Address().String())
}

Rust实现

use msg_chain_crypto::dilithium::DilithiumKeypair;
use msg_chain_crypto::bip39::{Mnemonic, Language, Seed};
use msg_chain_crypto::hd::derive_dilithium_key;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mnemonic = Mnemonic::generate(Language::ChineseSimplified, 24)?;
    println!("助记词: {}", mnemonic);

    let seed = Seed::new(&mnemonic, "");
    let keypair = derive_dilithium_key(seed.as_bytes(), "m/44'/118'/0'/0/0")?;

    println!("地址: {}", keypair.address());
    println!("公钥: {}", hex::encode(keypair.public_key_bytes()));

    let seed2 = Seed::new(&mnemonic, "");
    let keypair2 = derive_dilithium_key(seed2.as_bytes(), "m/44'/118'/0'/0/0")?;
    assert_eq!(keypair.address(), keypair2.address());
    Ok(())
}

Python实现

from msgchain_sdk.crypto import Dilithium5
from msgchain_sdk.bip39 import generate_mnemonic, seed_from_mnemonic
from msgchain_sdk.hd import derive_dilithium_key

mnemonic = generate_mnemonic(strength=256)
print(f"助记词: {mnemonic}")

seed = seed_from_mnemonic(mnemonic, passphrase="")
private_key_bytes = derive_dilithium_key(seed, "m/44'/118'/0'/0/0")

keypair = Dilithium5.from_bytes(private_key_bytes)
print(f"地址: {keypair.address}")
print(f"公钥: {keypair.public_key.hex()}")

seed2 = seed_from_mnemonic(mnemonic, passphrase="")
private_key_bytes2 = derive_dilithium_key(seed2, "m/44'/118'/0'/0/0")
keypair2 = Dilithium5.from_bytes(private_key_bytes2)
assert keypair2.public_key.hex() == keypair.public_key.hex()
print("确定性派生验证通过")

3.4 硬件安全模块(HSM)集成

对于生产验证者节点,推荐使用HSM保护Dilithium-5私钥:

Go: PKCS#11接口

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/msgchain/msgchain/crypto/dilithium"
    "github.com/msgchain/msgchain/hsm"
)

func main() {
    hsmClient, err := hsm.NewPKCS11Client(&hsm.PKCS11Config{
        Library: "/usr/lib/softhsm/libsofthsm2.so",
        Slot:    0,
        PIN:     os.Getenv("HSM_PIN"),
    })
    if err != nil {
        log.Fatalf("HSM连接失败: %v", err)
    }
    defer hsmClient.Close()

    keyID, err := hsmClient.GenerateDilithium5Key()
    if err != nil {
        log.Fatalf("HSM密钥生成失败: %v", err)
    }
    fmt.Printf("HSM密钥ID: %x\n", keyID)

    pubKey, err := hsmClient.GetDilithium5PublicKey(keyID)
    if err != nil {
        log.Fatal(err)
    }

    msg := []byte("要签名的消息")
    signature, err := hsmClient.SignDilithium5(keyID, msg)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("签名 (%d 字节): %x...\n", len(signature), signature[:32])

    valid := pubKey.VerifySignature(msg, signature)
    fmt.Printf("签名验证: %v\n", valid)
}

Rust: TPM 2.0集成

use msg_chain_crypto::dilithium::DilithiumPublicKey;
use tss_esapi::{Context, constants::tss::TPM2_ALG_DILITHIUM5};

fn tpm_signing_example() -> Result<(), Box<dyn std::error::Error>> {
    let mut context = Context::new("/dev/tpmrm0")?;
    let key_handle = context.create_primary(TPM2_ALG_DILITHIUM5, &Default::default())?;
    let pub_key_bytes = context.read_public(key_handle)?.dilithium5_public()?;
    let pub_key = DilithiumPublicKey::from_bytes(&pub_key_bytes)?;

    let digest = sha2::Sha256::digest(b"要签名的数据");
    let signature = context.sign(key_handle, &digest, None)?;

    let valid = pub_key.verify(b"要签名的数据", &signature);
    println!("TPM签名验证: {}", valid);
    Ok(())
}

第四章:签名与验证

4.1 基本签名操作

Go SDK

package main

import (
    "crypto/rand"
    "fmt"
    "log"

    "github.com/msgchain/msgchain/crypto/dilithium"
)

func main() {
    privKey, err := dilithium.GenKeyV5(rand.Reader)
    if err != nil {
        log.Fatal(err)
    }
    pubKey := privKey.PubKey()

    message := []byte("{\"chain_id\":\"msg-chain-1\",\"account_number\":\"42\",\"sequence\":\"1\"}")

    signature, err := privKey.Sign(message)
    if err != nil {
        log.Fatalf("签名失败: %v", err)
    }
    fmt.Printf("消息: %s\n", message)
    fmt.Printf("签名长度: %d 字节\n", len(signature))
    fmt.Printf("签名 (hex): %x\n", signature)

    valid := pubKey.VerifySignature(message, signature)
    fmt.Printf("签名验证: %v\n", valid)

    tampered := []byte("篡改后的消息")
    validTampered := pubKey.VerifySignature(tampered, signature)
    fmt.Printf("篡改消息验证: %v (应返回false)\n", validTampered)
}

Rust SDK

use msg_chain_crypto::dilithium::DilithiumKeypair;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let keypair = DilithiumKeypair::generate()?;
    let message = b"{\"chain_id\":\"msg-chain-1\",\"account_number\":\"42\"}";

    let signature = keypair.sign(message)?;
    println!("签名长度: {} 字节", signature.len());
    println!("签名 (hex): {}", hex::encode(&signature));

    let valid = keypair.verify(message, &signature);
    println!("签名验证: {}", valid);

    let pub_key = keypair.public_key();
    let valid2 = pub_key.verify(message, &signature);
    println!("公钥验证: {}", valid2);
    Ok(())
}

Python SDK

from msgchain_sdk.crypto import Dilithium5

keypair = Dilithium5.generate()
message = b'{"chain_id":"msg-chain-1","account_number":"42"}'

signature = keypair.sign(message)
print(f"签名长度: {len(signature)} 字节")
print(f"签名 (hex): {signature.hex()}")

is_valid = keypair.verify(message, signature)
print(f"签名验证: {is_valid}")

pk = keypair.get_public_key()
is_valid2 = pk.verify(message, signature)
print(f"公钥验证: {is_valid2}")

TypeScript SDK

import { DilithiumKeypair } from '@msg-chain/crypto';

const keypair = DilithiumKeypair.generate();
const message = Buffer.from('{"chain_id":"msg-chain-1","account_number":"42"}');

const signature = keypair.sign(message);
console.log(`签名长度: ${signature.length} 字节`);
console.log(`签名 (hex): ${Buffer.from(signature).toString('hex')}`);

const valid = keypair.verify(message, signature);
console.log(`签名验证: ${valid}`);

const keypair2 = DilithiumKeypair.fromHex(keypair.getPrivateKey('hex'));
console.log(`恢复密钥地址一致: ${keypair.getAddress() === keypair2.getAddress()}`);

4.2 Amino编码与Cosmos SDK兼容

package main

import (
    "fmt"
    "log"

    "github.com/cosmos/cosmos-sdk/codec"
    "github.com/msgchain/msgchain/crypto/dilithium"
)

func main() {
    cdc := codec.NewLegacyAmino()

    cdc.RegisterConcrete(&dilithium.PubKeyDilithium5{},
        "tendermint/PubKeyDilithium5", nil)
    cdc.RegisterConcrete(&dilithium.PrivKeyDilithium5{},
        "tendermint/PrivKeyDilithium5", nil)

    privKey, _ := dilithium.GenKeyV5(rand.Reader)
    pubKey := privKey.PubKey()

    pubAmino, err := cdc.MarshalJSON(pubKey)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Amino公钥JSON: %s\n", pubAmino)
}

4.3 SIGN_MODE_DIRECT (Protobuf)

Cosmos SDK的SIGN_MODE_DIRECT使用Protobuf序列化交易进行签名:

package main

import (
    "fmt"
    "log"

    "github.com/cosmos/cosmos-sdk/x/auth/signing"
    "github.com/cosmos/cosmos-sdk/x/auth/tx"
    "github.com/cosmos/cosmos-sdk/client"
    "github.com/cosmos/cosmos-sdk/types/tx/signing"
    "github.com/msgchain/msgchain/crypto/dilithium"
)

func SignWithSignModeDirect(
    privKey *dilithium.PrivKeyDilithium5,
    txBuilder client.TxBuilder,
    signData signing.SignData,
) ([]byte, error) {
    signDoc := tx.NewSignDoc(
        signData.ChainID,
        signData.AccountNumber,
        signData.AccountSequence,
        signData.SignMode,
        signData.SignDoc,
    )
    signBytes, err := signDoc.Marshal()
    if err != nil {
        return nil, fmt.Errorf("SignDoc序列化失败: %w", err)
    }
    signature, err := privKey.Sign(signBytes)
    if err != nil {
        return nil, fmt.Errorf("Dilithium签名失败: %w", err)
    }
    return signature, nil
}

4.4 多签聚合

Go SDK: 多签

package main

import (
    "crypto/rand"
    "fmt"
    "log"

    "github.com/msgchain/msgchain/crypto/dilithium"
)

type MultiSignature struct {
    Signatures [][]byte `json:"signatures"`
    PubKeys    [][]byte `json:"pub_keys"`
}

func CreateMultiSig(message []byte, privKeys []*dilithium.PrivKeyDilithium5) (*MultiSignature, error) {
    multiSig := &MultiSignature{
        Signatures: make([][]byte, len(privKeys)),
        PubKeys:    make([][]byte, len(privKeys)),
    }
    for i, privKey := range privKeys {
        sig, err := privKey.Sign(message)
        if err != nil {
            return nil, fmt.Errorf("签名者%d签名失败: %w", i, err)
        }
        multiSig.Signatures[i] = sig
        multiSig.PubKeys[i] = privKey.PubKey().Bytes()
    }
    return multiSig, nil
}

func VerifyMultiSig(message []byte, multiSig *MultiSignature) bool {
    for i, sig := range multiSig.Signatures {
        pubKey := &dilithium.PubKeyDilithium5{}
        if err := pubKey.UnmarshalAmino(multiSig.PubKeys[i]); err != nil {
            return false
        }
        if !pubKey.VerifySignature(message, sig) {
            return false
        }
    }
    return true
}

func main() {
    keys := make([]*dilithium.PrivKeyDilithium5, 3)
    for i := range keys {
        key, _ := dilithium.GenKeyV5(rand.Reader)
        keys[i] = key
    }
    message := []byte("多签交易数据")
    multiSig, _ := CreateMultiSig(message, keys)
    ok := VerifyMultiSig(message, multiSig)
    fmt.Printf("全部签名验证: %v\n", ok)
}

第五章:Cosmos SDK集成

5.1 密钥环(Keyring)集成

MSG Chain的密钥环支持Dilithium-5密钥类型:

package main

import (
    "fmt"
    "log"

    "github.com/cosmos/cosmos-sdk/client"
    "github.com/cosmos/cosmos-sdk/crypto/keyring"
    "github.com/msgchain/msgchain/crypto/dilithium"
    sdk "github.com/cosmos/cosmos-sdk/types"
)

func main() {
    kr, err := keyring.New(
        "msgchain",
        keyring.BackendFile,
        "/home/user/.msgchain",
        nil,
        dilithium.Dilithium5Codec(),
    )
    if err != nil {
        log.Fatal(err)
    }

    record, err := kr.NewAccount(
        "my-dilithium-key",
        nil,
        "",
        sdk.FullFundraiserPath,
        nil,
    )
    if err != nil {
        log.Fatal(err)
    }

    pubKey, _ := record.GetPubKey()
    addr, _ := record.GetAddress()
    fmt.Printf("密钥名称: %s\n", record.Name)
    fmt.Printf("地址: %s\n", addr.String())
    fmt.Printf("公钥类型: %s\n", pubKey.Type())
}

通过命令行创建Dilithium-5密钥:

./bin/quantum_node_linux keys add my-key \
    --key-type dilithium-5 \
    --keyring-backend file \
    --home ~/.msgchain

5.2 地址派生

Dilithium-5地址派生的完整流程:

package main

import (
    "golang.org/x/crypto/sha3"
    "fmt"

    "github.com/btcsuite/btcutil/bech32"
    "github.com/msgchain/msgchain/crypto/dilithium"
)

func DeriveAddress(pubKey *dilithium.PubKeyDilithium5) (string, error) {
    pubBytes := pubKey.Bytes()
    hasher := sha3.New512()
    hasher.Write(pubBytes)
    hash := hasher.Sum(nil)
    addrBytes := hash[:20]
    encoded, err := bech32.Encode("msg", addrBytes)
    if err != nil {
        return "", fmt.Errorf("bech32编码失败: %w", err)
    }
    return encoded, nil
}

func main() {
    privKey, _ := dilithium.GenKeyV5(rand.Reader)
    pubKey := privKey.PubKey()
    addr, _ := DeriveAddress(pubKey)
    fmt.Printf("地址: %s\n", addr)

    hrp, decoded, err := bech32.Decode(addr)
    if err != nil {
        fmt.Printf("地址解码失败: %v\n", err)
        return
    }
    fmt.Printf("HRP: %s\n", hrp)
    fmt.Printf("地址字节: %x\n", decoded)
    fmt.Printf("地址长度: %d 字节\n", len(decoded))
}

第六章:SDK各语言实现

6.1 Go SDK完整示例

密钥生成

package main

import (
    "crypto/rand"
    "fmt"
    "log"

    "github.com/msgchain/msgchain/crypto/dilithium"
)

func main() {
    // 随机生成
    privKey, err := dilithium.GenKeyV5(rand.Reader)
    if err != nil {
        log.Fatal(err)
    }

    // 确定性生成(用于测试)
    seed := make([]byte, 32)
    for i := range seed {
        seed[i] = byte(i)
    }
    privKey2, err := dilithium.GenKeyV5WithSeed(seed)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("随机密钥地址: %s\n", privKey.PubKey().Address().String())
    fmt.Printf("确定性密钥地址: %s\n", privKey2.PubKey().Address().String())
}

签名与验证

package main

import (
    "crypto/rand"
    "fmt"
    "log"

    "github.com/msgchain/msgchain/crypto/dilithium"
)

func main() {
    privKey, _ := dilithium.GenKeyV5(rand.Reader)
    pubKey := privKey.PubKey()

    message := []byte("Hello, MSG Chain!")
    signature, err := privKey.Sign(message)
    if err != nil {
        log.Fatalf("签名失败: %v", err)
    }
    fmt.Printf("签名 (%d 字节): %x\n", len(signature), signature)

    valid := pubKey.VerifySignature(message, signature)
    fmt.Printf("验证结果: %v\n", valid)
}

基准测试

package dilithium_test

import (
    "crypto/rand"
    "testing"
    "github.com/msgchain/msgchain/crypto/dilithium"
)

func BenchmarkDilithium5_KeyGen(b *testing.B) {
    for i := 0; i < b.N; i++ {
        dilithium.GenKeyV5(rand.Reader)
    }
}

func BenchmarkDilithium5_Sign(b *testing.B) {
    privKey, _ := dilithium.GenKeyV5(rand.Reader)
    msg := make([]byte, 256)
    rand.Read(msg)
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        privKey.Sign(msg)
    }
}

func BenchmarkDilithium5_Verify(b *testing.B) {
    privKey, _ := dilithium.GenKeyV5(rand.Reader)
    pubKey := privKey.PubKey()
    msg := make([]byte, 256)
    rand.Read(msg)
    sig, _ := privKey.Sign(msg)
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        pubKey.VerifySignature(msg, sig)
    }
}

6.2 Rust SDK完整示例

Cargo.toml

[package]
name = "msgchain-dilithium-example"
version = "0.1.0"
edition = "2021"

[dependencies]
msg-chain-crypto = { version = "0.3", features = ["dilithium5"] }
hex = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
anyhow = "1.0"

完整示例

use msg_chain_crypto::dilithium::{DilithiumKeypair, DilithiumSignature};
use anyhow::Result;

struct DilithiumWallet {
    keypair: DilithiumKeypair,
}

impl DilithiumWallet {
    fn generate() -> Result<Self> {
        let keypair = DilithiumKeypair::generate()?;
        Ok(Self { keypair })
    }

    fn from_private_key(hex_key: &str) -> Result<Self> {
        let bytes = hex::decode(hex_key)?;
        let keypair = DilithiumKeypair::from_secret_key_bytes(&bytes)?;
        Ok(Self { keypair })
    }

    fn address(&self) -> String {
        self.keypair.address()
    }

    fn public_key_hex(&self) -> String {
        hex::encode(self.keypair.public_key_bytes())
    }

    fn sign_tx(&self, tx_bytes: &[u8]) -> Result<Vec<u8>> {
        let signature = self.keypair.sign(tx_bytes)?;
        Ok(signature.to_bytes())
    }

    fn verify_tx(&self, tx_bytes: &[u8], signature: &[u8]) -> bool {
        let sig = match DilithiumSignature::from_bytes(signature) {
            Ok(s) => s,
            Err(_) => return false,
        };
        self.keypair.verify(tx_bytes, &sig)
    }
}

fn main() -> Result<()> {
    let wallet = DilithiumWallet::generate()?;
    println!("地址: {}", wallet.address());
    println!("公钥: {}", wallet.public_key_hex());

    let tx = b"{\"chain_id\":\"msg-chain-1\",\"nonce\":1}";
    let signature = wallet.sign_tx(tx)?;
    println!("签名长度: {} 字节", signature.len());
    assert!(wallet.verify_tx(tx, &signature));
    println!("签名验证通过");
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_key_generation() {
        let wallet = DilithiumWallet::generate().unwrap();
        assert!(wallet.address().starts_with("msg1"));
        assert_eq!(wallet.keypair.public_key_bytes().len(), 2592);
    }

    #[test]
    fn test_sign_verify() {
        let wallet = DilithiumWallet::generate().unwrap();
        let msg = b"test message";
        let sig = wallet.sign_tx(msg).unwrap();
        assert!(wallet.verify_tx(msg, &sig));
        assert!(!wallet.verify_tx(b"tampered", &sig));
    }
}

6.3 Python SDK完整示例

安装

pip install msgchain-sdk

完整钱包示例

#!/usr/bin/env python3
import os
import json
from msgchain_sdk.crypto import Dilithium5


class DilithiumWallet:
    def __init__(self, keypair: Dilithium5):
        self._keypair = keypair

    @classmethod
    def generate(cls) -> "DilithiumWallet":
        return cls(Dilithium5.generate())

    @classmethod
    def from_private_key(cls, hex_key: str) -> "DilithiumWallet":
        return cls(Dilithium5.from_hex(hex_key))

    @property
    def address(self) -> str:
        return self._keypair.address

    @property
    def public_key_hex(self) -> str:
        return self._keypair.public_key.hex()

    @property
    def private_key_hex(self) -> str:
        return self._keypair.private_key.hex()

    def sign(self, data: bytes) -> bytes:
        return self._keypair.sign(data)

    def verify(self, data: bytes, signature: bytes) -> bool:
        return self._keypair.verify(data, signature)

    def to_dict(self) -> dict:
        return {
            "algorithm": "dilithium-5",
            "address": self.address,
            "public_key": self.public_key_hex,
            "private_key": self.private_key_hex,
        }


def main():
    print("MSG Chain Dilithium-5 Wallet Demo")
    print("=" * 50)

    wallet = DilithiumWallet.generate()
    print(f"地址: {wallet.address}")
    print(f"公钥: {wallet.public_key_hex[:64]}...")

    message = b"Hello, MSG Chain with Post-Quantum Security!"
    signature = wallet.sign(message)
    print(f"签名长度: {len(signature)} 字节")
    print(f"验证结果: {wallet.verify(message, signature)}")
    print(f"篡改验证: {wallet.verify(b'tampered', signature)}")


if __name__ == "__main__":
    main()

RPC交互

from msgchain_sdk.crypto import Dilithium5
from msgchain_sdk.rpc import RPCClient
from msgchain_sdk.tx import TransactionBuilder


class DilithiumTxSigner:
    def __init__(self, rpc_url: str = "https://rpc.msgchain.org"):
        self.rpc = RPCClient(rpc_url)

    def sign_and_broadcast(self, keypair: Dilithium5, msgs: list, gas: int = 200000) -> dict:
        account = self.rpc.get_account(keypair.address)
        chain_id = self.rpc.get_chain_id()

        tx_builder = TransactionBuilder(
            chain_id=chain_id,
            account_number=account.account_number,
            sequence=account.sequence,
        )
        unsigned_tx = tx_builder.build(msgs=msgs, gas=gas)
        sign_bytes = tx_builder.get_sign_bytes(unsigned_tx)
        signature = keypair.sign(sign_bytes)

        signed_tx = tx_builder.attach_signature(
            unsigned_tx, signature.hex(), keypair.public_key.hex()
        )
        return self.rpc.broadcast_tx(signed_tx)

6.4 TypeScript SDK完整示例

安装

npm install @msg-chain/crypto@alpha

钱包类

import { DilithiumKeypair, DilithiumKeyExport } from '@msg-chain/crypto';
import { generateMnemonic, mnemonicToSeed } from '@msg-chain/crypto/bip39';
import { deriveDilithiumKey, DilithiumHDPath } from '@msg-chain/crypto/hd';

class DilithiumWallet {
  private keypair: DilithiumKeypair;

  constructor(keypair: DilithiumKeypair) {
    this.keypair = keypair;
  }

  static generate(): DilithiumWallet {
    return new DilithiumWallet(DilithiumKeypair.generate());
  }

  static fromPrivateKey(hexKey: string): DilithiumWallet {
    return new DilithiumWallet(DilithiumKeypair.fromHex(hexKey));
  }

  static fromMnemonic(mnemonic: string, passphrase = ''): DilithiumWallet {
    const seed = mnemonicToSeed(mnemonic, passphrase);
    const privateKey = deriveDilithiumKey(seed, DilithiumHDPath.cosmos(0, 0));
    return new DilithiumWallet(DilithiumKeypair.fromBytes(privateKey));
  }

  get address(): string { return this.keypair.getAddress(); }
  get publicKeyHex(): string { return this.keypair.getPublicKey('hex'); }
  get privateKeyHex(): string { return this.keypair.getPrivateKey('hex'); }

  sign(data: Uint8Array): Uint8Array { return this.keypair.sign(data); }
  verify(data: Uint8Array, signature: Uint8Array): boolean {
    return this.keypair.verify(data, signature);
  }

  toJSON(): DilithiumKeyExport { return this.keypair.toJSON(); }
  static fromJSON(json: DilithiumKeyExport): DilithiumWallet {
    return new DilithiumWallet(DilithiumKeypair.fromJSON(json));
  }
}

async function main() {
  const wallet = DilithiumWallet.generate();
  console.log(`地址: ${wallet.address}`);

  const message = Buffer.from('Hello Dilithium-5!');
  const sig = wallet.sign(message);
  console.log(`签名: ${wallet.verify(message, sig)}`);
}

main().catch(console.error);

React Hooks

import { useState, useCallback } from 'react';
import { DilithiumKeypair } from '@msg-chain/crypto';

export function useDilithiumWallet() {
  const [keypair, setKeypair] = useState<DilithiumKeypair | null>(null);
  const [address, setAddress] = useState('');

  const connect = useCallback(async (privateKeyHex?: string) => {
    let kp: DilithiumKeypair;
    if (privateKeyHex) {
      kp = DilithiumKeypair.fromHex(privateKeyHex);
    } else {
      kp = DilithiumKeypair.generate();
    }
    setKeypair(kp);
    setAddress(kp.getAddress());
  }, []);

  const signMessage = useCallback(async (message: string): Promise<string> => {
    if (!keypair) throw new Error('Wallet not connected');
    const sig = keypair.sign(Buffer.from(message));
    return Buffer.from(sig).toString('hex');
  }, [keypair]);

  const disconnect = useCallback(() => {
    setKeypair(null);
    setAddress('');
  }, []);

  return { address, keypair, connect, signMessage, disconnect };
}

第七章:交易构造与广播

7.1 Cosmos SDK StdTx构造

Go: 完整交易构造

package main

import (
    "fmt"
    "log"

    "github.com/cosmos/cosmos-sdk/client"
    "github.com/cosmos/cosmos-sdk/client/tx"
    sdk "github.com/cosmos/cosmos-sdk/types"
    "github.com/cosmos/cosmos-sdk/types/tx/signing"
    authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
    banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
    "github.com/msgchain/msgchain/crypto/dilithium"
)

type UnsignedTx struct {
    SignBytes []byte `json:"sign_bytes"`
}

func BuildUnsignedTx(
    fromAddr sdk.AccAddress, toAddr sdk.AccAddress,
    amount sdk.Coins, gasLimit uint64, fee sdk.Coins,
    memo string, accountNumber, sequence uint64, chainID string,
) (*UnsignedTx, error) {
    msgSend := banktypes.NewMsgSend(fromAddr, toAddr, amount)
    txConfig := authtx.NewTxConfig(
        codec.NewProtoCodec(codec.NewInterfaceRegistry()),
        []signing.SignMode{signing.SignMode_SIGN_MODE_DIRECT},
    )
    txBuilder := txConfig.NewTxBuilder()
    txBuilder.SetMsgs(msgSend)
    txBuilder.SetGasLimit(gasLimit)
    txBuilder.SetFeeAmount(fee)
    txBuilder.SetMemo(memo)

    signBytes, err := txConfig.SignModeHandler().GetSignBytes(
        signing.SignMode_SIGN_MODE_DIRECT,
        signing.SignerData{ChainID: chainID, AccountNumber: accountNumber, Sequence: sequence},
        txBuilder.GetTx(),
    )
    if err != nil {
        return nil, fmt.Errorf("获取签名字节失败: %w", err)
    }
    return &UnsignedTx{SignBytes: signBytes}, nil
}

func SignTxWithDilithium(unsignedTx *UnsignedTx, privKey *dilithium.PrivKeyDilithium5) ([]byte, []byte, error) {
    signature, err := privKey.Sign(unsignedTx.SignBytes)
    if err != nil {
        return nil, nil, fmt.Errorf("Dilithium签名失败: %w", err)
    }
    return signature, privKey.PubKey().Bytes(), nil
}

func EncodeSignedTx(unsignedTx *UnsignedTx, signature, pubKeyBytes []byte) ([]byte, error) {
    txConfig := authtx.NewTxConfig(
        codec.NewProtoCodec(codec.NewInterfaceRegistry()),
        []signing.SignMode{signing.SignMode_SIGN_MODE_DIRECT},
    )
    txBuilder := txConfig.NewTxBuilder()

    pubKey := &dilithium.PubKeyDilithium5{}
    pubKey.UnmarshalAmino(pubKeyBytes)

    sig := signing.SignatureV2{
        PubKey: pubKey,
        Data:   &signing.SingleSignatureData{SignMode: signing.SignMode_SIGN_MODE_DIRECT, Signature: signature},
        Sequence: 0,
    }
    txBuilder.SetSignatures(sig)
    return txConfig.TxEncoder()(txBuilder.GetTx())
}

func main() {
    privKey, _ := dilithium.GenKeyV5(rand.Reader)
    fromAddr := privKey.PubKey().Address()
    toAddr := sdk.AccAddress([]byte("destination_address_20bytes____"))

    unsignedTx, _ := BuildUnsignedTx(fromAddr, toAddr,
        sdk.NewCoins(sdk.NewInt64Coin("umsg", 1000000)),
        200000, sdk.NewCoins(sdk.NewInt64Coin("umsg", 5000)),
        "Dilithium-5签名交易", 42, 1, "msg-chain-1")

    sig, pubBytes, _ := SignTxWithDilithium(unsignedTx, privKey)
    txBytes, _ := EncodeSignedTx(unsignedTx, sig, pubBytes)

    fmt.Printf("签名大小: %d 字节\n", len(sig))
    fmt.Printf("交易大小: %d 字节\n", len(txBytes))
}

7.2 混合签名(Dilithium-5 + Secp256k1)

package main

import (
    "crypto/rand"
    "fmt"
    "log"

    "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
    "github.com/msgchain/msgchain/crypto/dilithium"
)

type MixedSignedTx struct {
    DilithiumSig []byte `json:"dilithium_signature"`
    Secp256k1Sig []byte `json:"secp256k1_signature"`
}

func CreateMixedSignatureTx(
    dilithiumPriv *dilithium.PrivKeyDilithium5,
    secp256k1Priv *secp256k1.PrivKeySecp256k1,
    txBytes []byte,
) (*MixedSignedTx, error) {
    dilithiumSig, err := dilithiumPriv.Sign(txBytes)
    if err != nil {
        return nil, fmt.Errorf("Dilithium签名失败: %w", err)
    }
    secp256k1Sig, err := secp256k1Priv.Sign(txBytes)
    if err != nil {
        return nil, fmt.Errorf("Secp256k1签名失败: %w", err)
    }
    return &MixedSignedTx{DilithiumSig: dilithiumSig, Secp256k1Sig: secp256k1Sig}, nil
}

func main() {
    dilithiumPriv, _ := dilithium.GenKeyV5(rand.Reader)
    secp256k1Priv := secp256k1.GenPrivKey()
    txBytes := []byte("混合签名交易示例")

    mixed, _ := CreateMixedSignatureTx(dilithiumPriv, secp256k1Priv, txBytes)
    fmt.Printf("Dilithium-5签名: %d 字节\n", len(mixed.DilithiumSig))
    fmt.Printf("Secp256k1签名: %d 字节\n", len(mixed.Secp256k1Sig))
}

7.3 Gas开销计算

package main

import (
    "fmt"
    "math"
)

const (
    Dilithium5SignatureSize = 4595
    Dilithium5PubKeySize    = 2592
    Secp256k1SignatureSize  = 71
    Secp256k1PubKeySize     = 33
    GasCostPerByte          = 10
    GasCostPerSign          = 1000
    GasCostPerVerify        = 500
)

func CalculateTxGas(baseTxSize int, numDilithiumSigners, numSecp256k1Signers int) uint64 {
    totalSize := baseTxSize +
        numDilithiumSigners*(Dilithium5SignatureSize+Dilithium5PubKeySize) +
        numSecp256k1Signers*(Secp256k1SignatureSize+Secp256k1PubKeySize)

    totalGas := uint64(totalSize*GasCostPerByte) +
        uint64((numDilithiumSigners+numSecp256k1Signers)*(GasCostPerSign+GasCostPerVerify)) +
        50000

    return uint64(math.Ceil(float64(totalGas)/1000) * 1000)
}

func main() {
    gas1 := CalculateTxGas(150, 1, 0)
    gas2 := CalculateTxGas(150, 0, 1)
    fmt.Printf("MsgSend + Dilithium-5: %d gas\n", gas1)
    fmt.Printf("MsgSend + Secp256k1: %d gas\n", gas2)
    fmt.Printf("Gas比例: %.2fx\n", float64(gas1)/float64(gas2))
}

7.4 RPC广播

REST API

SIGNED_TX_B64=$(base64 -w0 signed_tx.bin)
curl -X POST https://rpc.msgchain.org/cosmos/tx/v1beta1/txs \
  -H "Content-Type: application/json" \
  -d '{"tx_bytes": "'$SIGNED_TX_B64'", "mode": "BROADCAST_MODE_SYNC"}'

Go RPC

package main

import (
    "bytes" "encoding/base64" "encoding/json" "fmt" "net/http"
)

type BroadcastReq struct {
    TxBytes string `json:"tx_bytes"`
    Mode    string `json:"mode"`
}

type BroadcastResp struct {
    TxResponse struct {
        TxHash string `json:"txhash"`
        Code   uint32 `json:"code"`
        RawLog string `json:"raw_log"`
    } `json:"tx_response"`
}

func BroadcastTxREST(txBytes []byte, endpoint string) (*BroadcastResp, error) {
    payload := BroadcastReq{
        TxBytes: base64.StdEncoding.EncodeToString(txBytes),
        Mode:    "BROADCAST_MODE_SYNC",
    }
    jsonData, _ := json.Marshal(payload)
    resp, err := http.Post(endpoint+"/cosmos/tx/v1beta1/txs", "application/json", bytes.NewReader(jsonData))
    if err != nil {
        return nil, fmt.Errorf("RPC请求失败: %w", err)
    }
    defer resp.Body.Close()
    var result BroadcastResp
    json.NewDecoder(resp.Body).Decode(&result)
    return &result, nil
}

Python RPC

import requests
import base64


class DilithiumTxBroadcaster:
    def __init__(self, rpc_url: str = "https://rpc.msgchain.org"):
        self.rpc_url = rpc_url.rstrip("/")

    def broadcast_sync(self, signed_tx_bytes: bytes) -> dict:
        tx_b64 = base64.b64encode(signed_tx_bytes).decode()
        resp = requests.post(
            f"{self.rpc_url}/cosmos/tx/v1beta1/txs",
            json={"tx_bytes": tx_b64, "mode": "BROADCAST_MODE_SYNC"},
        )
        resp.raise_for_status()
        return resp.json()

第八章:密钥轮换与迁移

8.1 验证者共识密钥轮换

package main

import (
    "fmt"
    "log"

    "github.com/cosmos/cosmos-sdk/x/staking/types"
    "github.com/msgchain/msgchain/crypto/dilithium"
)

func RotateValidatorKey(oldPrivKey *dilithium.PrivKeyDilithium5, homeDir string) (*dilithium.PrivKeyDilithium5, error) {
    newPrivKey, err := dilithium.GenKeyV5(rand.Reader)
    if err != nil {
        return nil, fmt.Errorf("新密钥生成失败: %w", err)
    }

    newPubKey := newPrivKey.PubKey()
    rotateMsg := types.NewMsgRotateConsensusKey(oldPrivKey.PubKey().Address(), newPubKey)

    signBytes := rotateMsg.GetSignBytes()
    signature, err := oldPrivKey.Sign(signBytes)
    if err != nil {
        return nil, fmt.Errorf("轮换消息签名失败: %w", err)
    }

    valid := oldPrivKey.PubKey().VerifySignature(signBytes, signature)
    if !valid {
        return nil, fmt.Errorf("轮换消息签名验证失败")
    }

    if err := saveValidatorKey(newPrivKey, homeDir); err != nil {
        return nil, fmt.Errorf("新密钥保存失败: %w", err)
    }

    fmt.Printf("验证者密钥轮换成功!\n")
    fmt.Printf("旧地址: %s\n", oldPrivKey.PubKey().Address().String())
    fmt.Printf("新地址: %s\n", newPubKey.Address().String())
    return newPrivKey, nil
}

使用CLI轮换密钥

./bin/quantum_node_linux keys add new-consensus-key \
    --key-type dilithium-5 \
    --keyring-backend file \
    --home ~/.msgchain

./bin/quantum_node_linux tx staking rotate-consensus-key \
    --pubkey $(./bin/quantum_node_linux keys show new-consensus-key --pubkey) \
    --from validator \
    --chain-id msg-chain-1 \
    --fees 10000umsg \
    --gas 300000 \
    --home ~/.msgchain

cp ~/.msgchain/config/priv_validator_key.json ~/.msgchain/config/priv_validator_key.json.bak
sudo systemctl restart msgchain

8.2 从Secp256k1迁移到Dilithium-5

package main

import (
    "fmt"
    "log"

    "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
    "github.com/msgchain/msgchain/crypto/dilithium"
)

func MigrateSecp256k1ToDilithium5(
    oldPrivKey *secp256k1.PrivKeySecp256k1,
) (*dilithium.PrivKeyDilithium5, error) {
    newPrivKey, err := dilithium.GenKeyV5(rand.Reader)
    if err != nil {
        return nil, fmt.Errorf("Dilithium-5密钥生成失败: %w", err)
    }

    // 用旧密钥签名迁移确认消息
    migrateMsg := []byte("MIGRATE_TO_DILITHIUM5")
    oldSig, err := oldPrivKey.Sign(migrateMsg)
    if err != nil {
        return nil, fmt.Errorf("迁移确认签名失败: %w", err)
    }
    oldPubKey := oldPrivKey.PubKey()
    if !oldPubKey.VerifySignature(migrateMsg, oldSig) {
        return nil, fmt.Errorf("迁移确认签名验证失败")
    }

    fmt.Printf("迁移成功!\n")
    fmt.Printf("旧Secp256k1地址: %s\n", oldPubKey.Address().String())
    fmt.Printf("新Dilithium-5地址: %s\n", newPrivKey.PubKey().Address().String())
    return newPrivKey, nil
}

第九章:安全最佳实践

9.1 熵源要求

后量子密码学对熵源质量要求更高。Dilithium-5的密钥生成需要高质量随机数:

package main

import (
    "crypto/rand"
    "fmt"
    "log"

    "github.com/msgchain/msgchain/crypto/dilithium"
)

func main() {
    // 推荐:使用操作系统crypto/rand
    privKey, err := dilithium.GenKeyV5(rand.Reader)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("地址: %s\n", privKey.PubKey().Address().String())

    // 使用硬件随机数生成器
    // hwrng, _ := os.Open("/dev/hwrng")
    // privKey, err = dilithium.GenKeyV5(hwrng)
}

9.2 侧信道攻击缓解

// 恒定时间比较函数(防止定时攻击)
func ConstantTimeEqual(a, b []byte) bool {
    if len(a) != len(b) {
        return false
    }
    var v byte
    for i := 0; i < len(a); i++ {
        v |= a[i] ^ b[i]
    }
    return v == 0
}

// 验证签名时使用恒定时间比较
func VerifyConstantTime(pubKey *dilithium.PubKeyDilithium5, msg, sig []byte) bool {
    expectedValid := pubKey.VerifySignature(msg, sig)
    // 即使结果无效,也运行完整计算以防止短路
    _ = pubKey.VerifySignature(append(msg, 0), sig)
    return expectedValid
}

9.3 密钥备份加密

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "crypto/sha256"
    "fmt"
    "io"

    "golang.org/x/crypto/pbkdf2"
)

func EncryptPrivateKey(privKeyBytes []byte, passphrase string) ([]byte, error) {
    salt := make([]byte, 32)
    if _, err := rand.Read(salt); err != nil {
        return nil, err
    }

    key := pbkdf2.Key([]byte(passphrase), salt, 600000, 32, sha256.New)
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    aead, err := cipher.NewGCM(block)
    if err != nil {
        return nil, err
    }

    nonce := make([]byte, aead.NonceSize())
    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
        return nil, err
    }

    ciphertext := aead.Seal(nonce, nonce, privKeyBytes, nil)
    return append(salt, ciphertext...), nil
}

func DecryptPrivateKey(encrypted []byte, passphrase string) ([]byte, error) {
    salt := encrypted[:32]
    ciphertext := encrypted[32:]

    key := pbkdf2.Key([]byte(passphrase), salt, 600000, 32, sha256.New)
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    aead, err := cipher.NewGCM(block)
    if err != nil {
        return nil, err
    }

    nonceSize := aead.NonceSize()
    nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
    return aead.Open(nil, nonce, ciphertext, nil)
}

9.4 测试后量子功能

# 运行后量子相关测试
make test-quantum

# 运行全部测试
make test

# 构建后量子节点
make build-linux
# 输出: bin/genesis_node_linux, bin/quantum_node_linux

9.5 生产环境检查清单


第十章:完整工作流

10.1 端到端流程

1. 密钥生成 -> 2. 密钥存储 -> 3. 交易构造 -> 4. 交易签名 -> 5. 广播 -> 6. 验证

Go: 完整流程

package main

import (
    "crypto/rand"
    "fmt"
    "log"

    "github.com/cosmos/cosmos-sdk/types/tx/signing"
    authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
    banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
    "github.com/msgchain/msgchain/crypto/dilithium"
    sdk "github.com/cosmos/cosmos-sdk/types"
)

func main() {
    // Step 1: 密钥生成
    privKey, err := dilithium.GenKeyV5(rand.Reader)
    if err != nil {
        log.Fatal(err)
    }
    pubKey := privKey.PubKey()
    fmt.Printf("地址: %s\n", pubKey.Address().String())

    // Step 2: 构造交易
    txConfig := authtx.NewTxConfig(nil, []signing.SignMode{signing.SignMode_SIGN_MODE_DIRECT})
    txBuilder := txConfig.NewTxBuilder()
    txBuilder.SetMsgs(banktypes.NewMsgSend(
        pubKey.Address(),
        sdk.AccAddress([]byte("recipient_addr_20bytes__")),
        sdk.NewCoins(sdk.NewInt64Coin("umsg", 1000)),
    ))
    txBuilder.SetGasLimit(200000)
    txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewInt64Coin("umsg", 5000)))

    // Step 3: 签名
    signBytes, _ := txConfig.SignModeHandler().GetSignBytes(
        signing.SignMode_SIGN_MODE_DIRECT,
        signing.SignerData{ChainID: "msg-chain-1", AccountNumber: 0, Sequence: 0},
        txBuilder.GetTx(),
    )
    sig, _ := privKey.Sign(signBytes)

    // Step 4: 附加签名
    txBuilder.SetSignatures(signing.SignatureV2{
        PubKey: pubKey,
        Data:   &signing.SingleSignatureData{SignMode: signing.SignMode_SIGN_MODE_DIRECT, Signature: sig},
    })

    // Step 5: 编码并广播
    txBytes, _ := txConfig.TxEncoder()(txBuilder.GetTx())
    fmt.Printf("交易大小: %d 字节\n", len(txBytes))

    // Step 6: 验证
    valid := pubKey.VerifySignature(signBytes, sig)
    fmt.Printf("签名验证: %v\n", valid)
    // broadcastTx(txBytes)
}

Rust: 完整流程

use msg_chain_crypto::dilithium::DilithiumKeypair;
use msg_chain_sdk::tx::{TxBuilder, SignMode};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let keypair = DilithiumKeypair::generate()?;
    println!("地址: {}", keypair.address());

    let tx = TxBuilder::new("msg-chain-1")
        .add_message(bank::MsgSend {
            from_address: keypair.address(),
            to_address: "msg1x9v6xkqf4u5a3d7e2n8lkvgzck8wkz3".into(),
            amount: vec![coin("1000000", "umsg")],
        })
        .set_gas(200000)
        .set_fee(vec![coin("5000", "umsg")])
        .set_account_number(0)
        .set_sequence(0)
        .set_sign_mode(SignMode::Direct)
        .build()?;

    let signed_tx = tx.sign(&keypair)?;
    let tx_bytes = signed_tx.to_bytes();
    println!("交易大小: {} 字节", tx_bytes.len());

    let client = msg_chain_sdk::Client::new("https://rpc.msgchain.org");
    let response = client.broadcast_tx(tx_bytes).await?;
    println!("交易哈希: {}", response.tx_hash);
    Ok(())
}

Python: 完整流程

from msgchain_sdk.crypto import Dilithium5
from msgchain_sdk.rpc import RPCClient
from msgchain_sdk.tx import TransactionBuilder

# Step 1: 密钥生成
keypair = Dilithium5.generate()
print(f"地址: {keypair.address}")

# Step 2-5: 构建、签名、广播
rpc = RPCClient("https://rpc.msgchain.org")
account = rpc.get_account(keypair.address)
chain_id = rpc.get_chain_id()

tx_builder = TransactionBuilder(
    chain_id=chain_id,
    account_number=account.account_number,
    sequence=account.sequence,
)

unsigned_tx = tx_builder.build(
    msgs=[{
        "@type": "/cosmos.bank.v1beta1.MsgSend",
        "from_address": keypair.address,
        "to_address": "msg1x9v6xkqf4u5a3d7e2n8lkvgzck8wkz3",
        "amount": [{"denom": "umsg", "amount": "1000000"}],
    }],
    gas=200000,
)

sign_bytes = tx_builder.get_sign_bytes(unsigned_tx)
signature = keypair.sign(sign_bytes)
signed_tx = tx_builder.attach_signature(unsigned_tx, signature.hex(), keypair.public_key.hex())

result = rpc.broadcast_tx(signed_tx)
print(f"交易哈希: {result['txhash']}")

# Step 6: 验证
assert keypair.verify(sign_bytes, signature)
print("签名验证通过")

TypeScript: 完整流程

import { DilithiumKeypair } from '@msg-chain/crypto';
import { SigningStargateClient } from '@cosmjs/stargate';
import { Coin } from '@cosmjs/amino';

async function main() {
  // Step 1: 密钥生成
  const keypair = DilithiumKeypair.generate();
  console.log(`地址: ${keypair.getAddress()}`);

  // Step 2-5: 构建并广播
  const client = await SigningStargateClient.connectWithSigner(
    'https://rpc.msgchain.org',
    keypairToSigner(keypair),
  );
  const result = await client.sendTokens(
    keypair.getAddress(),
    'msg1x9v6xkqf4u5a3d7e2n8lkvgzck8wkz3',
    [{ denom: 'umsg', amount: '1000000' } as Coin],
    { amount: [{ denom: 'umsg', amount: '5000' }], gas: '200000' },
    'Dilithium-5 transaction',
  );
  console.log(`交易哈希: ${result.transactionHash}`);

  // Step 6: 验证
  const message = new Uint8Array([/* sign bytes */]);
  const valid = keypair.verify(message, result.msgResponses[0].signature);
  console.log(`验证: ${valid}`);
}

附录

A: FIPS 204合规性说明

Dilithium-5在MSG Chain中的实现遵循NIST FIPS 204(ML-DSA)标准:

B: 密钥格式参考

字段 类型 大小 说明
PrivKeyDilithium5 Amino/Protobuf ~4,864 B 包含rho, rho', K, s1, s2, t0
PubKeyDilithium5 Amino/Protobuf 2,592 B 包含rho, t1
地址 bech32 (msg) 20 B SHA3-512(pubkey)[:20] + SHA-256 checksum
签名 FIPS 204 4,595 B 包含c, z, h

C: 类型注册标识符

类型 Amino类型名 Protobuf类型URL
PubKeyDilithium5 tendermint/PubKeyDilithium5 /msgchain.crypto.dilithium.PubKey
PrivKeyDilithium5 tendermint/PrivKeyDilithium5 /msgchain.crypto.dilithium.PrivKey

D: 常见问题

Q: Dilithium-5签名比Secp256k1大很多,是否会影响链性能?

A: Dilithium-5签名约4,595字节(Secp256k1约70字节),确实会增大交易大小。但MSG Chain的Gas模型已为此优化,且验证速度快5倍,对全节点更友好。

Q: 如何从Secp256k1迁移到Dilithium-5?

A: MSG Chain支持双轨运行。用户可通过密钥轮换交易逐步迁移,无需一次性切换。详见第八章。

Q: Dilithium-5是否需要特殊硬件?

A: 不需要。Dilithium-5在标准x86-64处理器上运行良好。对于生产验证者,建议使用支持Dilithium-5的HSM。

Q: BIP39助记词能否用于Dilithium-5?

A: 可以。MSG Chain支持从BIP39助记词派生Dilithium-5密钥,使用标准BIP44路径m/44'/118'/0'/0/0。

Q: Quantum_node和genesis_node有何区别?

A: genesis_node_linux是包含Dilithium-5支持的标准节点,quantum_node_linux是纯后量子节点(移除Secp256k1兼容层)。


MSG Chain Whitepaper | https://msgchain.org/whitepaper | 代码库实际状态,不代表生产可用