Filecoin Lotus 链消息解析

Filecoin中的链消息的参数是通过CBOR方式编码的,在解码前是不知道里面存的具体是什么数据。CBOR将对象编码成二进制数据,在传输过程中以base64编码方式传输,所以在解码是现将base64格式字符串解码,然后在解析成对应类型。

这里以PreCommitSector提交扇区消息为例,说明如何解析链消息中的Params字段,其他字段按原来JSON格式解析。

本文已在Ahezime上首发: ahezime.com/blog/post/2

链消息格式

type Message struct {
    Version uint64

    To   address.Address
    From address.Address

    Nonce uint64

    Value abi.TokenAmount

    GasLimit   int64
    GasFeeCap  abi.TokenAmount
    GasPremium abi.TokenAmount

    Method abi.MethodNum
    Params []byte
}

消息中的Params为消息参数,在传输过程中该字段会被编码成CBOR格式。

PreCommitSector消息对应的参数Params类型为: SectorPreCommitInfo,源码路径: github.com/filecoin-project/specs-actors/actors/builtin/miner/miner_state -> SectorPreCommitInfo。

SectorPreCommitInfo:

// Information provided by a miner when pre-committing a sector.
type SectorPreCommitInfo struct {
    SealProof       abi.RegisteredSealProof
    SectorNumber    abi.SectorNumber
    SealedCID       cid.Cid `checked:"true"` // CommR
    SealRandEpoch   abi.ChainEpoch
    DealIDs         []abi.DealID
    Expiration      abi.ChainEpoch
    ReplaceCapacity bool // Whether to replace a "committed capacity" no-deal sector (requires non-empty DealIDs)
    // The committed capacity sector to replace, and it's deadline/partition location
    ReplaceSectorDeadline  uint64
    ReplaceSectorPartition uint64
    ReplaceSectorNumber    abi.SectorNumber
}

查询一个PreCommitSector消息

// Get message by using lotus CLI


gt; ~/hlm-miner/script/lotus/lotus-user# ./lotus.sh chain getmessage bafy2bzacec3x5rlwdveh46m55dbe47g7bmzlhncpczwwgcuoowvazumpf25se
{
"Version": 0,
"To": "f03176",
"From": "f3ruolcbftfwhudz2qexbkylbj2y2awtxeopovv5jeoafikmstk4j7bwowynmnes66xv6fselgolug55uuxp4q",
"Nonce": 687292,
"Value": "150610342700684832",
"GasLimit": 15145026,
"GasFeeCap": "101967",
"GasPremium": "100913",
"Method": 6,
"Params": "igMaAAVUk9gqWCkAAYLiA4HoAiCHKF0gQg0SAAfbmkUG8iNEAyelBUyRNNBOfCQJBZZXSxoAAslIgRoADudPGgAK4gz0AAAA",
"CID": {
"/": "bafy2bzacec3x5rlwdveh46m55dbe47g7bmzlhncpczwwgcuoowvazumpf25se"
}
}

"Method:": 6 => 6表示此消息方法为PreCommitSector

CID => 消息ID。

"Params": "igMaAAVUk9gqWCkAAYLiA4HoAiCHKF0gQg0SAAfbmkUG8iNEAyelBUyRNNBOfCQJBZZXSxoAAslIgRoADudPGgAK4gz0AAAA"

以上Params为传输中被编码为base64的SectorPreCommitInfo结构信息。

解码

解码base64:

// base64 encoded params
params := "igMaAAVUk9gqWCkAAYLiA4HoAiCHKF0gQg0SAAfbmkUG8iNEAyelBUyRNNBOfCQJBZZXSxoAAslIgRoADudPGgAK4gz0AAAA"

var precommitInfo miner.SectorPreCommitInfo
bin, err := base64.StdEncoding.DecodeString(params)
if err != nil {
    log.Error(err)
    return
}

解码CBOR格式数据:

解码CBOR格式数据的代码可以参考源码github.com/lotus/cmd/lotus-pcr/ -> main.go。

m := &types.Message{
    Params: bin,
}

if err := precommitInfo.UnmarshalCBOR(bytes.NewReader(m.Params)); err != nil {
    log.Error(err)
    return
}

解析后的precommitInfo数据:

{
    SealProof:3 
    SectorNumber:349331 
    SealedCID:bagboea4b5abcbbziluqeedisaad5xgsfa3zcgrade6sqktergtie47bebeczmv2l 
    SealRandEpoch:182600 
    DealIDs:[976719] 
    Expiration:713228 
    ReplaceCapacity:false 
    ReplaceSectorDeadline:0 
    ReplaceSectorPartition:0 
    ReplaceSectorNumber:0
}

完整的解析例子代码

package filmessage

import (
    "bytes"
    "encoding/base64"
    "testing"

    "github.com/filecoin-project/lotus/chain/types"
    "github.com/filecoin-project/specs-actors/actors/builtin/miner"
    log "github.com/sirupsen/logrus"
)

func TestChainMessageDecoding(t *testing.T) {
    // Message struct
    // lotus -> chain -> types -> Message
    // type Message struct {
    //  Version uint64

    //  To   address.Address
    //  From address.Address

    //  Nonce uint64

    //  Value abi.TokenAmount

    //  GasLimit   int64
    //  GasFeeCap  abi.TokenAmount
    //  GasPremium abi.TokenAmount

    //  Method abi.MethodNum
    //  Params []byte
    // }

    // Get message by using lotus CLI
    //

gt; ~/hlm-miner/script/lotus/lotus-user# ./lotus.sh chain getmessage bafy2bzacec3x5rlwdveh46m55dbe47g7bmzlhncpczwwgcuoowvazumpf25se
// {
// "Version": 0,
// "To": "f03176",
// "From": "f3ruolcbftfwhudz2qexbkylbj2y2awtxeopovv5jeoafikmstk4j7bwowynmnes66xv6fselgolug55uuxp4q",
// "Nonce": 687292,
// "Value": "150610342700684832",
// "GasLimit": 15145026,
// "GasFeeCap": "101967",
// "GasPremium": "100913",
// "Method": 6,
// "Params": "igMaAAVUk9gqWCkAAYLiA4HoAiCHKF0gQg0SAAfbmkUG8iNEAyelBUyRNNBOfCQJBZZXSxoAAslIgRoADudPGgAK4gz0AAAA",
// "CID": {
// "/": "bafy2bzacec3x5rlwdveh46m55dbe47g7bmzlhncpczwwgcuoowvazumpf25se"
// }
// }

// base64 encoded params
params := "igMaAAVUk9gqWCkAAYLiA4HoAiCHKF0gQg0SAAfbmkUG8iNEAyelBUyRNNBOfCQJBZZXSxoAAslIgRoADudPGgAK4gz0AAAA"

var precommitInfo miner.SectorPreCommitInfo
bin, err := base64.StdEncoding.DecodeString(params)
if err != nil {
log.Error(err)
return
}

m := &types.Message{
Params: bin,
}

log.Println("bin => ", bin)

if err := precommitInfo.UnmarshalCBOR(bytes.NewReader(m.Params)); err != nil {
// log.Warnw("failed to decode precommit params", "err", err, "method", messageMethod, "cid", msg.Cid, "miner", m.To)
// return false, messageMethod, types.NewInt(0), nil
log.Error(err)
return
}

log.Printf("precommit info => %+v", precommitInfo)

//Output:
// INFO[0000] bin => [138 3 26 0 5 84 147 216 42 88 41 0 1 130 226 3 129 232 2 32 135 40 93 32 66 13 18 0 7 219 154 69 6 242 35 68 3 39 165 5 76 145 52 208 78 124 36 9 5 150 87 75 26 0 2 201 72 129 26 0 14 231 79 26 0 10 226 12 244 0 0 0]
// INFO[0000] precommit info => {SealProof:3 SectorNumber:349331 SealedCID:bagboea4b5abcbbziluqeedisaad5xgsfa3zcgrade6sqktergtie47bebeczmv2l SealRandEpoch:182600 DealIDs:[976719] Expiration:713228 ReplaceCapacity:false ReplaceSectorDeadline:0 ReplaceSectorPartition:0 ReplaceSectorNumber:0}
// PASS
// ok grandhelmsman.com/test/chain 0.003s
}

参考

github.com/whyrusleepin