cosmos tendermint irisnet 的amino編解碼解析

elvin5發表於2019-07-02

amino編解碼,加解密


amino編解碼

amino編解碼可以稱之為protobuf3的一次升級,再protobuf3的基礎上新增了對interface的直接支援,也就說:可以直接把序列化好的資料unmarshal到一個介面物件中,但是這需要把定義的interface和實現interface的物件事前註冊到amino的編解碼物件中。具體例子如下:

cdc := amino.NewCodec()
cdc.RegisterInterface((*MyInterface1)(nil), nil)
cdc.RegisterInterface((*MyInterface2)(nil), nil)
cdc.RegisterConcrete(MyStruct1{}, "com.tendermint/MyStruct1", nil)
cdc.RegisterConcrete(MyStruct2{}, "com.tendermint/MyStruct2", nil)
cdc.RegisterConcrete(&MyStruct3{}, "anythingcangoinhereifitsunique", nil)

請注意,註冊介面時要註冊介面的指標,實現介面物件的註冊要注意的是實現方法的接受物件是指標還是物件本身。

註冊介面實現物件時需要提供一個名字。這個名字需要全域性唯一,為了防止大量註冊物件時的碰撞問題,會對這個名字進行一定的運算,從而計算出字首,字首一般是4個位元組,如果為了取得更好的防碰撞性,也可以在4個位元組的前面加上3個位元組的消岐位元組,字首位元組和消岐位元組都不能是0,具體的演算法如下: 對註冊時的名字進行sha256計算:

> hash := sha256("com.tendermint.consensus/MyConcreteName")
> hex.EncodeBytes(hash) // 0x{00 00 A8 FC 54 00 00 00 BB 9C 83 DD ...} (example)

去掉0

> rest = dropLeadingZeroBytes(hash) // 0x{A8 FC 54 00 00 00 BB 9C 83 DD ...}
> disamb = rest[0:3]
> rest = dropLeadingZeroBytes(rest[3:])
> prefix = rest[0:4]

結果如下:

> <0xA8 0xFC 0x54> [0xBB 0x9C 9x83 9xDD] // <Disamb Bytes> and [Prefix Bytes]

詳細標準請參考:amino spec

cosmos專案加解密

Ed25519,Secp256k1都稱之為橢圓加密演算法,只是他們採用的引數不一樣,比特幣和以太坊都是採用了Secp256k1演算法,而Cosmos專案同時採用了這兩種演算法,驗證者的consensus簽名採用的是Ed25519非對稱加密,使用者交易的簽名採用的是Secp256k1非對稱加密。

在cosmos專案中定義了兩個介面:

//tendermint/crypto/crypto.go
type PubKey interface {
    Address() Address
    Bytes() []byte
    VerifyBytes(msg []byte, sig []byte) bool
    Equals(PubKey) bool
}

type PrivKey interface {
    Bytes() []byte
    Sign(msg []byte) ([]byte, error)
    PubKey() PubKey
    Equals(PrivKey) bool
}

不管Ed25519還是Secp256k1的公私鑰都實現以上兩個介面,通過一下程式碼進行註冊,可以對公私鑰進行很方便的序列化。

//tendermint/crypto/encoding/amino/amino.go
// RegisterAmino registers all crypto related types in the given (amino) codec.
func RegisterAmino(cdc *amino.Codec) {
    // These are all written here instead of
    cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
    cdc.RegisterConcrete(ed25519.PubKeyEd25519{},
        "tendermint/PubKeyEd25519", nil)
    cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
        "tendermint/PubKeySecp256k1", nil)

    cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
    cdc.RegisterConcrete(ed25519.PrivKeyEd25519{},
        "tendermint/PrivKeyEd25519", nil)
    cdc.RegisterConcrete(secp256k1.PrivKeySecp256k1{},
        "tendermint/PrivKeySecp256k1", nil)
}

按照amino的規範,對註冊名字進行sha256,並擷取[3:4]可以得到各個公私鑰的字首,也可以用以下快捷表進行換算。

Type Name Prefix Length Notes
PubKeyEd25519 tendermint/PubKeyEd25519 0x1624DE64 0x20
PubKeySecp256k1 tendermint/PubKeySecp256k1 0xEB5AE987 0x21
PrivKeyEd25519 tendermint/PrivKeyEd25519 0xA3288910 0x40
PrivKeySecp256k1 tendermint/PrivKeySecp256k1 0xE1B0F79B 0x20
例如:
經過amino編碼的33個位元組(21位元組16進位制)Secp256k1公鑰:
020BD40F225A57ED383B440CF073BC5539D0341F5767D2BF2D78406D00475A2EE9
按照以上表格進行編碼得到:
EB5AE98721020BD40F225A57ED383B440CF073BC5539D0341F5767D2BF2D78406D00475A2EE9

我們可以從字首輕易的看出到底是公鑰還是私鑰,也可以很輕易的辨認出是那種非對稱加密。

地址

  • Ed25519地址演算法 address = SHA256(pubkey)[:20]

  • Secp256k1地址演算法 address = RIPEMD160(SHA256(pubkey)) 和比特幣一樣。RIPEMD160也是一種hash演算法。

bech32

為了增加地址的魯棒性,可以更好的進行正確性檢查,cosmos採用bech32格式來表示地址和公鑰,當然,程式內部很多地方還是使用16進位制編碼或者base64編碼來表示的。bech32的字首我們稱之為:human readable part(HRP),以下表格詳細解釋了HRP所表示的意思。

HRP Definition
cosmos Cosmos 賬戶地址,本地資料庫
cosmospub Cosmos 賬戶公鑰,本地資料庫
cosmosvalcons Cosmos 驗證者共識地址,也就是來自於priv_validator.json檔案
cosmosvalconspub Cosmos 驗證者共識公鑰,也就是來自於priv_validator.json檔案
cosmosvaloper Bond驗證者共識地址的賬戶地址
cosmosvaloperpub Bond驗證者共識地址的賬戶公鑰

更多cosmos, tendermint筆記請訪問並關注我的github:https://github.com/elvin-du/cosmos-sdk-code-analysis

相關文章