zoukankan      html  css  js  c++  java
  • Fabric区块的数据结构总图

    本图目的:
    分析时不至于迷失方向

    Block

    Block是区块链数据结构里面的一个最基本的元素。

    // This is finalized block structure to be shared among the orderer and peer
    // Note that the BlockHeader chains to the previous BlockHeader, and the BlockData hash is embedded
    // in the BlockHeader.  This makes it natural and obvious that the Data is included in the hash, but
    // the Metadata is not.
    type Block struct {
       Header               *BlockHeader   `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
       Data                 *BlockData     `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
       Metadata             *BlockMetadata `protobuf:"bytes,3,opt,name=metadata" json:"metadata,omitempty"`
       XXX_NoUnkeyedLiteral struct{}       `json:"-"`
       XXX_unrecognized     []byte         `json:"-"`
       XXX_sizecache        int32          `json:"-"`
    }
    

    从这个结构体的定义可以看出,一个Block里面包含了三种类型的数据,

    Header,即区块头,这里又包含了三项数据,包括:
        Number:Block Number,即区块号,用来标识每个区块。
        PreviousHash:前一个区块头的hash值
        DataHash:Data域的hash值,这里没有包含Metadata
    Data,即区块数据,这里包含的就是所有的Transaction数据
    Metadata,元数据,这里记录的是一些辅助信息,包括:
        Metadata[BlockMetadataIndex_SIGNATURES]:签名信息
        Metadata[BlockMetadataIndex_LAST_CONFIG]:Channel的最新配置区块索引
        Metadata[BlockMetadataIndex_TRANSACTIONS_FILTER]:交易是否合法的标记
        Metadata[BlockMetadataIndex_ORDERER]:Channel的排序服务信息
    

    这里是BlockHeader结构体的定义,这里面的三项数据前面已经有说明了:

    // This is finalized block structure to be shared among the orderer and peer
    // Note that the BlockHeader chains to the previous BlockHeader, and the BlockData hash is embedded
    // in the BlockHeader.  This makes it natural and obvious that the Data is included in the hash, but
    // the Metadata is not.
    type Block struct {
    	Header               *BlockHeader   `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
    	Data                 *BlockData     `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
    	Metadata             *BlockMetadata `protobuf:"bytes,3,opt,name=metadata" json:"metadata,omitempty"`
    	XXX_NoUnkeyedLiteral struct{}       `json:"-"`
    	XXX_unrecognized     []byte         `json:"-"`
    	XXX_sizecache        int32          `json:"-"`
    }
    

    这里是BlockMetadata的定义:

    type BlockMetadata struct {
    	Metadata             [][]byte `protobuf:"bytes,1,rep,name=metadata,proto3" json:"metadata,omitempty"`
    	XXX_NoUnkeyedLiteral struct{} `json:"-"`
    	XXX_unrecognized     []byte   `json:"-"`
    	XXX_sizecache        int32    `json:"-"`
    }
    

    我们可以看到,BlockMetadata结构体中的Metadata域是一个byte类型的二维数组,而这个数据是由下面的Metadata结构体序列化而来的。

    // Metadata is a common structure to be used to encode block metadata
    type Metadata struct {
    Value []byte protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"
    Signatures []*MetadataSignature protobuf:"bytes,2,rep,name=signatures" json:"signatures,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    从这个定义中可以知道,元数据就是由一个value和相应的签名组成。

    总结下:Block包含BlockHeader,BlockData和BlockMetadata。

    BlockHeader

    这里不再详细分析,上面已经有了。
    BlockData

    下面我们来看下核心的BlockData。

    这个结构体只有一个数据Data,又是一个byte类型的二维数组。和前面的Metadata一样,这个也是由另外一个数据结构序列化而来的。

    type BlockData struct {
    Data [][]byte protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    // Envelope wraps a Payload with a signature so that the message may be authenticated
    type Envelope struct {
    // A marshaled Payload
    Payload []byte protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"
    // A signature by the creator specified in the Payload header
    Signature []byte protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    在Envelope这个结构体中,Signature就是对Payload数据的签名,这个数据在网络中传输的时候,这个签名用于对数据做有效性验证。Payload本身又是由另外一个数据结构序列化而来的。
    Payload

    // Payload is the message contents (and header to allow for signing)
    type Payload struct {
    // Header is included to provide identity and prevent replay
    Header *Header protobuf:"bytes,1,opt,name=header" json:"header,omitempty"
    // Data, the encoding of which is defined by the type in the header
    Data []byte protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    Payload,中文直译是负载的意思,也就是说具体承载交易数据。
    header

    这里包含一个header,这个header带有类型,负责描述这个payload的性质以及如何解析data字段。 另外header中还包含有创建者的信息和一个随机数,以及用来标识逻辑时间窗口的时期信息,这个时间窗口数据用于抵御重放攻击。
    ChannelHeader

    同样,Header中的ChannelHeader字段是由一个ChannelHeader的结构序列化而来的。

    type Header struct {
    ChannelHeader []byte protobuf:"bytes,1,opt,name=channel_header,json=channelHeader,proto3" json:"channel_header,omitempty"
    SignatureHeader []byte protobuf:"bytes,2,opt,name=signature_header,json=signatureHeader,proto3" json:"signature_header,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    // Header is a generic replay prevention and identity message to include in a signed payload
    type ChannelHeader struct {
    Type int32 protobuf:"varint,1,opt,name=type" json:"type,omitempty"
    // Version indicates message protocol version
    Version int32 protobuf:"varint,2,opt,name=version" json:"version,omitempty"
    // Timestamp is the local time when the message was created
    // by the sender
    Timestamp *timestamp.Timestamp protobuf:"bytes,3,opt,name=timestamp" json:"timestamp,omitempty"
    // Identifier of the channel this message is bound for
    ChannelId string protobuf:"bytes,4,opt,name=channel_id,json=channelId" json:"channel_id,omitempty"
    // An unique identifier that is used end-to-end.
    // - set by higher layers such as end user or SDK
    // - passed to the endorser (which will check for uniqueness)
    // - as the header is passed along unchanged, it will be
    // be retrieved by the committer (uniqueness check here as well)
    // - to be stored in the ledger
    TxId string protobuf:"bytes,5,opt,name=tx_id,json=txId" json:"tx_id,omitempty"
    // The epoch in which this header was generated, where epoch is defined based on block height
    // Epoch in which the response has been generated. This field identifies a
    // logical window of time. A proposal response is accepted by a peer only if
    // two conditions hold:
    // 1. the epoch specified in the message is the current epoch
    // 2. this message has been only seen once during this epoch (i.e. it hasn't
    // been replayed)
    Epoch uint64 protobuf:"varint,6,opt,name=epoch" json:"epoch,omitempty"
    // Extension that may be attached based on the header type
    Extension []byte protobuf:"bytes,7,opt,name=extension,proto3" json:"extension,omitempty"
    // If mutual TLS is employed, this represents
    // the hash of the client's TLS certificate
    TlsCertHash []byte protobuf:"bytes,8,opt,name=tls_cert_hash,json=tlsCertHash,proto3" json:"tls_cert_hash,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    Type:ChannelHeader中的Type,定义了这个Payload包含的是那种类型的消息,其中,1~10000的数值定义由系统保留。目前有如下的类型定义:
        MESSAGE = 0; // 不透明的消息类型
        CONFIG = 1; // Channel配置消息
        CONFIG_UPDATE = 2; // Channel配置更新的交易
        ENDORSER_TRANSACTION = 3; // 客户端通过SDK向Endorser节点提交交易提案
        ORDERER_TRANSACTION = 4; // 排序节点内部使用
        DELIVER_SEEK_INFO = 5; // 用于指示Deliver API查找信息
        CHAINCODE_PACKAGE = 6; // 用户安装链码的时候打包链码组件
        PEER_RESOURCE_UPDATE = 7; // 用于peer资源更新的时候,对更新信息做编码
    Version:指定消息协议的版本号
    Timestamp:定义发件人发出消息时候的本地时间,这里使用了Google Protobuf框架里面定义的时间格式
    ChannelId:指定了这个消息是绑定在哪一个Channel上的。我们知道Fabric中定义了Multi Channel的概念,绑定到特定的Channel上面,意味着这个消息只能由这个特定的Channel处理,其他的Channel是不能查看这个消息的
    TxId:一个唯一的ID,通常由更高层设定,比如用户或者SDK。这个数据传递给背书节点的时候,背书节点会检查其唯一性。当消息被正确传递的时候,记账节点也会去检索这条消息,同时也会去检查其唯一性。最终,这个数据会被保存在账本中
    Epoch:这个数据基于区块的高度(区块号)来定义,指定了这条消息的时间窗口,只有在满足以下两个条件的情况下,目的节点才会接受这条消息:
        消息中指定的时间信息是当前时期
        在这一个时间段内,这条消息只出现了一次(预防重放攻击)
    Extension:根据header type,不同的特定消息会附加自己特定的扩展数据
    TlsCertHash:如果使用了共同TLS,则这个数据指示了客户端的TLS证书
    

    SignatureHeader

    type SignatureHeader struct {
    // Creator of the message, a marshaled msp.SerializedIdentity
    Creator []byte protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"
    // Arbitrary number that may only be used once. Can be used to detect replay attacks.
    Nonce []byte protobuf:"bytes,2,opt,name=nonce,proto3" json:"nonce,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    SignatureHeader由signingidentity序列化而成:

    type signingidentity struct {
    // we embed everything from a base identity
    identity

    // signer corresponds to the object that can produce signatures from this identity
    signer crypto.Signer
    }

    type identity struct {
    // id contains the identifier (MSPID and identity identifier) for this instance
    id *IdentityIdentifier

    // cert contains the x.509 certificate that signs the public key of this instance
    cert *x509.Certificate

    // this is the public key of this instance
    pk bccsp.Key

    // reference to the MSP that "owns" this identity
    msp *bccspmsp
    }

    // Signer is an interface for an opaque private key that can be used for
    // signing operations. For example, an RSA key kept in a hardware module.
    type Signer interface {
    // Public returns the public key corresponding to the opaque,
    // private key.
    Public() PublicKey

    // Sign signs digest with the private key, possibly using entropy from
    // rand. For an RSA key, the resulting signature should be either a
    // PKCS#1 v1.5 or PSS signature (as indicated by opts). For an (EC)DSA
    // key, it should be a DER-serialised, ASN.1 signature structure.
    //
    // Hash implements the SignerOpts interface and, in most cases, one can
    // simply pass in the hash function used as opts. Sign may also attempt
    // to type assert opts to other types in order to obtain algorithm
    // specific values. See the documentation in each package for details.
    //
    // Note that when a signature of a hash of a larger message is needed,
    // the caller is responsible for hashing the larger message and passing
    // the hash (as digest) and the hash function (as opts) to Sign.
    Sign(rand io.Reader, digest []byte, opts SignerOpts) (signature []byte, err error)
    }

    Transaction

    在Payload结构体中的Data,是由Transaction结构体序列化而来的,

    // The transaction to be sent to the ordering service. A transaction contains
    // one or more TransactionAction. Each TransactionAction binds a proposal to
    // potentially multiple actions. The transaction is atomic meaning that either
    // all actions in the transaction will be committed or none will. Note that
    // while a Transaction might include more than one Header, the Header.creator
    // field must be the same in each.
    // A single client is free to issue a number of independent Proposal, each with
    // their header (Header) and request payload (ChaincodeProposalPayload). Each
    // proposal is independently endorsed generating an action
    // (ProposalResponsePayload) with one signature per Endorser. Any number of
    // independent proposals (and their action) might be included in a transaction
    // to ensure that they are treated atomically.
    type Transaction struct {
    // The payload is an array of TransactionAction. An array is necessary to
    // accommodate multiple actions per transaction
    Actions []*TransactionAction protobuf:"bytes,1,rep,name=actions" json:"actions,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    客户端发送到排序服务节点的数据就是由这个Transaction结构体定义的,一个Transaction结构又包含了一个或者多个TransactionAction数据(因为需要多个背书节点)。每一个TransactionAction数据就是一个交易提案,可能包含有多个Action。Transaction结构数据是原子性的,这就意味着,一条Transaction结构数据中包含的所有Action要么是全部被写入账本,要么是全部丢弃。这里需要注意的是:一条Transaction结构数据中可能会包含有多个Header,但是所有这些Header的Creator都必须是相同的,这也就意味着所有这些Action都是由同一个用户发起的。

    一个单独的客户端可以提交一系列相互独立的提案,每一个提案都可以包含它自己的header和Payload(ChaincodeProposalPayload)。背书节点会对每一个提案单独做背书并且产生独立的Action

    每一个独立的背书节点都会给Payload(ChaincodeProposalPayload)打上自己的签名,任意数量的提案以及这些提案包含的Action都可以打包到一条Transaction数据之中,当然这样的一条Transaction数据会被当做一条原子数据来处理。

    Transaction结构体本身是由TransactionAction的数组组成。
    TransactionAction

    // TransactionAction binds a proposal to its action. The type field in the
    // header dictates the type of action to be applied to the ledger.
    type TransactionAction struct {
    // The header of the proposal action, which is the proposal header
    Header []byte protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"
    // The payload of the action as defined by the type in the header For
    // chaincode, it's the bytes of ChaincodeActionPayload
    Payload []byte protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    这个结构体为每一个Action绑定了一个提案,在Header字段中的type指定了这个action的分类。

    这个结构体中的Payload具体类型则是由Header中的type定义的,它是ChaincodeActionPayload结构体的序列化数据。

    // ChaincodeActionPayload is the message to be used for the TransactionAction's
    // payload when the Header's type is set to CHAINCODE. It carries the
    // chaincodeProposalPayload and an endorsed action to apply to the ledger.
    type ChaincodeActionPayload struct {
    // This field contains the bytes of the ChaincodeProposalPayload message from
    // the original invocation (essentially the arguments) after the application
    // of the visibility function. The main visibility modes are "full" (the
    // entire ChaincodeProposalPayload message is included here), "hash" (only
    // the hash of the ChaincodeProposalPayload message is included) or
    // "nothing". This field will be used to check the consistency of
    // ProposalResponsePayload.proposalHash. For the CHAINCODE type,
    // ProposalResponsePayload.proposalHash is supposed to be H(ProposalHeader ||
    // f(ChaincodeProposalPayload)) where f is the visibility function.
    ChaincodeProposalPayload []byte protobuf:"bytes,1,opt,name=chaincode_proposal_payload,json=chaincodeProposalPayload,proto3" json:"chaincode_proposal_payload,omitempty"
    // The list of actions to apply to the ledger
    Action *ChaincodeEndorsedAction protobuf:"bytes,2,opt,name=action" json:"action,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    ChaincodeProposalPayload

    // ChaincodeProposalPayload is the Proposal's payload message to be used when
    // the Header's type is CHAINCODE. It contains the arguments for this
    // invocation.
    type ChaincodeProposalPayload struct {
    // Input contains the arguments for this invocation. If this invocation
    // deploys a new chaincode, ESCC/VSCC are part of this field.
    // This is usually a marshaled ChaincodeInvocationSpec
    Input []byte protobuf:"bytes,1,opt,name=input,proto3" json:"input,omitempty"
    // TransientMap contains data (e.g. cryptographic material) that might be used
    // to implement some form of application-level confidentiality. The contents
    // of this field are supposed to always be omitted from the transaction and
    // excluded from the ledger.
    TransientMap map[string][]byte protobuf:"bytes,2,rep,name=TransientMap" json:"TransientMap,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value,proto3"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    // Carries the chaincode function and its arguments.
    type ChaincodeInvocationSpec struct {
    ChaincodeSpec *ChaincodeSpec protobuf:"bytes,1,opt,name=chaincode_spec,json=chaincodeSpec" json:"chaincode_spec,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    ChaincodeSpec是实际的chaincode的metadata。Type包含java,node等。ChaincodeId在下面有说,ChaincodeInput下面也有说,Timeout是超时时间。

    // Carries the chaincode specification. This is the actual metadata required for
    // defining a chaincode.
    type ChaincodeSpec struct {
    Type ChaincodeSpec_Type protobuf:"varint,1,opt,name=type,enum=protos.ChaincodeSpec_Type" json:"type,omitempty"
    ChaincodeId *ChaincodeID protobuf:"bytes,2,opt,name=chaincode_id,json=chaincodeId" json:"chaincode_id,omitempty"
    Input *ChaincodeInput protobuf:"bytes,3,opt,name=input" json:"input,omitempty"
    Timeout int32 protobuf:"varint,4,opt,name=timeout" json:"timeout,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    ChaincodeID包含Path,Name,Version。

    // ChaincodeID contains the path as specified by the deploy transaction
    // that created it as well as the hashCode that is generated by the
    // system for the path. From the user level (ie, CLI, REST API and so on)
    // deploy transaction is expected to provide the path and other requests
    // are expected to provide the hashCode. The other value will be ignored.
    // Internally, the structure could contain both values. For instance, the
    // hashCode will be set when first generated using the path
    type ChaincodeID struct {
    // deploy transaction will use the path
    Path string protobuf:"bytes,1,opt,name=path" json:"path,omitempty"
    // all other requests will use the name (really a hashcode) generated by
    // the deploy transaction
    Name string protobuf:"bytes,2,opt,name=name" json:"name,omitempty"
    // user friendly version name for the chaincode
    Version string protobuf:"bytes,3,opt,name=version" json:"version,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    下面来看ChaincodeInput,包含function和arguments,

    // Carries the chaincode function and its arguments.
    // UnmarshalJSON in transaction.go converts the string-based REST/JSON input to
    // the []byte-based current ChaincodeInput structure.
    type ChaincodeInput struct {
    Args [][]byte protobuf:"bytes,1,rep,name=args,proto3" json:"args,omitempty"
    Decorations map[string][]byte protobuf:"bytes,2,rep,name=decorations" json:"decorations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value,proto3"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    ChaincodeEndorsedAction

    // ChaincodeEndorsedAction carries information about the endorsement of a
    // specific proposal
    type ChaincodeEndorsedAction struct {
    // This is the bytes of the ProposalResponsePayload message signed by the
    // endorsers. Recall that for the CHAINCODE type, the
    // ProposalResponsePayload's extenstion field carries a ChaincodeAction
    ProposalResponsePayload []byte protobuf:"bytes,1,opt,name=proposal_response_payload,json=proposalResponsePayload,proto3" json:"proposal_response_payload,omitempty"
    // The endorsement of the proposal, basically the endorser's signature over
    // proposalResponsePayload
    Endorsements []*Endorsement protobuf:"bytes,2,rep,name=endorsements" json:"endorsements,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    // ProposalResponsePayload is the payload of a proposal response. This message
    // is the "bridge" between the client's request and the endorser's action in
    // response to that request. Concretely, for chaincodes, it contains a hashed
    // representation of the proposal (proposalHash) and a representation of the
    // chaincode state changes and events inside the extension field.
    type ProposalResponsePayload struct {
    // Hash of the proposal that triggered this response. The hash is used to
    // link a response with its proposal, both for bookeeping purposes on an
    // asynchronous system and for security reasons (accountability,
    // non-repudiation). The hash usually covers the entire Proposal message
    // (byte-by-byte). However this implies that the hash can only be verified
    // if the entire proposal message is available when ProposalResponsePayload is
    // included in a transaction or stored in the ledger. For confidentiality
    // reasons, with chaincodes it might be undesirable to store the proposal
    // payload in the ledger. If the type is CHAINCODE, this is handled by
    // separating the proposal's header and
    // the payload: the header is always hashed in its entirety whereas the
    // payload can either be hashed fully, or only its hash may be hashed, or
    // nothing from the payload can be hashed. The PayloadVisibility field in the
    // Header's extension controls to which extent the proposal payload is
    // "visible" in the sense that was just explained.
    ProposalHash []byte protobuf:"bytes,1,opt,name=proposal_hash,json=proposalHash,proto3" json:"proposal_hash,omitempty"
    // Extension should be unmarshaled to a type-specific message. The type of
    // the extension in any proposal response depends on the type of the proposal
    // that the client selected when the proposal was initially sent out. In
    // particular, this information is stored in the type field of a Header. For
    // chaincode, it's a ChaincodeAction message
    Extension []byte protobuf:"bytes,2,opt,name=extension,proto3" json:"extension,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    Endorsement包含背书者和签名值。

    // An endorsement is a signature of an endorser over a proposal response. By
    // producing an endorsement message, an endorser implicitly "approves" that
    // proposal response and the actions contained therein. When enough
    // endorsements have been collected, a transaction can be generated out of a
    // set of proposal responses. Note that this message only contains an identity
    // and a signature but no signed payload. This is intentional because
    // endorsements are supposed to be collected in a transaction, and they are all
    // expected to endorse a single proposal response/action (many endorsements
    // over a single proposal response)
    type Endorsement struct {
    // Identity of the endorser (e.g. its certificate)
    Endorser []byte protobuf:"bytes,1,opt,name=endorser,proto3" json:"endorser,omitempty"
    // Signature of the payload included in ProposalResponse concatenated with
    // the endorser's certificate; ie, sign(ProposalResponse.payload + endorser)
    Signature []byte protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"
    XXX_NoUnkeyedLiteral struct{} json:"-"
    XXX_unrecognized []byte json:"-"
    XXX_sizecache int32 json:"-"
    }

    BlockMetadata

    上面已经分析。

    原文

    聊聊fabric的数据结构

  • 相关阅读:
    国内10大前端团队网站
    可视化搭建前端工程
    Vue CLI环境变量和模式
    BetterScroll:可能是目前最好用的移动端滚动插件
    洛谷月赛
    CF438D The Child and Sequence
    P1447 [NOI2010]能量采集
    Cow Relays,过N条边的最短路
    Numerical Sequence(hard version),两次二分
    洛谷P3237 米特运输
  • 原文地址:https://www.cnblogs.com/jiftle/p/14600306.html
Copyright © 2011-2022 走看看