diff --git a/api/protoblocktx/block_tx.pb.go b/api/protoblocktx/block_tx.pb.go index 6df8fc8a..5310869a 100644 --- a/api/protoblocktx/block_tx.pb.go +++ b/api/protoblocktx/block_tx.pb.go @@ -25,6 +25,57 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type PolicyType int32 + +const ( + // A policy for verifying a single signature that was generated via a Threshold Signature + // Scheme (TSS). In a TSS, a threshold (T) of N parties must cooperate to + // collectively compute and produce the single signature. + PolicyType_THRESHOLD_RULE PolicyType = 0 + // A policy defined by an explicit rule that evaluates one or more required signatures. + // For example: "OR('Org1MSP.admin', 'Org2MSP.admin')" + PolicyType_SIGNATURE_RULE PolicyType = 1 +) + +// Enum value maps for PolicyType. +var ( + PolicyType_name = map[int32]string{ + 0: "THRESHOLD_RULE", + 1: "SIGNATURE_RULE", + } + PolicyType_value = map[string]int32{ + "THRESHOLD_RULE": 0, + "SIGNATURE_RULE": 1, + } +) + +func (x PolicyType) Enum() *PolicyType { + p := new(PolicyType) + *p = x + return p +} + +func (x PolicyType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (PolicyType) Descriptor() protoreflect.EnumDescriptor { + return file_api_protoblocktx_block_tx_proto_enumTypes[0].Descriptor() +} + +func (PolicyType) Type() protoreflect.EnumType { + return &file_api_protoblocktx_block_tx_proto_enumTypes[0] +} + +func (x PolicyType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use PolicyType.Descriptor instead. +func (PolicyType) EnumDescriptor() ([]byte, []int) { + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{0} +} + // Status represents the result of transaction validation. // Except for NOT_VALIDATED, all statuses are recorded in the ledger. // Some statuses are also stored in the state database which prevent resubmission of the same transaction ID. @@ -117,11 +168,11 @@ func (x Status) String() string { } func (Status) Descriptor() protoreflect.EnumDescriptor { - return file_api_protoblocktx_block_tx_proto_enumTypes[0].Descriptor() + return file_api_protoblocktx_block_tx_proto_enumTypes[1].Descriptor() } func (Status) Type() protoreflect.EnumType { - return &file_api_protoblocktx_block_tx_proto_enumTypes[0] + return &file_api_protoblocktx_block_tx_proto_enumTypes[1] } func (x Status) Number() protoreflect.EnumNumber { @@ -130,17 +181,20 @@ func (x Status) Number() protoreflect.EnumNumber { // Deprecated: Use Status.Descriptor instead. func (Status) EnumDescriptor() ([]byte, []int) { - return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{0} + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{1} } -// Represents a transaction in the blockchain. type Tx struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Namespaces []*TxNamespace `protobuf:"bytes,1,rep,name=namespaces,proto3" json:"namespaces,omitempty"` // Namespaces associated with the transaction. - Signatures [][]byte `protobuf:"bytes,2,rep,name=signatures,proto3" json:"signatures,omitempty"` // Signature per namespace. + // A list of namespaces that define the transaction's scope. + Namespaces []*TxNamespace `protobuf:"bytes,1,rep,name=namespaces,proto3" json:"namespaces,omitempty"` + // A list of endorsements. + // IMPORTANT: This list MUST be the same size as the namespaces list. + // The Endorsement at index i corresponds to the namespace at index i. + Endorsements []*Endorsements `protobuf:"bytes,2,rep,name=endorsements,proto3" json:"endorsements,omitempty"` } func (x *Tx) Reset() { @@ -182,9 +236,9 @@ func (x *Tx) GetNamespaces() []*TxNamespace { return nil } -func (x *Tx) GetSignatures() [][]byte { +func (x *Tx) GetEndorsements() []*Endorsements { if x != nil { - return x.Signatures + return x.Endorsements } return nil } @@ -445,20 +499,220 @@ func (x *Write) GetValue() []byte { return nil } +// Endorsements holds all the signatures that correspond to a single namespace +// in the transaction's namespaces list. +type Endorsements struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The list of individual signatures for the corresponding namespace. + EndorsementsWithIdentity []*EndorsementWithIdentity `protobuf:"bytes,1,rep,name=endorsements_with_identity,json=endorsementsWithIdentity,proto3" json:"endorsements_with_identity,omitempty"` +} + +func (x *Endorsements) Reset() { + *x = Endorsements{} + if protoimpl.UnsafeEnabled { + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Endorsements) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Endorsements) ProtoMessage() {} + +func (x *Endorsements) ProtoReflect() protoreflect.Message { + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Endorsements.ProtoReflect.Descriptor instead. +func (*Endorsements) Descriptor() ([]byte, []int) { + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{5} +} + +func (x *Endorsements) GetEndorsementsWithIdentity() []*EndorsementWithIdentity { + if x != nil { + return x.EndorsementsWithIdentity + } + return nil +} + +// EndorsementWithIdentity bundles a single signature with the identity of its creator. +type EndorsementWithIdentity struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The actual cryptographic signature bytes. + Endorsement []byte `protobuf:"bytes,1,opt,name=endorsement,proto3" json:"endorsement,omitempty"` + // The identity of the creator who produced the signature, i.e., the endorsement. + Identity *Identity `protobuf:"bytes,2,opt,name=identity,proto3" json:"identity,omitempty"` +} + +func (x *EndorsementWithIdentity) Reset() { + *x = EndorsementWithIdentity{} + if protoimpl.UnsafeEnabled { + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EndorsementWithIdentity) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EndorsementWithIdentity) ProtoMessage() {} + +func (x *EndorsementWithIdentity) ProtoReflect() protoreflect.Message { + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EndorsementWithIdentity.ProtoReflect.Descriptor instead. +func (*EndorsementWithIdentity) Descriptor() ([]byte, []int) { + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{6} +} + +func (x *EndorsementWithIdentity) GetEndorsement() []byte { + if x != nil { + return x.Endorsement + } + return nil +} + +func (x *EndorsementWithIdentity) GetIdentity() *Identity { + if x != nil { + return x.Identity + } + return nil +} + +type Identity struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The identifier of the associated membership service provider + MspId string `protobuf:"bytes,1,opt,name=msp_id,json=mspId,proto3" json:"msp_id,omitempty"` + // Types that are assignable to Creator: + // + // *Identity_Certificate + // *Identity_CertificateId + Creator isIdentity_Creator `protobuf_oneof:"creator"` +} + +func (x *Identity) Reset() { + *x = Identity{} + if protoimpl.UnsafeEnabled { + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Identity) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Identity) ProtoMessage() {} + +func (x *Identity) ProtoReflect() protoreflect.Message { + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Identity.ProtoReflect.Descriptor instead. +func (*Identity) Descriptor() ([]byte, []int) { + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{7} +} + +func (x *Identity) GetMspId() string { + if x != nil { + return x.MspId + } + return "" +} + +func (m *Identity) GetCreator() isIdentity_Creator { + if m != nil { + return m.Creator + } + return nil +} + +func (x *Identity) GetCertificate() []byte { + if x, ok := x.GetCreator().(*Identity_Certificate); ok { + return x.Certificate + } + return nil +} + +func (x *Identity) GetCertificateId() string { + if x, ok := x.GetCreator().(*Identity_CertificateId); ok { + return x.CertificateId + } + return "" +} + +type isIdentity_Creator interface { + isIdentity_Creator() +} + +type Identity_Certificate struct { + // The full raw bytes of the creator's certificate (e.g., an X.509 certificate). + Certificate []byte `protobuf:"bytes,2,opt,name=certificate,proto3,oneof"` +} + +type Identity_CertificateId struct { + // An identifier for a certificate that is pre-stored or known by the committer. + CertificateId string `protobuf:"bytes,3,opt,name=certificate_id,json=certificateId,proto3,oneof"` +} + +func (*Identity_Certificate) isIdentity_Creator() {} + +func (*Identity_CertificateId) isIdentity_Creator() {} + // Represents a namespace policy. type NamespacePolicy struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Scheme string `protobuf:"bytes,1,opt,name=scheme,proto3" json:"scheme,omitempty"` // The scheme for signature verification. - PublicKey []byte `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // The public key for signature verification. + Type PolicyType `protobuf:"varint,1,opt,name=type,proto3,enum=protoblocktx.PolicyType" json:"type,omitempty"` // The type of policy used. + Policy []byte `protobuf:"bytes,2,opt,name=policy,proto3" json:"policy,omitempty"` // The policy rule. } func (x *NamespacePolicy) Reset() { *x = NamespacePolicy{} if protoimpl.UnsafeEnabled { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[5] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -471,7 +725,7 @@ func (x *NamespacePolicy) String() string { func (*NamespacePolicy) ProtoMessage() {} func (x *NamespacePolicy) ProtoReflect() protoreflect.Message { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[5] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -484,17 +738,72 @@ func (x *NamespacePolicy) ProtoReflect() protoreflect.Message { // Deprecated: Use NamespacePolicy.ProtoReflect.Descriptor instead. func (*NamespacePolicy) Descriptor() ([]byte, []int) { - return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{5} + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{8} } -func (x *NamespacePolicy) GetScheme() string { +func (x *NamespacePolicy) GetType() PolicyType { + if x != nil { + return x.Type + } + return PolicyType_THRESHOLD_RULE +} + +func (x *NamespacePolicy) GetPolicy() []byte { + if x != nil { + return x.Policy + } + return nil +} + +type ThresholdRule struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Scheme string `protobuf:"bytes,1,opt,name=scheme,proto3" json:"scheme,omitempty"` // The scheme for signature verification. + PublicKey []byte `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // The public key for signature verification. +} + +func (x *ThresholdRule) Reset() { + *x = ThresholdRule{} + if protoimpl.UnsafeEnabled { + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ThresholdRule) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ThresholdRule) ProtoMessage() {} + +func (x *ThresholdRule) ProtoReflect() protoreflect.Message { + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ThresholdRule.ProtoReflect.Descriptor instead. +func (*ThresholdRule) Descriptor() ([]byte, []int) { + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{9} +} + +func (x *ThresholdRule) GetScheme() string { if x != nil { return x.Scheme } return "" } -func (x *NamespacePolicy) GetPublicKey() []byte { +func (x *ThresholdRule) GetPublicKey() []byte { if x != nil { return x.PublicKey } @@ -512,7 +821,7 @@ type BlockInfo struct { func (x *BlockInfo) Reset() { *x = BlockInfo{} if protoimpl.UnsafeEnabled { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[6] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -525,7 +834,7 @@ func (x *BlockInfo) String() string { func (*BlockInfo) ProtoMessage() {} func (x *BlockInfo) ProtoReflect() protoreflect.Message { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[6] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -538,7 +847,7 @@ func (x *BlockInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use BlockInfo.ProtoReflect.Descriptor instead. func (*BlockInfo) Descriptor() ([]byte, []int) { - return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{6} + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{10} } func (x *BlockInfo) GetNumber() uint64 { @@ -559,7 +868,7 @@ type LastCommittedBlock struct { func (x *LastCommittedBlock) Reset() { *x = LastCommittedBlock{} if protoimpl.UnsafeEnabled { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[7] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -572,7 +881,7 @@ func (x *LastCommittedBlock) String() string { func (*LastCommittedBlock) ProtoMessage() {} func (x *LastCommittedBlock) ProtoReflect() protoreflect.Message { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[7] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -585,7 +894,7 @@ func (x *LastCommittedBlock) ProtoReflect() protoreflect.Message { // Deprecated: Use LastCommittedBlock.ProtoReflect.Descriptor instead. func (*LastCommittedBlock) Descriptor() ([]byte, []int) { - return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{7} + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{11} } func (x *LastCommittedBlock) GetBlock() *BlockInfo { @@ -606,7 +915,7 @@ type QueryStatus struct { func (x *QueryStatus) Reset() { *x = QueryStatus{} if protoimpl.UnsafeEnabled { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[8] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -619,7 +928,7 @@ func (x *QueryStatus) String() string { func (*QueryStatus) ProtoMessage() {} func (x *QueryStatus) ProtoReflect() protoreflect.Message { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[8] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -632,7 +941,7 @@ func (x *QueryStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryStatus.ProtoReflect.Descriptor instead. func (*QueryStatus) Descriptor() ([]byte, []int) { - return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{8} + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{12} } func (x *QueryStatus) GetTxIDs() []string { @@ -653,7 +962,7 @@ type TransactionsStatus struct { func (x *TransactionsStatus) Reset() { *x = TransactionsStatus{} if protoimpl.UnsafeEnabled { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[9] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -666,7 +975,7 @@ func (x *TransactionsStatus) String() string { func (*TransactionsStatus) ProtoMessage() {} func (x *TransactionsStatus) ProtoReflect() protoreflect.Message { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[9] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -679,7 +988,7 @@ func (x *TransactionsStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use TransactionsStatus.ProtoReflect.Descriptor instead. func (*TransactionsStatus) Descriptor() ([]byte, []int) { - return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{9} + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{13} } func (x *TransactionsStatus) GetStatus() map[string]*StatusWithHeight { @@ -702,7 +1011,7 @@ type StatusWithHeight struct { func (x *StatusWithHeight) Reset() { *x = StatusWithHeight{} if protoimpl.UnsafeEnabled { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[10] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -715,7 +1024,7 @@ func (x *StatusWithHeight) String() string { func (*StatusWithHeight) ProtoMessage() {} func (x *StatusWithHeight) ProtoReflect() protoreflect.Message { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[10] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -728,7 +1037,7 @@ func (x *StatusWithHeight) ProtoReflect() protoreflect.Message { // Deprecated: Use StatusWithHeight.ProtoReflect.Descriptor instead. func (*StatusWithHeight) Descriptor() ([]byte, []int) { - return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{10} + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{14} } func (x *StatusWithHeight) GetCode() Status { @@ -763,7 +1072,7 @@ type NamespacePolicies struct { func (x *NamespacePolicies) Reset() { *x = NamespacePolicies{} if protoimpl.UnsafeEnabled { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[11] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -776,7 +1085,7 @@ func (x *NamespacePolicies) String() string { func (*NamespacePolicies) ProtoMessage() {} func (x *NamespacePolicies) ProtoReflect() protoreflect.Message { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[11] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -789,7 +1098,7 @@ func (x *NamespacePolicies) ProtoReflect() protoreflect.Message { // Deprecated: Use NamespacePolicies.ProtoReflect.Descriptor instead. func (*NamespacePolicies) Descriptor() ([]byte, []int) { - return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{11} + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{15} } func (x *NamespacePolicies) GetPolicies() []*PolicyItem { @@ -805,14 +1114,14 @@ type PolicyItem struct { unknownFields protoimpl.UnknownFields Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` - Policy []byte `protobuf:"bytes,2,opt,name=policy,proto3" json:"policy,omitempty"` + Policy []byte `protobuf:"bytes,2,opt,name=policy,proto3" json:"policy,omitempty"` // This holds the complete NamespacePolicy. Version uint64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` } func (x *PolicyItem) Reset() { *x = PolicyItem{} if protoimpl.UnsafeEnabled { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[12] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -825,7 +1134,7 @@ func (x *PolicyItem) String() string { func (*PolicyItem) ProtoMessage() {} func (x *PolicyItem) ProtoReflect() protoreflect.Message { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[12] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -838,7 +1147,7 @@ func (x *PolicyItem) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyItem.ProtoReflect.Descriptor instead. func (*PolicyItem) Descriptor() ([]byte, []int) { - return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{12} + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{16} } func (x *PolicyItem) GetNamespace() string { @@ -874,7 +1183,7 @@ type ConfigTransaction struct { func (x *ConfigTransaction) Reset() { *x = ConfigTransaction{} if protoimpl.UnsafeEnabled { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[13] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -887,7 +1196,7 @@ func (x *ConfigTransaction) String() string { func (*ConfigTransaction) ProtoMessage() {} func (x *ConfigTransaction) ProtoReflect() protoreflect.Message { - mi := &file_api_protoblocktx_block_tx_proto_msgTypes[13] + mi := &file_api_protoblocktx_block_tx_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -900,7 +1209,7 @@ func (x *ConfigTransaction) ProtoReflect() protoreflect.Message { // Deprecated: Use ConfigTransaction.ProtoReflect.Descriptor instead. func (*ConfigTransaction) Descriptor() ([]byte, []int) { - return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{13} + return file_api_protoblocktx_block_tx_proto_rawDescGZIP(), []int{17} } func (x *ConfigTransaction) GetEnvelope() []byte { @@ -923,12 +1232,14 @@ var file_api_protoblocktx_block_tx_proto_rawDesc = []byte{ 0x0a, 0x1f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x78, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x78, 0x22, - 0x5f, 0x0a, 0x02, 0x54, 0x78, 0x12, 0x39, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x7f, 0x0a, 0x02, 0x54, 0x78, 0x12, 0x39, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x78, 0x2e, 0x54, 0x78, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x12, 0x3e, 0x0a, 0x0c, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x74, 0x78, 0x2e, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x0c, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xe6, 0x01, 0x0a, 0x0b, 0x54, 0x78, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x73, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, @@ -957,100 +1268,131 @@ var file_api_protoblocktx_block_tx_proto_rawDesc = []byte{ 0x0a, 0x05, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0x48, 0x0a, 0x0f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x22, 0x23, 0x0a, 0x09, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x52, - 0x0a, 0x12, 0x4c, 0x61, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x32, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x74, 0x78, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x05, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x22, 0x23, 0x0a, 0x0b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x22, 0xb5, 0x01, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x44, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x78, 0x2e, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x1a, 0x59, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x74, 0x78, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x57, 0x69, 0x74, 0x68, 0x48, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0x7c, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x57, 0x69, 0x74, 0x68, 0x48, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x78, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, - 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x08, 0x74, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x49, 0x0a, - 0x11, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, - 0x65, 0x73, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x74, 0x78, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x08, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0x5c, 0x0a, 0x0a, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x18, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x49, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, - 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x65, - 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x2a, 0x83, 0x05, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x11, 0x0a, 0x0d, - 0x4e, 0x4f, 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1d, - 0x0a, 0x19, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, - 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x02, 0x12, 0x19, 0x0a, - 0x15, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x5f, 0x4d, 0x56, 0x43, 0x43, 0x5f, 0x43, 0x4f, - 0x4e, 0x46, 0x4c, 0x49, 0x43, 0x54, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x4a, 0x45, - 0x43, 0x54, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x54, - 0x58, 0x5f, 0x49, 0x44, 0x10, 0x64, 0x12, 0x1a, 0x0a, 0x16, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, - 0x4d, 0x45, 0x44, 0x5f, 0x42, 0x41, 0x44, 0x5f, 0x45, 0x4e, 0x56, 0x45, 0x4c, 0x4f, 0x50, 0x45, - 0x10, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, - 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x5f, 0x54, 0x58, 0x5f, 0x49, 0x44, 0x10, 0x66, 0x12, - 0x2a, 0x0a, 0x26, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x53, - 0x55, 0x50, 0x50, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x5f, 0x45, 0x4e, 0x56, 0x45, 0x4c, 0x4f, 0x50, - 0x45, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x67, 0x12, 0x22, 0x0a, 0x1e, 0x4d, - 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x42, 0x41, 0x44, 0x5f, 0x45, 0x4e, 0x56, - 0x45, 0x4c, 0x4f, 0x50, 0x45, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x68, 0x12, - 0x1c, 0x0a, 0x18, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x54, 0x58, 0x5f, - 0x49, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x4c, 0x49, 0x43, 0x54, 0x10, 0x69, 0x12, 0x1e, 0x0a, - 0x1a, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x45, 0x4d, 0x50, 0x54, 0x59, - 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x53, 0x10, 0x6a, 0x12, 0x21, 0x0a, - 0x1d, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x50, 0x4c, 0x49, - 0x43, 0x41, 0x54, 0x45, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x6b, - 0x12, 0x22, 0x0a, 0x1e, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x4e, 0x41, - 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x49, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, - 0x49, 0x44, 0x10, 0x6c, 0x12, 0x26, 0x0a, 0x22, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, - 0x44, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x53, 0x5f, 0x4e, - 0x4f, 0x54, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x10, 0x6d, 0x12, 0x17, 0x0a, 0x13, - 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x5f, 0x57, 0x52, 0x49, - 0x54, 0x45, 0x53, 0x10, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, - 0x45, 0x44, 0x5f, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x6f, 0x12, 0x2d, - 0x0a, 0x29, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x50, 0x4c, - 0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x49, 0x4e, 0x5f, 0x52, 0x45, 0x41, - 0x44, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x70, 0x12, 0x1f, 0x0a, - 0x1b, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, - 0x4e, 0x47, 0x5f, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, 0x10, 0x71, 0x12, 0x26, - 0x0a, 0x22, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, - 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x49, 0x4e, 0x56, - 0x41, 0x4c, 0x49, 0x44, 0x10, 0x72, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, - 0x4d, 0x45, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x54, 0x58, 0x5f, 0x49, 0x4e, - 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x73, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x79, 0x70, 0x65, 0x72, 0x6c, 0x65, 0x64, 0x67, 0x65, - 0x72, 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2d, 0x78, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x74, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x74, 0x78, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x0a, 0x0c, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, + 0x63, 0x0a, 0x1a, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, + 0x77, 0x69, 0x74, 0x68, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x74, 0x78, 0x2e, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x57, 0x69, + 0x74, 0x68, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x18, 0x65, 0x6e, 0x64, 0x6f, + 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x57, 0x69, 0x74, 0x68, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x22, 0x6f, 0x0a, 0x17, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x57, 0x69, 0x74, 0x68, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, + 0x20, 0x0a, 0x0b, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x32, 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x74, 0x78, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x08, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0x79, 0x0a, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x12, 0x15, 0x0a, 0x06, 0x6d, 0x73, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x6d, 0x73, 0x70, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, + 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x0e, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x49, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, + 0x22, 0x57, 0x0a, 0x0f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x12, 0x2c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x78, + 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x46, 0x0a, 0x0d, 0x54, 0x68, 0x72, + 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, + 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x79, 0x22, 0x23, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, + 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, + 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x52, 0x0a, 0x12, 0x4c, 0x61, 0x73, 0x74, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x32, 0x0a, 0x05, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x78, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x88, 0x01, 0x01, + 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x23, 0x0a, 0x0b, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x78, 0x49, + 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x22, + 0xb5, 0x01, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x44, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x74, 0x78, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x59, 0x0a, 0x0b, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x78, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x57, 0x69, 0x74, 0x68, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x57, 0x69, 0x74, 0x68, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x63, + 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x78, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x74, 0x78, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x49, 0x0a, 0x11, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x78, 0x2e, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, + 0x22, 0x5c, 0x0a, 0x0a, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1c, + 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x49, + 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2a, 0x34, 0x0a, 0x0a, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x48, 0x52, 0x45, 0x53, + 0x48, 0x4f, 0x4c, 0x44, 0x5f, 0x52, 0x55, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, + 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x55, 0x4c, 0x45, 0x10, 0x01, 0x2a, + 0x83, 0x05, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x4f, + 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, + 0x09, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, + 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, + 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x41, + 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x5f, 0x4d, 0x56, 0x43, 0x43, 0x5f, 0x43, 0x4f, 0x4e, 0x46, + 0x4c, 0x49, 0x43, 0x54, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, + 0x45, 0x44, 0x5f, 0x44, 0x55, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x58, 0x5f, + 0x49, 0x44, 0x10, 0x64, 0x12, 0x1a, 0x0a, 0x16, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, + 0x44, 0x5f, 0x42, 0x41, 0x44, 0x5f, 0x45, 0x4e, 0x56, 0x45, 0x4c, 0x4f, 0x50, 0x45, 0x10, 0x65, + 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x4d, 0x49, + 0x53, 0x53, 0x49, 0x4e, 0x47, 0x5f, 0x54, 0x58, 0x5f, 0x49, 0x44, 0x10, 0x66, 0x12, 0x2a, 0x0a, + 0x26, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x55, 0x50, + 0x50, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x5f, 0x45, 0x4e, 0x56, 0x45, 0x4c, 0x4f, 0x50, 0x45, 0x5f, + 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x67, 0x12, 0x22, 0x0a, 0x1e, 0x4d, 0x41, 0x4c, + 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x42, 0x41, 0x44, 0x5f, 0x45, 0x4e, 0x56, 0x45, 0x4c, + 0x4f, 0x50, 0x45, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x68, 0x12, 0x1c, 0x0a, + 0x18, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x54, 0x58, 0x5f, 0x49, 0x44, + 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x4c, 0x49, 0x43, 0x54, 0x10, 0x69, 0x12, 0x1e, 0x0a, 0x1a, 0x4d, + 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x5f, 0x4e, + 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x53, 0x10, 0x6a, 0x12, 0x21, 0x0a, 0x1d, 0x4d, + 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x50, 0x4c, 0x49, 0x43, 0x41, + 0x54, 0x45, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x6b, 0x12, 0x22, + 0x0a, 0x1e, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, + 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x49, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, + 0x10, 0x6c, 0x12, 0x26, 0x0a, 0x22, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, + 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x53, 0x5f, 0x4e, 0x4f, 0x54, + 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x10, 0x6d, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x41, + 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, + 0x53, 0x10, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, + 0x5f, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x6f, 0x12, 0x2d, 0x0a, 0x29, + 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x50, 0x4c, 0x49, 0x43, + 0x41, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x49, 0x4e, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, + 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x70, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, + 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, + 0x5f, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, 0x10, 0x71, 0x12, 0x26, 0x0a, 0x22, + 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, + 0x41, 0x43, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, + 0x49, 0x44, 0x10, 0x72, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, + 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x54, 0x58, 0x5f, 0x49, 0x4e, 0x56, 0x41, + 0x4c, 0x49, 0x44, 0x10, 0x73, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x79, 0x70, 0x65, 0x72, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x2f, + 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2d, 0x78, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, + 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x74, 0x78, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1065,41 +1407,50 @@ func file_api_protoblocktx_block_tx_proto_rawDescGZIP() []byte { return file_api_protoblocktx_block_tx_proto_rawDescData } -var file_api_protoblocktx_block_tx_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_api_protoblocktx_block_tx_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_api_protoblocktx_block_tx_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_api_protoblocktx_block_tx_proto_msgTypes = make([]protoimpl.MessageInfo, 19) var file_api_protoblocktx_block_tx_proto_goTypes = []interface{}{ - (Status)(0), // 0: protoblocktx.Status - (*Tx)(nil), // 1: protoblocktx.Tx - (*TxNamespace)(nil), // 2: protoblocktx.TxNamespace - (*Read)(nil), // 3: protoblocktx.Read - (*ReadWrite)(nil), // 4: protoblocktx.ReadWrite - (*Write)(nil), // 5: protoblocktx.Write - (*NamespacePolicy)(nil), // 6: protoblocktx.NamespacePolicy - (*BlockInfo)(nil), // 7: protoblocktx.BlockInfo - (*LastCommittedBlock)(nil), // 8: protoblocktx.LastCommittedBlock - (*QueryStatus)(nil), // 9: protoblocktx.QueryStatus - (*TransactionsStatus)(nil), // 10: protoblocktx.TransactionsStatus - (*StatusWithHeight)(nil), // 11: protoblocktx.StatusWithHeight - (*NamespacePolicies)(nil), // 12: protoblocktx.NamespacePolicies - (*PolicyItem)(nil), // 13: protoblocktx.PolicyItem - (*ConfigTransaction)(nil), // 14: protoblocktx.ConfigTransaction - nil, // 15: protoblocktx.TransactionsStatus.StatusEntry + (PolicyType)(0), // 0: protoblocktx.PolicyType + (Status)(0), // 1: protoblocktx.Status + (*Tx)(nil), // 2: protoblocktx.Tx + (*TxNamespace)(nil), // 3: protoblocktx.TxNamespace + (*Read)(nil), // 4: protoblocktx.Read + (*ReadWrite)(nil), // 5: protoblocktx.ReadWrite + (*Write)(nil), // 6: protoblocktx.Write + (*Endorsements)(nil), // 7: protoblocktx.Endorsements + (*EndorsementWithIdentity)(nil), // 8: protoblocktx.EndorsementWithIdentity + (*Identity)(nil), // 9: protoblocktx.Identity + (*NamespacePolicy)(nil), // 10: protoblocktx.NamespacePolicy + (*ThresholdRule)(nil), // 11: protoblocktx.ThresholdRule + (*BlockInfo)(nil), // 12: protoblocktx.BlockInfo + (*LastCommittedBlock)(nil), // 13: protoblocktx.LastCommittedBlock + (*QueryStatus)(nil), // 14: protoblocktx.QueryStatus + (*TransactionsStatus)(nil), // 15: protoblocktx.TransactionsStatus + (*StatusWithHeight)(nil), // 16: protoblocktx.StatusWithHeight + (*NamespacePolicies)(nil), // 17: protoblocktx.NamespacePolicies + (*PolicyItem)(nil), // 18: protoblocktx.PolicyItem + (*ConfigTransaction)(nil), // 19: protoblocktx.ConfigTransaction + nil, // 20: protoblocktx.TransactionsStatus.StatusEntry } var file_api_protoblocktx_block_tx_proto_depIdxs = []int32{ - 2, // 0: protoblocktx.Tx.namespaces:type_name -> protoblocktx.TxNamespace - 3, // 1: protoblocktx.TxNamespace.reads_only:type_name -> protoblocktx.Read - 4, // 2: protoblocktx.TxNamespace.read_writes:type_name -> protoblocktx.ReadWrite - 5, // 3: protoblocktx.TxNamespace.blind_writes:type_name -> protoblocktx.Write - 7, // 4: protoblocktx.LastCommittedBlock.block:type_name -> protoblocktx.BlockInfo - 15, // 5: protoblocktx.TransactionsStatus.status:type_name -> protoblocktx.TransactionsStatus.StatusEntry - 0, // 6: protoblocktx.StatusWithHeight.code:type_name -> protoblocktx.Status - 13, // 7: protoblocktx.NamespacePolicies.policies:type_name -> protoblocktx.PolicyItem - 11, // 8: protoblocktx.TransactionsStatus.StatusEntry.value:type_name -> protoblocktx.StatusWithHeight - 9, // [9:9] is the sub-list for method output_type - 9, // [9:9] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 3, // 0: protoblocktx.Tx.namespaces:type_name -> protoblocktx.TxNamespace + 7, // 1: protoblocktx.Tx.endorsements:type_name -> protoblocktx.Endorsements + 4, // 2: protoblocktx.TxNamespace.reads_only:type_name -> protoblocktx.Read + 5, // 3: protoblocktx.TxNamespace.read_writes:type_name -> protoblocktx.ReadWrite + 6, // 4: protoblocktx.TxNamespace.blind_writes:type_name -> protoblocktx.Write + 8, // 5: protoblocktx.Endorsements.endorsements_with_identity:type_name -> protoblocktx.EndorsementWithIdentity + 9, // 6: protoblocktx.EndorsementWithIdentity.identity:type_name -> protoblocktx.Identity + 0, // 7: protoblocktx.NamespacePolicy.type:type_name -> protoblocktx.PolicyType + 12, // 8: protoblocktx.LastCommittedBlock.block:type_name -> protoblocktx.BlockInfo + 20, // 9: protoblocktx.TransactionsStatus.status:type_name -> protoblocktx.TransactionsStatus.StatusEntry + 1, // 10: protoblocktx.StatusWithHeight.code:type_name -> protoblocktx.Status + 18, // 11: protoblocktx.NamespacePolicies.policies:type_name -> protoblocktx.PolicyItem + 16, // 12: protoblocktx.TransactionsStatus.StatusEntry.value:type_name -> protoblocktx.StatusWithHeight + 13, // [13:13] is the sub-list for method output_type + 13, // [13:13] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_api_protoblocktx_block_tx_proto_init() } @@ -1169,7 +1520,7 @@ func file_api_protoblocktx_block_tx_proto_init() { } } file_api_protoblocktx_block_tx_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NamespacePolicy); i { + switch v := v.(*Endorsements); i { case 0: return &v.state case 1: @@ -1181,7 +1532,7 @@ func file_api_protoblocktx_block_tx_proto_init() { } } file_api_protoblocktx_block_tx_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlockInfo); i { + switch v := v.(*EndorsementWithIdentity); i { case 0: return &v.state case 1: @@ -1193,7 +1544,7 @@ func file_api_protoblocktx_block_tx_proto_init() { } } file_api_protoblocktx_block_tx_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LastCommittedBlock); i { + switch v := v.(*Identity); i { case 0: return &v.state case 1: @@ -1205,7 +1556,7 @@ func file_api_protoblocktx_block_tx_proto_init() { } } file_api_protoblocktx_block_tx_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryStatus); i { + switch v := v.(*NamespacePolicy); i { case 0: return &v.state case 1: @@ -1217,7 +1568,7 @@ func file_api_protoblocktx_block_tx_proto_init() { } } file_api_protoblocktx_block_tx_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TransactionsStatus); i { + switch v := v.(*ThresholdRule); i { case 0: return &v.state case 1: @@ -1229,7 +1580,7 @@ func file_api_protoblocktx_block_tx_proto_init() { } } file_api_protoblocktx_block_tx_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatusWithHeight); i { + switch v := v.(*BlockInfo); i { case 0: return &v.state case 1: @@ -1241,7 +1592,7 @@ func file_api_protoblocktx_block_tx_proto_init() { } } file_api_protoblocktx_block_tx_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NamespacePolicies); i { + switch v := v.(*LastCommittedBlock); i { case 0: return &v.state case 1: @@ -1253,7 +1604,7 @@ func file_api_protoblocktx_block_tx_proto_init() { } } file_api_protoblocktx_block_tx_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyItem); i { + switch v := v.(*QueryStatus); i { case 0: return &v.state case 1: @@ -1265,6 +1616,54 @@ func file_api_protoblocktx_block_tx_proto_init() { } } file_api_protoblocktx_block_tx_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionsStatus); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_protoblocktx_block_tx_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatusWithHeight); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_protoblocktx_block_tx_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NamespacePolicies); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_protoblocktx_block_tx_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PolicyItem); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_protoblocktx_block_tx_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ConfigTransaction); i { case 0: return &v.state @@ -1279,14 +1678,18 @@ func file_api_protoblocktx_block_tx_proto_init() { } file_api_protoblocktx_block_tx_proto_msgTypes[2].OneofWrappers = []interface{}{} file_api_protoblocktx_block_tx_proto_msgTypes[3].OneofWrappers = []interface{}{} - file_api_protoblocktx_block_tx_proto_msgTypes[7].OneofWrappers = []interface{}{} + file_api_protoblocktx_block_tx_proto_msgTypes[7].OneofWrappers = []interface{}{ + (*Identity_Certificate)(nil), + (*Identity_CertificateId)(nil), + } + file_api_protoblocktx_block_tx_proto_msgTypes[11].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_protoblocktx_block_tx_proto_rawDesc, - NumEnums: 1, - NumMessages: 15, + NumEnums: 2, + NumMessages: 19, NumExtensions: 0, NumServices: 0, }, diff --git a/api/protoblocktx/block_tx.proto b/api/protoblocktx/block_tx.proto index cdc18b70..1ceaaa39 100644 --- a/api/protoblocktx/block_tx.proto +++ b/api/protoblocktx/block_tx.proto @@ -10,10 +10,14 @@ option go_package = "github.com/hyperledger/fabric-x-committer/api/protoblocktx" package protoblocktx; -// Represents a transaction in the blockchain. message Tx { - repeated TxNamespace namespaces = 1; // Namespaces associated with the transaction. - repeated bytes signatures = 2; // Signature per namespace. + // A list of namespaces that define the transaction's scope. + repeated TxNamespace namespaces = 1; + + // A list of endorsements. + // IMPORTANT: This list MUST be the same size as the namespaces list. + // The Endorsement at index i corresponds to the namespace at index i. + repeated Endorsements endorsements = 2; } // Represents a namespace within a transaction. @@ -44,9 +48,54 @@ message Write { bytes value = 2; // The value associated with the key being written. } +// Endorsements holds all the signatures that correspond to a single namespace +// in the transaction's namespaces list. +message Endorsements { + // The list of individual signatures for the corresponding namespace. + repeated EndorsementWithIdentity endorsements_with_identity = 1; +} + +// EndorsementWithIdentity bundles a single signature with the identity of its creator. +message EndorsementWithIdentity { + // The actual cryptographic signature bytes. + bytes endorsement = 1; + + // The identity of the creator who produced the signature, i.e., the endorsement. + Identity identity = 2; +} + +message Identity { + // The identifier of the associated membership service provider + string msp_id = 1; + + oneof creator { + // The full raw bytes of the creator's certificate (e.g., an X.509 certificate). + bytes certificate= 2; + + // An identifier for a certificate that is pre-stored or known by the committer. + string certificate_id = 3; + } +} + // Represents a namespace policy. message NamespacePolicy { - string scheme = 1; // The scheme for signature verification. + PolicyType type = 1; // The type of policy used. + bytes policy = 2; // The policy rule. +} + +enum PolicyType { + // A policy for verifying a single signature that was generated via a Threshold Signature + // Scheme (TSS). In a TSS, a threshold (T) of N parties must cooperate to + // collectively compute and produce the single signature. + THRESHOLD_RULE = 0; + + // A policy defined by an explicit rule that evaluates one or more required signatures. + // For example: "OR('Org1MSP.admin', 'Org2MSP.admin')" + SIGNATURE_RULE = 1; +} + +message ThresholdRule { + string scheme = 1; // The scheme for signature verification. bytes public_key = 2; // The public key for signature verification. } @@ -78,7 +127,7 @@ message NamespacePolicies { message PolicyItem { string namespace = 1; - bytes policy = 2; + bytes policy = 2; // This holds the complete NamespacePolicy. uint64 version = 3; } diff --git a/go.mod b/go.mod index fbdb1d47..7e11f846 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ go 1.24.3 require ( github.com/cenkalti/backoff/v4 v4.3.0 - github.com/cockroachdb/errors v1.11.3 + github.com/cockroachdb/errors v1.12.0 github.com/consensys/gnark-crypto v0.14.0 github.com/docker/docker v28.0.0+incompatible github.com/docker/go-connections v0.5.0 @@ -20,7 +20,7 @@ require ( github.com/hyperledger/fabric v1.4.0-rc1.0.20240918034325-94590aa4332b github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5 github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 - github.com/hyperledger/fabric-x-common v0.0.0-20250701155113-a1ddf93333d8 + github.com/hyperledger/fabric-x-common v0.0.0-20250929135229-402ed28111ec github.com/jackc/puddle v1.3.0 github.com/mitchellh/mapstructure v1.5.0 github.com/onsi/gomega v1.34.2 @@ -79,6 +79,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect github.com/hyperledger-labs/SmartBFT v0.0.0-20240916013553-852e5be5889b // indirect github.com/hyperledger/aries-bbs-go v0.0.0-20240528084656-761671ea73bc // indirect github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 // indirect @@ -113,6 +114,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nxadm/tail v1.4.11 // indirect github.com/onsi/ginkgo v1.16.5 // indirect + github.com/onsi/ginkgo/v2 v2.20.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect diff --git a/go.sum b/go.sum index c09a44b0..9c06455a 100644 --- a/go.sum +++ b/go.sum @@ -673,8 +673,8 @@ github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= -github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo= +github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= @@ -921,8 +921,8 @@ github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5 h1:RPW github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5/go.mod h1:SHNCq8AB0VpHAmvJEtdbzabv6NNV1F48JdmDihasBjc= github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 h1:Xpd6fzG/KjAOHJsq7EQXY2l+qi/y8muxBaY7R6QWABk= github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3/go.mod h1:2pq0ui6ZWA0cC8J+eCErgnMDCS1kPOEYVY+06ZAK0qE= -github.com/hyperledger/fabric-x-common v0.0.0-20250701155113-a1ddf93333d8 h1:6cGlSZDuTxCIFOU2JUNDoaFoxZRhchJ3oncTGo9GDOE= -github.com/hyperledger/fabric-x-common v0.0.0-20250701155113-a1ddf93333d8/go.mod h1:xO5q1ytO5d5/DeWkDKMGAgoaWBnhmLIjT/ZS2BZfFVs= +github.com/hyperledger/fabric-x-common v0.0.0-20250929135229-402ed28111ec h1:qN9nTEzDkwHl8ZXba04tKsbT0UbJwH431EO2SrVEq6s= +github.com/hyperledger/fabric-x-common v0.0.0-20250929135229-402ed28111ec/go.mod h1:Jq5eZFxs8ofSglp5YOYKlNfz1NLw9hBVWTz6bk52vc4= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= diff --git a/integration/runner/runtime.go b/integration/runner/runtime.go index f3902e07..dcb846d2 100644 --- a/integration/runner/runtime.go +++ b/integration/runner/runtime.go @@ -409,9 +409,9 @@ func (c *CommitterRuntime) MakeAndSendTransactionsToOrderer( Namespaces: namespaces, } if expectedStatus != nil && expectedStatus[i] == protoblocktx.Status_ABORTED_SIGNATURE_INVALID { - tx.Signatures = make([][]byte, len(namespaces)) + tx.Endorsements = make([]*protoblocktx.Endorsements, len(namespaces)) for nsIdx := range namespaces { - tx.Signatures[nsIdx] = []byte("dummy") + tx.Endorsements[nsIdx] = test.CreateEndorsementsForThresholdRule([]byte("dummy"))[0] } } txs[i] = c.TxBuilder.MakeTx(tx) diff --git a/integration/test/config_update_test.go b/integration/test/config_update_test.go index 822a4f50..4cd7d3a7 100644 --- a/integration/test/config_update_test.go +++ b/integration/test/config_update_test.go @@ -91,7 +91,7 @@ func TestConfigUpdate(t *testing.T) { ordererEnv.SubmitConfigBlock(t, &workload.ConfigBlock{ ChannelID: c.SystemConfig.Policy.ChannelID, OrdererEndpoints: endpoints, - MetaNamespaceVerificationKey: metaPolicy.PublicKey, + MetaNamespaceVerificationKey: metaPolicy.Policy, }) } submitConfigBlock(ordererEnv.AllRealOrdererEndpoints()) diff --git a/loadgen/workload/config_tx.go b/loadgen/workload/config_tx.go index 7a670546..5b58a777 100644 --- a/loadgen/workload/config_tx.go +++ b/loadgen/workload/config_tx.go @@ -97,7 +97,7 @@ func CreateConfigBlock(policy *PolicyProfile) (*common.Block, error) { // CreateDefaultConfigBlock creates a config block with default values. func CreateDefaultConfigBlock(conf *ConfigBlock) (*common.Block, error) { - configBlock := genesisconfig.Load(genesisconfig.SampleFabricX, configtest.GetDevConfigDir()) + configBlock := genesisconfig.Load(genesisconfig.TwoOrgsSampleFabricX, configtest.GetDevConfigDir()) tlsCertPath := filepath.Join(configtest.GetDevConfigDir(), "msp", "tlscacerts", "tlsroot.pem") for _, consenter := range configBlock.Orderer.ConsenterMapping { consenter.Identity = tlsCertPath diff --git a/loadgen/workload/conflicts.go b/loadgen/workload/conflicts.go index 0a1f308b..7c4278e8 100644 --- a/loadgen/workload/conflicts.go +++ b/loadgen/workload/conflicts.go @@ -11,6 +11,7 @@ import ( "math/rand" "github.com/hyperledger/fabric-x-committer/api/protoblocktx" + "github.com/hyperledger/fabric-x-committer/utils/test" ) // Dependency types. @@ -61,9 +62,9 @@ func newSignTxModifier(rnd *rand.Rand, profile *Profile) *signTxModifier { func (g *signTxModifier) Modify(tx *protoblocktx.Tx) { if g.invalidSignGenerator.Next() { // Pre-assigning prevents TxBuilder from re-signing the TX. - tx.Signatures = make([][]byte, len(tx.Namespaces)) - for i := range tx.Namespaces { - tx.Signatures[i] = g.invalidSignature + tx.Endorsements = make([]*protoblocktx.Endorsements, len(tx.Namespaces)) + for i := range len(tx.Namespaces) { + tx.Endorsements[i] = test.CreateEndorsementsForThresholdRule(g.invalidSignature)[0] } } } diff --git a/loadgen/workload/sign.go b/loadgen/workload/sign.go index 8d9b1243..8864b7a3 100644 --- a/loadgen/workload/sign.go +++ b/loadgen/workload/sign.go @@ -10,6 +10,7 @@ import ( "os" "github.com/cockroachdb/errors" + "github.com/hyperledger/fabric-x-common/protoutil" "github.com/hyperledger/fabric-x-committer/api/protoblocktx" "github.com/hyperledger/fabric-x-committer/api/types" @@ -17,6 +18,7 @@ import ( "github.com/hyperledger/fabric-x-committer/utils/logging" "github.com/hyperledger/fabric-x-committer/utils/signature" "github.com/hyperledger/fabric-x-committer/utils/signature/sigtest" + "github.com/hyperledger/fabric-x-committer/utils/test" ) var logger = logging.New("load-gen-sign") @@ -57,19 +59,19 @@ func NewTxSignerVerifier(policy *PolicyProfile) *TxSignerVerifier { // Sign signs a TX. func (e *TxSignerVerifier) Sign(txID string, tx *protoblocktx.Tx) { - tx.Signatures = make([][]byte, len(tx.Namespaces)) + tx.Endorsements = make([]*protoblocktx.Endorsements, len(tx.Namespaces)) for nsIndex, ns := range tx.Namespaces { signer, ok := e.HashSigners[ns.NsId] if !ok { continue } - tx.Signatures[nsIndex] = signer.Sign(txID, tx, nsIndex) + tx.Endorsements[nsIndex] = test.CreateEndorsementsForThresholdRule(signer.Sign(txID, tx, nsIndex))[0] } } // Verify verifies a signature on the transaction. func (e *TxSignerVerifier) Verify(txID string, tx *protoblocktx.Tx) bool { - if len(tx.Signatures) < len(tx.Namespaces) { + if len(tx.Endorsements) < len(tx.Namespaces) { return false } @@ -130,8 +132,11 @@ func (e *HashSignerVerifier) Verify(txID string, tx *protoblocktx.Tx, nsIndex in // GetVerificationPolicy returns the verification policy. func (e *HashSignerVerifier) GetVerificationPolicy() *protoblocktx.NamespacePolicy { return &protoblocktx.NamespacePolicy{ - Scheme: e.scheme, - PublicKey: e.pubKey, + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: e.scheme, + PublicKey: e.pubKey, + }), } } diff --git a/loadgen/workload/tx_builder.go b/loadgen/workload/tx_builder.go index 5888e0d2..dce721b5 100644 --- a/loadgen/workload/tx_builder.go +++ b/loadgen/workload/tx_builder.go @@ -89,14 +89,14 @@ func (txb *TxBuilder) makeTx(optionalTxID *string, blockTx *protoblocktx.Tx) *pr // 2. Signs the TX: switch { - case blockTx.Signatures != nil: + case len(blockTx.Endorsements) > 0: // If the TX already have a signature, it doesn't re-sign it. case txb.TxSigner != nil: // If TxSigner is given, it is used to sign the TX. txb.TxSigner.Sign(txID, blockTx) case txb.TxSigner == nil: // Otherwise, it puts empty signatures for all namespaces to ensure well-formed TX. - blockTx.Signatures = make([][]byte, len(blockTx.Namespaces)) + blockTx.Endorsements = make([]*protoblocktx.Endorsements, len(blockTx.Namespaces)) } // 3. Serializes the envelope's payload. diff --git a/mock/sigverifier.go b/mock/sigverifier.go index 1915b545..35be8499 100644 --- a/mock/sigverifier.go +++ b/mock/sigverifier.go @@ -131,7 +131,7 @@ func (m *SigVerifier) sendResponseBatch( } status := protoblocktx.Status_COMMITTED isConfig := len(req.Tx.Namespaces) == 1 && req.Tx.Namespaces[0].NsId == types.ConfigNamespaceID - if len(req.Tx.Signatures) == 0 && !isConfig { + if len(req.Tx.Endorsements) == 0 && !isConfig { status = protoblocktx.Status_ABORTED_SIGNATURE_INVALID } respBatch.Responses = append(respBatch.Responses, &protosigverifierservice.Response{ diff --git a/service/coordinator/coordinator_test.go b/service/coordinator/coordinator_test.go index e626624c..6eccf245 100644 --- a/service/coordinator/coordinator_test.go +++ b/service/coordinator/coordinator_test.go @@ -15,6 +15,7 @@ import ( "time" "github.com/google/uuid" + "github.com/hyperledger/fabric/protoutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" @@ -28,6 +29,7 @@ import ( "github.com/hyperledger/fabric-x-committer/utils/channel" "github.com/hyperledger/fabric-x-committer/utils/connection" "github.com/hyperledger/fabric-x-committer/utils/monitoring" + "github.com/hyperledger/fabric-x-committer/utils/signature" "github.com/hyperledger/fabric-x-committer/utils/test" ) @@ -156,8 +158,11 @@ func (e *coordinatorTestEnv) ensureStreamActive(t *testing.T) { func (e *coordinatorTestEnv) createNamespaces(t *testing.T, blkNum int, nsIDs ...string) { t.Helper() p := &protoblocktx.NamespacePolicy{ - Scheme: "ECDSA", - PublicKey: []byte("publicKey"), + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: []byte("publicKey"), + }), } pBytes, err := proto.Marshal(p) require.NoError(t, err) @@ -193,7 +198,7 @@ func (e *coordinatorTestEnv) createNamespaces(t *testing.T, blkNum int, nsIDs .. } for _, tx := range blk.Txs { // The mock verifier verifies that len(tx.Namespace)==len(tx.Signatures) - tx.Content.Signatures = make([][]byte, len(tx.Content.Namespaces)) + tx.Content.Endorsements = make([]*protoblocktx.Endorsements, len(tx.Content.Namespaces)) } err = e.csStream.Send(blk) @@ -254,8 +259,11 @@ func TestCoordinatorServiceValidTx(t *testing.T) { preMetricsValue := test.GetIntMetricValue(t, env.coordinator.metrics.transactionReceivedTotal) p := &protoblocktx.NamespacePolicy{ - Scheme: "ECDSA", - PublicKey: []byte("publicKey"), + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: []byte("publicKey"), + }), } pBytes, err := proto.Marshal(p) require.NoError(t, err) @@ -285,7 +293,7 @@ func TestCoordinatorServiceValidTx(t *testing.T) { }, }, }, - Signatures: make([][]byte, 2), + Endorsements: make([]*protoblocktx.Endorsements, 2), }, }, }, @@ -371,8 +379,11 @@ func TestCoordinatorServiceDependentOrderedTxs(t *testing.T) { mainKey := []byte("main-key") subKey := []byte("sub-key") p := &protoblocktx.NamespacePolicy{ - Scheme: "ECDSA", - PublicKey: []byte("public-key"), + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: []byte("public-key"), + }), } pBytes, err := proto.Marshal(p) require.NoError(t, err) @@ -480,7 +491,7 @@ func TestCoordinatorServiceDependentOrderedTxs(t *testing.T) { }, } for _, tx := range b1.Txs { - tx.Content.Signatures = [][]byte{[]byte("dummy")} + tx.Content.Endorsements = test.CreateEndorsementsForThresholdRule([]byte("dummy")) } expectedReceived := test.GetIntMetricValue(t, env.coordinator.metrics.transactionReceivedTotal) + len(b1.Txs) @@ -563,7 +574,7 @@ func TestCoordinatorRecovery(t *testing.T) { Value: []byte("value1"), }}, }}, - Signatures: make([][]byte, 1), + Endorsements: make([]*protoblocktx.Endorsements, 1), }, }}, }) @@ -585,8 +596,11 @@ func TestCoordinatorRecovery(t *testing.T) { // with two transaction but actual block 2 is supposed to have four transactions. Once the partial block 2 // is committed, we will restart the service and send a full block 2 with all four transactions. nsPolicy, err := proto.Marshal(&protoblocktx.NamespacePolicy{ - Scheme: "ECDSA", - PublicKey: []byte("publicKey"), + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: []byte("publicKey"), + }), }) require.NoError(t, err) block2 := &protocoordinatorservice.Batch{ @@ -601,7 +615,7 @@ func TestCoordinatorRecovery(t *testing.T) { Key: []byte("key2"), }}, }}, - Signatures: make([][]byte, 1), + Endorsements: make([]*protoblocktx.Endorsements, 1), }, }, { @@ -614,7 +628,7 @@ func TestCoordinatorRecovery(t *testing.T) { Key: []byte("key3"), }}, }}, - Signatures: make([][]byte, 1), + Endorsements: make([]*protoblocktx.Endorsements, 1), }, }, { @@ -628,7 +642,7 @@ func TestCoordinatorRecovery(t *testing.T) { Value: []byte("value1"), }}, }}, - Signatures: make([][]byte, 1), + Endorsements: make([]*protoblocktx.Endorsements, 1), }, }, }, @@ -668,7 +682,7 @@ func TestCoordinatorRecovery(t *testing.T) { Key: []byte("key2"), }}, }}, - Signatures: make([][]byte, 1), + Endorsements: make([]*protoblocktx.Endorsements, 1), }, }, { @@ -681,7 +695,7 @@ func TestCoordinatorRecovery(t *testing.T) { Key: []byte("key3"), }}, }}, - Signatures: make([][]byte, 1), + Endorsements: make([]*protoblocktx.Endorsements, 1), }, }, { @@ -694,7 +708,7 @@ func TestCoordinatorRecovery(t *testing.T) { Key: []byte("key3"), }}, }}, - Signatures: [][]byte{[]byte("dummy")}, + Endorsements: test.CreateEndorsementsForThresholdRule([]byte("dummy")), }, }, { @@ -721,7 +735,7 @@ func TestCoordinatorRecovery(t *testing.T) { NsVersion: 0, }, }, - Signatures: make([][]byte, 3), + Endorsements: make([]*protoblocktx.Endorsements, 3), }, }, { @@ -735,7 +749,7 @@ func TestCoordinatorRecovery(t *testing.T) { Value: []byte("value1"), }}, }}, - Signatures: make([][]byte, 1), + Endorsements: make([]*protoblocktx.Endorsements, 1), }, }, }, @@ -775,7 +789,7 @@ func TestCoordinatorStreamFailureWithSidecar(t *testing.T) { Key: []byte("key1"), }}, }}, - Signatures: [][]byte{[]byte("dummy")}, + Endorsements: test.CreateEndorsementsForThresholdRule([]byte("dummy")), }, }, }, @@ -981,7 +995,7 @@ func makeTestBlock(txPerBlock int) (*protocoordinatorservice.Batch, map[string]* Key: []byte("key" + strconv.Itoa(i)), }}, }}, - Signatures: [][]byte{[]byte("dummy")}, + Endorsements: test.CreateEndorsementsForThresholdRule([]byte("dummy")), }, } //nolint: gosec // int -> uint32. diff --git a/service/coordinator/dependencygraph/transaction_node.go b/service/coordinator/dependencygraph/transaction_node.go index e430a158..ac289e08 100644 --- a/service/coordinator/dependencygraph/transaction_node.go +++ b/service/coordinator/dependencygraph/transaction_node.go @@ -21,7 +21,7 @@ type ( // TransactionNode is a node in the dependency graph. TransactionNode struct { Tx *protovcservice.Tx - Signatures [][]byte + Signatures []*protoblocktx.Endorsements // dependsOnTxs is a set of transactions that this transaction depends on. // A transaction is eligible for validation once all the transactions @@ -76,7 +76,7 @@ func newTransactionNode(tx *protocoordinatorservice.Tx) *TransactionNode { Ref: tx.Ref, Namespaces: tx.Content.Namespaces, }, - Signatures: tx.Content.Signatures, + Signatures: tx.Content.Endorsements, rwKeys: readAndWriteKeys(tx.Content.Namespaces), } } diff --git a/service/coordinator/policy_manager_test.go b/service/coordinator/policy_manager_test.go index c65e5781..35921e0f 100644 --- a/service/coordinator/policy_manager_test.go +++ b/service/coordinator/policy_manager_test.go @@ -9,6 +9,7 @@ package coordinator import ( "testing" + "github.com/hyperledger/fabric-x-common/protoutil" "github.com/stretchr/testify/require" "github.com/hyperledger/fabric-x-committer/api/protoblocktx" @@ -154,7 +155,10 @@ func requireUpdateEqual(t *testing.T, expected, actual *protosigverifierservice. func makeFakePolicy(t *testing.T, ns, key string) *protoblocktx.PolicyItem { t.Helper() return policy.MakePolicy(t, ns, &protoblocktx.NamespacePolicy{ - PublicKey: []byte(key), - Scheme: signature.Ecdsa, + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: []byte(key), + }), }) } diff --git a/service/coordinator/signature_verifier_manager.go b/service/coordinator/signature_verifier_manager.go index 50bf29eb..4f902c77 100644 --- a/service/coordinator/signature_verifier_manager.go +++ b/service/coordinator/signature_verifier_manager.go @@ -224,8 +224,8 @@ func (sv *signatureVerifier) sendTransactionsToSVService( request.Requests[idx] = &protosigverifierservice.Tx{ Ref: txNode.Tx.Ref, Tx: &protoblocktx.Tx{ - Namespaces: txNode.Tx.Namespaces, - Signatures: txNode.Signatures, + Namespaces: txNode.Tx.Namespaces, + Endorsements: txNode.Signatures, }, } } diff --git a/service/coordinator/signature_verifier_manager_test.go b/service/coordinator/signature_verifier_manager_test.go index 367c18af..5ff818de 100644 --- a/service/coordinator/signature_verifier_manager_test.go +++ b/service/coordinator/signature_verifier_manager_test.go @@ -265,7 +265,7 @@ func createTxNodeBatchForTest( switch i % 2 { case 0: // even number txs are valid. - txNode.Signatures = [][]byte{[]byte("dummy")} + txNode.Signatures = test.CreateEndorsementsForThresholdRule([]byte("dummy")) expectedValidatedTxs = append(expectedValidatedTxs, txNode) case 1: // odd number txs are invalid. No signature means invalid transaction. diff --git a/service/coordinator/validator_committer_manager_test.go b/service/coordinator/validator_committer_manager_test.go index 4714d046..4f1fe7cb 100644 --- a/service/coordinator/validator_committer_manager_test.go +++ b/service/coordinator/validator_committer_manager_test.go @@ -14,6 +14,7 @@ import ( "time" "github.com/google/uuid" + "github.com/hyperledger/fabric-x-common/protoutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" @@ -225,8 +226,11 @@ func TestValidatorCommitterManagerX(t *testing.T) { Seed: 10, }).GetVerificationKeyAndSigner() p := &protoblocktx.NamespacePolicy{ - Scheme: signature.Ecdsa, - PublicKey: verificationKey, + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: verificationKey, + }), } pBytes, err := proto.Marshal(p) require.NoError(t, err) diff --git a/service/query/query_service_test.go b/service/query/query_service_test.go index 2efd06bb..d6cd07cd 100644 --- a/service/query/query_service_test.go +++ b/service/query/query_service_test.go @@ -21,7 +21,9 @@ import ( "github.com/yugabyte/pgx/v4/pgxpool" "golang.org/x/exp/slices" "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "github.com/hyperledger/fabric-x-committer/api/protoblocktx" "github.com/hyperledger/fabric-x-committer/api/protoqueryservice" "github.com/hyperledger/fabric-x-committer/api/types" "github.com/hyperledger/fabric-x-committer/loadgen" @@ -279,9 +281,12 @@ func TestQueryPolicies(t *testing.T) { _, ok := expectedNamespaces[p.Namespace] require.True(t, ok) delete(expectedNamespaces, p.Namespace) - item, parseErr := policy.ParseNamespacePolicyItem(p) + item, parseErr := policy.CreateNamespaceVerifier(p, nil) require.NoError(t, parseErr) - require.Equal(t, signature.Ecdsa, item.Scheme) + require.Equal(t, protoblocktx.PolicyType_THRESHOLD_RULE, item.Type) + p := &protoblocktx.ThresholdRule{} + require.NoError(t, proto.Unmarshal(item.Policy, p)) + require.Equal(t, signature.Ecdsa, p.Scheme) } configTX, err := env.clientConn.GetConfigTransaction(t.Context(), nil) diff --git a/service/sidecar/mapping.go b/service/sidecar/mapping.go index d75dc20c..b4e6f537 100644 --- a/service/sidecar/mapping.go +++ b/service/sidecar/mapping.go @@ -242,7 +242,7 @@ func verifyTxForm(tx *protoblocktx.Tx) protoblocktx.Status { if len(tx.Namespaces) == 0 { return protoblocktx.Status_MALFORMED_EMPTY_NAMESPACES } - if len(tx.Namespaces) != len(tx.Signatures) { + if len(tx.Namespaces) != len(tx.Endorsements) { return protoblocktx.Status_MALFORMED_MISSING_SIGNATURE } @@ -300,7 +300,7 @@ func checkMetaNamespace(txNs *protoblocktx.TxNamespace) protoblocktx.Status { return statusNotYetValidated } for _, pd := range u.NamespacePolicies.Policies { - _, err := policy.ParseNamespacePolicyItem(pd) + _, err := policy.CreateNamespaceVerifier(pd, nil) if err != nil { if errors.Is(err, policy.ErrInvalidNamespaceID) { return protoblocktx.Status_MALFORMED_NAMESPACE_ID_INVALID diff --git a/service/sidecar/test_exports.go b/service/sidecar/test_exports.go index fe3be41c..e1751d70 100644 --- a/service/sidecar/test_exports.go +++ b/service/sidecar/test_exports.go @@ -10,9 +10,9 @@ import ( "testing" "time" + "github.com/hyperledger/fabric-x-common/protoutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" "github.com/hyperledger/fabric-x-committer/api/protoblocktx" "github.com/hyperledger/fabric-x-committer/api/protoloadgen" @@ -85,12 +85,8 @@ func MalformedTxTestCases(txb *workload.TxBuilder) ( Namespaces: make([]*protoblocktx.TxNamespace, 0), })) add(protoblocktx.Status_MALFORMED_MISSING_SIGNATURE, txb.MakeTx(&protoblocktx.Tx{ - Namespaces: validTxNamespaces, - Signatures: make([][]byte, 0), // Not enough signatures. - })) - add(protoblocktx.Status_MALFORMED_MISSING_SIGNATURE, txb.MakeTx(&protoblocktx.Tx{ - Namespaces: validTxNamespaces, - Signatures: make([][]byte, 2), // Too many signatures. + Namespaces: validTxNamespaces, + Endorsements: make([]*protoblocktx.Endorsements, 2), // Too many signatures. })) add(protoblocktx.Status_MALFORMED_NO_WRITES, txb.MakeTx(&protoblocktx.Tx{ Namespaces: []*protoblocktx.TxNamespace{{ @@ -247,19 +243,23 @@ func MalformedTxTestCases(txb *workload.TxBuilder) ( } func defaultNsInvalidPolicy() []byte { - nsPolicy, _ := proto.Marshal(&protoblocktx.NamespacePolicy{ - Scheme: signature.Ecdsa, - PublicKey: []byte("publicKey"), + return protoutil.MarshalOrPanic(&protoblocktx.NamespacePolicy{ + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: []byte("publicKey"), + }), }) - return nsPolicy } func defaultNsValidPolicy() []byte { factory := sigtest.NewSignatureFactory(signature.Ecdsa) _, verificationKey := factory.NewKeys() - nsPolicy, _ := proto.Marshal(&protoblocktx.NamespacePolicy{ - Scheme: signature.Ecdsa, - PublicKey: verificationKey, + return protoutil.MarshalOrPanic(&protoblocktx.NamespacePolicy{ + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: verificationKey, + }), }) - return nsPolicy } diff --git a/service/vc/validator_committer_service_test.go b/service/vc/validator_committer_service_test.go index b1989825..d106e0f0 100644 --- a/service/vc/validator_committer_service_test.go +++ b/service/vc/validator_committer_service_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/hyperledger/fabric-x-common/protoutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" @@ -23,6 +24,7 @@ import ( "github.com/hyperledger/fabric-x-committer/utils" "github.com/hyperledger/fabric-x-committer/utils/connection" "github.com/hyperledger/fabric-x-committer/utils/grpcerror" + "github.com/hyperledger/fabric-x-committer/utils/signature" "github.com/hyperledger/fabric-x-committer/utils/test" ) @@ -98,8 +100,11 @@ func TestCreateConfigAndTables(t *testing.T) { t.Parallel() env := newValidatorAndCommitServiceTestEnvWithClient(t, 1) p := &protoblocktx.NamespacePolicy{ - Scheme: "ECDSA", - PublicKey: []byte("public-key"), + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: []byte("public-key"), + }), } pBytes, err := proto.Marshal(p) require.NoError(t, err) diff --git a/service/verifier/policy/policy.go b/service/verifier/policy/policy.go index a039755b..24b16d05 100644 --- a/service/verifier/policy/policy.go +++ b/service/verifier/policy/policy.go @@ -12,6 +12,7 @@ import ( "github.com/cockroachdb/errors" "github.com/hyperledger/fabric-lib-go/bccsp/factory" "github.com/hyperledger/fabric-x-common/common/channelconfig" + "github.com/hyperledger/fabric-x-common/msp" "github.com/hyperledger/fabric/protoutil" "google.golang.org/protobuf/proto" @@ -75,8 +76,10 @@ func GetUpdatesFromNamespace(nsTx *protoblocktx.TxNamespace) *protosigverifierse return nil } -// ParseNamespacePolicyItem parses policy item to a namespace policy. -func ParseNamespacePolicyItem(pd *protoblocktx.PolicyItem) (*signature.NsVerifier, error) { +// CreateNamespaceVerifier parses policy item to a namespace policy. +func CreateNamespaceVerifier( + pd *protoblocktx.PolicyItem, idDeserializer msp.IdentityDeserializer, +) (*signature.NsVerifier, error) { if err := validateNamespaceIDInPolicy(pd.Namespace); err != nil { return nil, err } @@ -85,7 +88,8 @@ func ParseNamespacePolicyItem(pd *protoblocktx.PolicyItem) (*signature.NsVerifie if err != nil { return nil, errors.Wrap(err, "failed to unmarshal namespace policy") } - return signature.NewNsVerifier(p.Scheme, p.PublicKey) + + return signature.NewNsVerifier(p, idDeserializer) } // validateNamespaceIDInPolicy checks that a given namespace fulfills namespace naming conventions. @@ -135,5 +139,16 @@ func ParsePolicyFromConfigTx(value []byte) (*signature.NsVerifier, error) { // We use existing proto here to avoid introducing new ones. // So we encode the key schema as the identifier. // This will be replaced in the future with a generic policy mechanism. - return signature.NewNsVerifier(key.KeyIdentifier, key.KeyMaterial) + + policy, err := proto.Marshal(&protoblocktx.ThresholdRule{ + Scheme: key.KeyIdentifier, + PublicKey: key.KeyMaterial, + }) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal ThresholdRule") + } + return signature.NewNsVerifier(&protoblocktx.NamespacePolicy{ + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: policy, + }, nil) } diff --git a/service/verifier/policy/policy_test.go b/service/verifier/policy/policy_test.go index ce555a7c..f895714a 100644 --- a/service/verifier/policy/policy_test.go +++ b/service/verifier/policy/policy_test.go @@ -10,6 +10,7 @@ import ( "fmt" "testing" + "github.com/hyperledger/fabric-x-common/protoutil" "github.com/stretchr/testify/require" "github.com/hyperledger/fabric-x-committer/api/protoblocktx" @@ -64,16 +65,17 @@ func TestParsePolicyItem(t *testing.T) { t.Parallel() _, verificationKey := sigtest.NewSignatureFactory(signature.Ecdsa).NewKeys() p := &protoblocktx.NamespacePolicy{ - Scheme: signature.Ecdsa, - PublicKey: verificationKey, + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, PublicKey: verificationKey, + }), } for _, ns := range []string{"0", "1"} { t.Run(fmt.Sprintf("valid policy ns: '%s'", ns), func(t *testing.T) { pd := MakePolicy(t, ns, p) - retP, err := ParseNamespacePolicyItem(pd) + retP, err := CreateNamespaceVerifier(pd, nil) require.NoError(t, err) - require.Equal(t, p.PublicKey, retP.PublicKey) - require.Equal(t, p.Scheme, retP.Scheme) + require.Equal(t, p.Policy, retP.Policy) }) } @@ -83,7 +85,7 @@ func TestParsePolicyItem(t *testing.T) { } { t.Run(fmt.Sprintf("valid ns: '%s'", ns), func(t *testing.T) { pd := MakePolicy(t, ns, p) - _, err := ParseNamespacePolicyItem(pd) + _, err := CreateNamespaceVerifier(pd, nil) require.NoError(t, err) }) } @@ -96,7 +98,7 @@ func TestParsePolicyItem(t *testing.T) { t.Run(fmt.Sprintf("invalid ns: '%s'", ns), func(t *testing.T) { t.Parallel() pd := MakePolicy(t, ns, p) - _, err := ParseNamespacePolicyItem(pd) + _, err := CreateNamespaceVerifier(pd, nil) require.ErrorIs(t, err, ErrInvalidNamespaceID) }) } @@ -104,7 +106,7 @@ func TestParsePolicyItem(t *testing.T) { t.Run("invalid policy", func(t *testing.T) { pd := MakePolicy(t, "0", p) pd.Policy = []byte("bad-policy") - _, err := ParseNamespacePolicyItem(pd) + _, err := CreateNamespaceVerifier(pd, nil) require.Error(t, err) }) } diff --git a/service/verifier/policy/test_exports.go b/service/verifier/policy/test_exports.go index c5ff3a13..3fab32df 100644 --- a/service/verifier/policy/test_exports.go +++ b/service/verifier/policy/test_exports.go @@ -9,6 +9,7 @@ package policy import ( "testing" + "github.com/hyperledger/fabric/protoutil" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" @@ -28,8 +29,10 @@ func MakePolicy( t.Helper() var policyBytes []byte if ns == types.MetaNamespaceID { + p := &protoblocktx.ThresholdRule{} + require.NoError(t, proto.Unmarshal(nsPolicy.Policy, p)) block, err := workload.CreateDefaultConfigBlock(&workload.ConfigBlock{ - MetaNamespaceVerificationKey: nsPolicy.PublicKey, + MetaNamespaceVerificationKey: p.PublicKey, }) require.NoError(t, err) policyBytes = block.Data.Data[0] @@ -56,8 +59,11 @@ func MakePolicyAndNsSigner( txSigner, err := factory.NewSigner(signingKey) require.NoError(t, err) p := MakePolicy(t, ns, &protoblocktx.NamespacePolicy{ - PublicKey: verificationKey, - Scheme: signature.Ecdsa, + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: verificationKey, + }), }) return p, txSigner } diff --git a/service/verifier/verifier_server.go b/service/verifier/verifier_server.go index 1b46dd69..687eb48f 100644 --- a/service/verifier/verifier_server.go +++ b/service/verifier/verifier_server.go @@ -115,6 +115,12 @@ func (s *Server) handleInputs( } logger.Debugf("Received input from client with %v requests", len(batch.Requests)) + // NOTE: When the sidecar receives a config transaction, it waits for all preceding + // transactions to complete before submitting it to the coordinator. The actual + // configuration update on the verifier is deferred. It is applied just-in-time, + // immediately before the next data transaction is processed, ensuring that + // transaction and all subsequent ones operate under the new configuration. + // Update policies if included in the batch. err := executor.verifier.updatePolicies(batch.Update) if err != nil { diff --git a/service/verifier/verifier_server_test.go b/service/verifier/verifier_server_test.go index 30523166..7038d382 100644 --- a/service/verifier/verifier_server_test.go +++ b/service/verifier/verifier_server_test.go @@ -9,14 +9,21 @@ package verifier import ( "context" "fmt" + "os" + "path/filepath" "testing" "time" + "github.com/hyperledger/fabric-x-common/common/policydsl" + "github.com/hyperledger/fabric-x-common/core/config/configtest" + "github.com/hyperledger/fabric/integration/nwo" + "github.com/hyperledger/fabric/protoutil" "github.com/stretchr/testify/require" "github.com/hyperledger/fabric-x-committer/api/protoblocktx" "github.com/hyperledger/fabric-x-committer/api/protosigverifierservice" "github.com/hyperledger/fabric-x-committer/api/types" + "github.com/hyperledger/fabric-x-committer/loadgen/workload" "github.com/hyperledger/fabric-x-committer/service/verifier/policy" "github.com/hyperledger/fabric-x-committer/utils/channel" "github.com/hyperledger/fabric-x-committer/utils/connection" @@ -26,8 +33,10 @@ import ( "github.com/hyperledger/fabric-x-committer/utils/test" ) -const testTimeout = 3 * time.Second -const fakeTxID = "fake-id" +const ( + testTimeout = 3 * time.Second + fakeTxID = "fake-id" +) func TestVerifierSecureConnection(t *testing.T) { t.Parallel() @@ -82,7 +91,7 @@ func TestMinimalInput(t *testing.T) { stream, _ := c.Client.StartStream(t.Context()) - update, txSigner := defaultUpdate(t) + update, signers := defaultUpdate(t) tx1 := &protoblocktx.Tx{ Namespaces: []*protoblocktx.TxNamespace{{ @@ -93,8 +102,8 @@ func TestMinimalInput(t *testing.T) { }}, }}, } - s, _ := txSigner.SignNs(fakeTxID, tx1, 0) - tx1.Signatures = append(tx1.Signatures, s) + s, _ := signers[1].SignNs(fakeTxID, tx1, 0) + tx1.Endorsements = test.AppendToEndorsementSetsForThresholdRule(tx1.Endorsements, s) tx2 := &protoblocktx.Tx{ Namespaces: []*protoblocktx.TxNamespace{{ @@ -106,8 +115,8 @@ func TestMinimalInput(t *testing.T) { }}, } - s, _ = txSigner.SignNs(fakeTxID, tx2, 0) - tx2.Signatures = append(tx2.Signatures, s) + s, _ = signers[1].SignNs(fakeTxID, tx2, 0) + tx2.Endorsements = test.AppendToEndorsementSetsForThresholdRule(tx2.Endorsements, s) tx3 := &protoblocktx.Tx{ Namespaces: []*protoblocktx.TxNamespace{{ @@ -118,8 +127,8 @@ func TestMinimalInput(t *testing.T) { }}, }}, } - s, _ = txSigner.SignNs(fakeTxID, tx3, 0) - tx3.Signatures = append(tx3.Signatures, s) + s, _ = signers[1].SignNs(fakeTxID, tx3, 0) + tx3.Endorsements = test.AppendToEndorsementSetsForThresholdRule(tx3.Endorsements, s) err := stream.Send(&protosigverifierservice.Batch{ Update: update, @@ -136,6 +145,101 @@ func TestMinimalInput(t *testing.T) { require.Len(t, ret, 3) } +func TestSignatureRule(t *testing.T) { + t.Parallel() + c := newTestState(t, defaultConfigQuickCutoff()) + + stream, err := c.Client.StartStream(t.Context()) + require.NoError(t, err) + + update, _ := defaultUpdate(t) + err = stream.Send(&protosigverifierservice.Batch{Update: update}) + require.NoError(t, err) + + signingIdentities := make([]*nwo.SigningIdentity, 2) + + for i, org := range []string{"Org1", "Org2"} { + signingIdentities[i] = &nwo.SigningIdentity{ + CertPath: filepath.Join(configtest.GetDevConfigDir(), "crypto/"+org+"/users/User1@"+org+"/msp", + "signcerts", "User1@"+org+"-cert.pem"), + KeyPath: filepath.Join(configtest.GetDevConfigDir(), "crypto/"+org+"/users/User1@"+org+"/msp", + "keystore", "key.pem"), + MSPID: org, + } + } + + serializedSigningIdentities := make([][]byte, len(signingIdentities)) + for i, si := range signingIdentities { + serializedIdentity, serr := si.Serialize() + require.NoError(t, serr) + serializedSigningIdentities[i] = serializedIdentity + } + + nsPolicy := &protoblocktx.NamespacePolicy{ + Policy: protoutil.MarshalOrPanic(policydsl.Envelope(policydsl.And(policydsl.SignedBy(0), policydsl.SignedBy(1)), + serializedSigningIdentities)), + Type: protoblocktx.PolicyType_SIGNATURE_RULE, + } + + update = &protosigverifierservice.Update{ + NamespacePolicies: &protoblocktx.NamespacePolicies{ + Policies: []*protoblocktx.PolicyItem{ + policy.MakePolicy(t, "2", nsPolicy), + }, + }, + } + + tx1 := &protoblocktx.Tx{ + Namespaces: []*protoblocktx.TxNamespace{{ + NsId: "2", + NsVersion: 0, + BlindWrites: []*protoblocktx.Write{{ + Key: []byte("0011"), + }}, + }}, + } + + data, err := signature.ASN1MarshalTxNamespace(fakeTxID, tx1.Namespaces[0]) + require.NoError(t, err) + + signatures := make([][]byte, len(signingIdentities)) + mspIDs := make([][]byte, len(signingIdentities)) + certsBytes := make([][]byte, len(signingIdentities)) + for i, si := range signingIdentities { + s, serr := si.Sign(data) + require.NoError(t, serr) + signatures[i] = s + + mspIDs[i] = []byte(si.MSPID) + certBytes, rerr := os.ReadFile(si.CertPath) + require.NoError(t, rerr) + certsBytes[i] = certBytes + } + + tx1.Endorsements = []*protoblocktx.Endorsements{ + test.CreateEndorsementsForSignatureRule(signatures, mspIDs, certsBytes), + } + + requireTestCase(t, stream, &testCase{ + update: update, + req: &protosigverifierservice.Tx{ + Ref: types.TxRef(fakeTxID, 1, 1), Tx: tx1, + }, + expectedStatus: protoblocktx.Status_COMMITTED, + }) + + tx1.Endorsements = []*protoblocktx.Endorsements{ + test.CreateEndorsementsForSignatureRule(signatures[0:1], mspIDs[0:1], certsBytes[0:1]), + } + + requireTestCase(t, stream, &testCase{ + req: &protosigverifierservice.Tx{ + Ref: types.TxRef(fakeTxID, 1, 1), Tx: tx1, + }, + expectedStatus: protoblocktx.Status_ABORTED_SIGNATURE_INVALID, + }) +} + func TestBadSignature(t *testing.T) { t.Parallel() c := newTestState(t, defaultConfigQuickCutoff()) @@ -158,7 +262,7 @@ func TestBadSignature(t *testing.T) { {Key: make([]byte, 0)}, }, }}, - Signatures: [][]byte{{0, 1, 2}}, + Endorsements: test.CreateEndorsementsForThresholdRule([]byte{0}, []byte{1}, []byte{2}), }, }, expectedStatus: protoblocktx.Status_ABORTED_SIGNATURE_INVALID, @@ -179,10 +283,13 @@ func TestUpdatePolicies(t *testing.T) { stream, err := c.Client.StartStream(t.Context()) require.NoError(t, err) + update, _ := defaultUpdate(t) + ns1Policy, _ := makePolicyItem(t, ns1) ns2Policy, _ := makePolicyItem(t, ns2) err = stream.Send(&protosigverifierservice.Batch{ Update: &protosigverifierservice.Update{ + Config: update.Config, NamespacePolicies: &protoblocktx.NamespacePolicies{ Policies: []*protoblocktx.PolicyItem{ns1Policy, ns2Policy}, }, @@ -199,8 +306,11 @@ func TestUpdatePolicies(t *testing.T) { Policies: []*protoblocktx.PolicyItem{ p3, policy.MakePolicy(t, ns2, &protoblocktx.NamespacePolicy{ - PublicKey: []byte("bad-key"), - Scheme: signature.Ecdsa, + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + PublicKey: []byte("bad-key"), + Scheme: signature.Ecdsa, + }), }), }, }, @@ -218,10 +328,13 @@ func TestUpdatePolicies(t *testing.T) { stream, err := c.Client.StartStream(t.Context()) require.NoError(t, err) + update, _ := defaultUpdate(t) + ns1Policy, ns1Signer := makePolicyItem(t, ns1) ns2Policy, _ := makePolicyItem(t, ns2) err = stream.Send(&protosigverifierservice.Batch{ Update: &protosigverifierservice.Update{ + Config: update.Config, NamespacePolicies: &protoblocktx.NamespacePolicies{ Policies: []*protoblocktx.PolicyItem{ns1Policy, ns2Policy}, }, @@ -263,6 +376,8 @@ func TestMultipleUpdatePolicies(t *testing.T) { stream, err := c.Client.StartStream(t.Context()) require.NoError(t, err) + update, _ := defaultUpdate(t) + // Each policy update will update a unique namespace, and the common namespace. updateCount := len(ns) - 1 uniqueNsSigners := make([]*sigtest.NsSigner, updateCount) @@ -273,6 +388,7 @@ func TestMultipleUpdatePolicies(t *testing.T) { commonNsPolicy, commonNsSigner := makePolicyItem(t, ns[len(ns)-1]) commonNsSigners[i] = commonNsSigner p := &protosigverifierservice.Update{ + Config: update.Config, NamespacePolicies: &protoblocktx.NamespacePolicies{ Policies: []*protoblocktx.PolicyItem{uniqueNsPolicy, commonNsPolicy}, }, @@ -321,17 +437,18 @@ func TestMultipleUpdatePolicies(t *testing.T) { } type testCase struct { + update *protosigverifierservice.Update req *protosigverifierservice.Tx expectedStatus protoblocktx.Status } func sign(t *testing.T, tx *protoblocktx.Tx, signers ...*sigtest.NsSigner) { t.Helper() - tx.Signatures = make([][]byte, len(signers)) + tx.Endorsements = make([]*protoblocktx.Endorsements, len(signers)) for i, s := range signers { s, err := s.SignNs(fakeTxID, tx, i) require.NoError(t, err) - tx.Signatures[i] = s + tx.Endorsements[i] = test.CreateEndorsementsForThresholdRule(s)[0] } } @@ -358,8 +475,11 @@ func makePolicyItem(t *testing.T, ns string) (*protoblocktx.PolicyItem, *sigtest txSigner, err := factory.NewSigner(signingKey) require.NoError(t, err) p := policy.MakePolicy(t, ns, &protoblocktx.NamespacePolicy{ - PublicKey: verificationKey, - Scheme: signature.Ecdsa, + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: verificationKey, + }), }) return p, txSigner } @@ -371,6 +491,7 @@ func requireTestCase( ) { t.Helper() err := stream.Send(&protosigverifierservice.Batch{ + Update: tt.update, Requests: []*protosigverifierservice.Tx{tt.req}, }) require.NoError(t, err) @@ -421,22 +542,35 @@ func readStream( return channel.NewReader(ctx, outputChan).Read() } -func defaultUpdate(t *testing.T) (*protosigverifierservice.Update, *sigtest.NsSigner) { +func defaultUpdate(t *testing.T) (*protosigverifierservice.Update, []*sigtest.NsSigner) { t.Helper() factory := sigtest.NewSignatureFactory(signature.Ecdsa) - signingKey, verificationKey := factory.NewKeys() - txSigner, _ := factory.NewSigner(signingKey) + nsTxSigningKey, nsTxVerificationKey := factory.NewKeys() + configBlock, err := workload.CreateDefaultConfigBlock(&workload.ConfigBlock{ + MetaNamespaceVerificationKey: nsTxVerificationKey, + }) + require.NoError(t, err) + nsTxSigner, _ := factory.NewSigner(nsTxSigningKey) + + dataTxSigningKey, dataTxVerificationKey := factory.NewKeys() + dataTxSigner, _ := factory.NewSigner(dataTxSigningKey) update := &protosigverifierservice.Update{ + Config: &protoblocktx.ConfigTransaction{ + Envelope: configBlock.Data.Data[0], + }, NamespacePolicies: &protoblocktx.NamespacePolicies{ Policies: []*protoblocktx.PolicyItem{ policy.MakePolicy(t, "1", &protoblocktx.NamespacePolicy{ - PublicKey: verificationKey, - Scheme: signature.Ecdsa, + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: protoutil.MarshalOrPanic(&protoblocktx.ThresholdRule{ + Scheme: signature.Ecdsa, + PublicKey: dataTxVerificationKey, + }), }), }, }, } - return update, txSigner + return update, []*sigtest.NsSigner{nsTxSigner, dataTxSigner} } func defaultConfigWithTLS(tlsConfig connection.TLSConfig) *Config { diff --git a/service/verifier/verify.go b/service/verifier/verify.go index 9be46a6f..eb5b5d1e 100644 --- a/service/verifier/verify.go +++ b/service/verifier/verify.go @@ -12,6 +12,10 @@ import ( "sync/atomic" "github.com/cockroachdb/errors" + "github.com/hyperledger/fabric-lib-go/bccsp/factory" + "github.com/hyperledger/fabric-x-common/common/channelconfig" + "github.com/hyperledger/fabric-x-common/msp" + "github.com/hyperledger/fabric-x-common/protoutil" "github.com/hyperledger/fabric-x-committer/api/protoblocktx" "github.com/hyperledger/fabric-x-committer/api/protosigverifierservice" @@ -23,6 +27,7 @@ import ( type verifier struct { verifiers atomic.Pointer[map[string]*signature.NsVerifier] + bundle *channelconfig.Bundle } func newVerifier() *verifier { @@ -41,11 +46,16 @@ func (v *verifier) updatePolicies( if update == nil || (update.Config == nil && update.NamespacePolicies == nil) { return nil } + + if err := v.updateBundle(update); err != nil { + return err + } + // We parse the policy during validation and mark transactions as invalid if parsing fails. // While it is unlikely that policy parsing would fail at this stage, it could happen // if the stored policy in the database is corrupted or maliciously altered, or if there is a // bug in the committer that modifies the policy bytes. - newVerifiers, err := parsePolicies(update) + newVerifiers, err := createVerifiers(update, v.bundle.MSPManager()) if err != nil { return errors.Join(ErrUpdatePolicies, err) } @@ -53,17 +63,33 @@ func (v *verifier) updatePolicies( defer logger.Infof("New verification policies for namespaces %v", slices.Collect(maps.Keys(newVerifiers))) for k, nsVerifier := range *v.verifiers.Load() { - if _, ok := newVerifiers[k]; !ok { - newVerifiers[k] = nsVerifier + _, ok := newVerifiers[k] + if ok { + continue } + + // If there is a config update, the verifier for signature policies must be + // recreated to use the latest MSP Manager from the new configuration. + if update.Config != nil && nsVerifier.Type == protoblocktx.PolicyType_SIGNATURE_RULE { + nsVerifier, err = policy.CreateNamespaceVerifier(&protoblocktx.PolicyItem{ + Namespace: k, Policy: nsVerifier.Policy, + }, v.bundle.MSPManager()) + if err != nil { + return err + } + } + newVerifiers[k] = nsVerifier } v.verifiers.Store(&newVerifiers) return nil } -func parsePolicies(update *protosigverifierservice.Update) (map[string]*signature.NsVerifier, error) { +func createVerifiers( + update *protosigverifierservice.Update, idDeserializer msp.IdentityDeserializer, +) (map[string]*signature.NsVerifier, error) { newPolicies := make(map[string]*signature.NsVerifier) if update.Config != nil { + // TODO: Support signature rule for meta namespace policy. nsVerifier, err := policy.ParsePolicyFromConfigTx(update.Config.Envelope) if err != nil { return nil, errors.Join(ErrUpdatePolicies, err) @@ -72,7 +98,7 @@ func parsePolicies(update *protosigverifierservice.Update) (map[string]*signatur } if update.NamespacePolicies != nil { for _, pd := range update.NamespacePolicies.Policies { - nsVerifier, err := policy.ParseNamespacePolicyItem(pd) + nsVerifier, err := policy.CreateNamespaceVerifier(pd, idDeserializer) if err != nil { return nil, errors.Join(ErrUpdatePolicies, err) } @@ -82,6 +108,23 @@ func parsePolicies(update *protosigverifierservice.Update) (map[string]*signatur return newPolicies, nil } +func (v *verifier) updateBundle(u *protosigverifierservice.Update) error { + if u.Config == nil { + return nil + } + envelope, err := protoutil.UnmarshalEnvelope(u.Config.Envelope) + if err != nil { + return errors.Wrap(err, "error unmarshalling envelope") + } + bundle, err := channelconfig.NewBundleFromEnvelope(envelope, factory.GetDefault()) + if err != nil { + return errors.Wrap(err, "error parsing config") + } + + v.bundle = bundle + return nil +} + func (v *verifier) verifyRequest(tx *protosigverifierservice.Tx) *protosigverifierservice.Response { logger.Debugf("Validating TX: %s", &utils.LazyJSON{O: tx}) response := &protosigverifierservice.Response{ diff --git a/utils/signature/block_tx_test.go b/utils/signature/block_tx_test.go index 81d6f662..a950654c 100644 --- a/utils/signature/block_tx_test.go +++ b/utils/signature/block_tx_test.go @@ -55,7 +55,7 @@ func TestAsnMarshal(t *testing.T) { for _, tx := range txs { tx := tx // We don't serialize the signature. - tx.Tx.Signatures = nil + tx.Tx.Endorsements = nil t.Run(tx.Id, func(t *testing.T) { t.Parallel() for _, ns := range tx.Tx.Namespaces { diff --git a/utils/signature/sigtest/bench_test.go b/utils/signature/sigtest/bench_test.go index c0b393d4..dd0e20dd 100644 --- a/utils/signature/sigtest/bench_test.go +++ b/utils/signature/sigtest/bench_test.go @@ -15,6 +15,7 @@ import ( "github.com/hyperledger/fabric-x-committer/utils/logging" "github.com/hyperledger/fabric-x-committer/utils/signature" "github.com/hyperledger/fabric-x-committer/utils/signature/sigtest" + "github.com/hyperledger/fabric-x-committer/utils/test" ) func BenchmarkSign(b *testing.B) { @@ -59,7 +60,7 @@ func BenchmarkVerify(b *testing.B) { for _, tx := range txs { sig, signErr := s.SignNs(tx.Id, tx.Tx, 0) require.NoError(b, signErr) - tx.Tx.Signatures = [][]byte{sig} + tx.Tx.Endorsements = test.CreateEndorsementsForThresholdRule(sig) } errBench := make([]error, b.N) diff --git a/utils/signature/sigtest/factory.go b/utils/signature/sigtest/factory.go index 56518ee6..5b8738c4 100644 --- a/utils/signature/sigtest/factory.go +++ b/utils/signature/sigtest/factory.go @@ -20,7 +20,9 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254" "github.com/consensys/gnark-crypto/ecc/bn254/fr" "golang.org/x/crypto/sha3" + "google.golang.org/protobuf/proto" + "github.com/hyperledger/fabric-x-committer/api/protoblocktx" "github.com/hyperledger/fabric-x-committer/utils" "github.com/hyperledger/fabric-x-committer/utils/signature" ) @@ -59,7 +61,17 @@ func (f *SignerVerifierFactory) Scheme() signature.Scheme { // NewVerifier instantiate a verifier given a public key. func (f *SignerVerifierFactory) NewVerifier(key signature.PublicKey) (*signature.NsVerifier, error) { - v, err := signature.NewNsVerifier(f.scheme, key) + policy, err := proto.Marshal(&protoblocktx.ThresholdRule{ + Scheme: f.scheme, + PublicKey: key, + }) + if err != nil { + return nil, errors.Wrap(err, "error marshaling threshold rule") + } + v, err := signature.NewNsVerifier(&protoblocktx.NamespacePolicy{ + Type: protoblocktx.PolicyType_THRESHOLD_RULE, + Policy: policy, + }, nil) return v, errors.Wrap(err, "error creating verifier") } diff --git a/utils/signature/sigtest/factory_test.go b/utils/signature/sigtest/factory_test.go index 46c8b283..72640e7d 100644 --- a/utils/signature/sigtest/factory_test.go +++ b/utils/signature/sigtest/factory_test.go @@ -18,6 +18,7 @@ import ( "github.com/hyperledger/fabric-x-committer/api/protoblocktx" "github.com/hyperledger/fabric-x-committer/utils/signature" + "github.com/hyperledger/fabric-x-committer/utils/test" ) func TestEndToEnd(t *testing.T) { @@ -40,7 +41,7 @@ func TestEndToEnd(t *testing.T) { }}, } sig, err := s.SignNs(txID, tx, 0) - tx.Signatures = [][]byte{sig} + tx.Endorsements = test.CreateEndorsementsForThresholdRule(sig) require.NoError(t, err) require.NoError(t, v.VerifyNs(txID, tx, 0)) }) @@ -93,12 +94,12 @@ func TestEcdsaPem(t *testing.T) { sig, err := s.SignNs(txID, tx, 0) require.NoError(t, err) - tx.Signatures = [][]byte{sig} + tx.Endorsements = test.CreateEndorsementsForThresholdRule(sig) require.NoError(t, pemV.VerifyNs(txID, tx, 0)) sig, err = pemS.SignNs(txID, tx, 0) require.NoError(t, err) - tx.Signatures = [][]byte{sig} + tx.Endorsements = test.CreateEndorsementsForThresholdRule(sig) require.NoError(t, v.VerifyNs(txID, tx, 0)) } diff --git a/utils/signature/types.go b/utils/signature/types.go index fee0acd1..eeec439c 100644 --- a/utils/signature/types.go +++ b/utils/signature/types.go @@ -21,6 +21,8 @@ type ( PublicKey = []byte // Scheme to be used. Scheme = string + // Policy defining the governance. + Policy = []byte ) // Supported schemes. diff --git a/utils/signature/verify.go b/utils/signature/verify.go index 284ca135..bd0a4f00 100644 --- a/utils/signature/verify.go +++ b/utils/signature/verify.go @@ -7,58 +7,113 @@ SPDX-License-Identifier: Apache-2.0 package signature import ( - "strings" - "github.com/cockroachdb/errors" + fmsp "github.com/hyperledger/fabric-protos-go-apiv2/msp" + "github.com/hyperledger/fabric-x-common/common/cauthdsl" + "github.com/hyperledger/fabric-x-common/common/policies" + "github.com/hyperledger/fabric-x-common/msp" + "github.com/hyperledger/fabric-x-common/protoutil" + "google.golang.org/protobuf/proto" "github.com/hyperledger/fabric-x-committer/api/protoblocktx" ) -// DigestVerifier verifies a digest. -type DigestVerifier interface { +// ThresholdVerifier verifies a digest. +type ThresholdVerifier interface { Verify(Digest, Signature) error } // NsVerifier verifies a given namespace. type NsVerifier struct { - verifier DigestVerifier - Scheme Scheme - PublicKey PublicKey + thresholdVerifier ThresholdVerifier + signatureVerifier policies.Policy + Type protoblocktx.PolicyType + Policy Policy } // NewNsVerifier creates a new namespace verifier according to the implementation scheme. -func NewNsVerifier(scheme Scheme, key PublicKey) (*NsVerifier, error) { +func NewNsVerifier(p *protoblocktx.NamespacePolicy, idDeserializer msp.IdentityDeserializer) (*NsVerifier, error) { res := &NsVerifier{ - Scheme: strings.ToUpper(scheme), - PublicKey: key, + Type: p.Type, + Policy: p.Policy, } var err error - switch res.Scheme { - case NoScheme, "": - res.verifier = nil - case Ecdsa: - res.verifier, err = NewEcdsaVerifier(key) - case Bls: - res.verifier, err = NewBLSVerifier(key) - case Eddsa: - res.verifier = &EdDSAVerifier{PublicKey: key} + + switch p.Type { + case protoblocktx.PolicyType_THRESHOLD_RULE: + policy := &protoblocktx.ThresholdRule{} + if merr := proto.Unmarshal(p.Policy, policy); merr != nil { + return nil, merr + } + + switch policy.Scheme { + case NoScheme, "": + res.thresholdVerifier = nil + case Ecdsa: + res.thresholdVerifier, err = NewEcdsaVerifier(policy.PublicKey) + case Bls: + res.thresholdVerifier, err = NewBLSVerifier(policy.PublicKey) + case Eddsa: + res.thresholdVerifier = &EdDSAVerifier{PublicKey: policy.PublicKey} + default: + return nil, errors.Newf("scheme '%v' not supported", policy.Scheme) + } + case protoblocktx.PolicyType_SIGNATURE_RULE: + pp := cauthdsl.NewPolicyProvider(idDeserializer) + res.signatureVerifier, _, err = pp.NewPolicy(p.Policy) default: - return nil, errors.Newf("scheme '%v' not supported", scheme) + return nil, errors.Newf("policy type '%v' not supported", p.Type) } + return res, errors.Wrap(err, "failed creating verifier") } // VerifyNs verifies a transaction's namespace signature. func (v *NsVerifier) VerifyNs(txID string, tx *protoblocktx.Tx, nsIndex int) error { - if nsIndex < 0 || nsIndex >= len(tx.Namespaces) || nsIndex >= len(tx.Signatures) { + if nsIndex < 0 || nsIndex >= len(tx.Namespaces) || nsIndex >= len(tx.Endorsements) { return errors.New("namespace index out of range") } - if v.verifier == nil { - return nil + + switch v.Type { + case protoblocktx.PolicyType_THRESHOLD_RULE: + if v.thresholdVerifier == nil { + return nil + } + digest, err := DigestTxNamespace(txID, tx.Namespaces[nsIndex]) + if err != nil { + return err + } + return v.thresholdVerifier.Verify(digest, tx.Endorsements[nsIndex].EndorsementsWithIdentity[0].Endorsement) + case protoblocktx.PolicyType_SIGNATURE_RULE: + data, err := ASN1MarshalTxNamespace(txID, tx.Namespaces[nsIndex]) + if err != nil { + return err + } + signedData := make([]*protoutil.SignedData, len(tx.Endorsements[nsIndex].EndorsementsWithIdentity)) + for i, s := range tx.Endorsements[nsIndex].EndorsementsWithIdentity { + idBytes, err := msp.NewSerializedIdentity(s.Identity.MspId, s.Identity.GetCertificate()) + if err != nil { + return err + } + + // Do we need to append Identity to the data? Is identity part of the signature content? + signedData[i] = &protoutil.SignedData{ + Data: data, + Identity: idBytes, + Signature: s.Endorsement, + } + } + return v.signatureVerifier.EvaluateSignedData(signedData) + default: + return errors.Newf("policy type [%v] not supported", v.Type) } - digest, err := DigestTxNamespace(txID, tx.Namespaces[nsIndex]) - if err != nil { - return err +} + +// CreateSerializedIdentities creates serialized identities using the mspIDs and certBytes. +func CreateSerializedIdentities(mspIDs, certBytes []string) [][]byte { + identities := make([][]byte, len(mspIDs)) + for i, mspID := range mspIDs { + identities[i] = protoutil.MarshalOrPanic(&fmsp.SerializedIdentity{Mspid: mspID, IdBytes: []byte(certBytes[i])}) } - return v.verifier.Verify(digest, tx.Signatures[nsIndex]) + return identities } diff --git a/utils/signature/verify_test.go b/utils/signature/verify_test.go new file mode 100644 index 00000000..6e51f423 --- /dev/null +++ b/utils/signature/verify_test.go @@ -0,0 +1,106 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package signature_test + +import ( + "testing" + + "github.com/hyperledger/fabric-x-common/common/cauthdsl" + "github.com/hyperledger/fabric-x-common/common/policydsl" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + "github.com/hyperledger/fabric-x-committer/api/protoblocktx" + "github.com/hyperledger/fabric-x-committer/service/verifier/policy" + "github.com/hyperledger/fabric-x-committer/utils/signature" + "github.com/hyperledger/fabric-x-committer/utils/test" +) + +const fakeTxID = "fake-id" + +func TestNsVerifierThresholdRule(t *testing.T) { + t.Parallel() + p, nsSigner := policy.MakePolicyAndNsSigner(t, "1") + nsPolicy := &protoblocktx.NamespacePolicy{} + require.NoError(t, proto.Unmarshal(p.Policy, nsPolicy)) + nsVerifier, err := signature.NewNsVerifier(nsPolicy, nil) + require.NoError(t, err) + + tx1 := &protoblocktx.Tx{ + Namespaces: []*protoblocktx.TxNamespace{{ + NsId: "1", + NsVersion: 1, + ReadWrites: []*protoblocktx.ReadWrite{{Key: []byte("k1"), Value: []byte("v1")}}, + }}, + } + sig, err := nsSigner.SignNs(fakeTxID, tx1, 0) + require.NoError(t, err) + tx1.Endorsements = test.CreateEndorsementsForThresholdRule(sig) + require.NoError(t, nsVerifier.VerifyNs(fakeTxID, tx1, 0)) +} + +func TestNsVerifierSignatureRule(t *testing.T) { + t.Parallel() + identities := signature.CreateSerializedIdentities([]string{"org0", "org1", "org2", "org3"}, + []string{"id0", "id1", "id2", "id3"}) + + // org0 and org3 must sign along with either org1 or org2. To realize this condition, the policy can be + // written in many ways but we choose the following to test the nested structure. + p := policydsl.Envelope( + policydsl.And( + policydsl.Or( + policydsl.And(policydsl.SignedBy(0), policydsl.SignedBy(1)), + policydsl.And(policydsl.SignedBy(0), policydsl.SignedBy(2)), + ), + policydsl.SignedBy(3), + ), identities) + pBytes, err := proto.Marshal(p) + require.NoError(t, err) + + nsVerifier, err := signature.NewNsVerifier( + &protoblocktx.NamespacePolicy{Policy: pBytes, Type: protoblocktx.PolicyType_SIGNATURE_RULE}, + &cauthdsl.MockIdentityDeserializer{}) + require.NoError(t, err) + + tx1 := &protoblocktx.Tx{ + Namespaces: []*protoblocktx.TxNamespace{{ + NsId: "1", + NsVersion: 1, + ReadWrites: []*protoblocktx.ReadWrite{{Key: []byte("k1"), Value: []byte("v1")}}, + }}, + } + // org0, org3, and org1 sign. + tx1.Endorsements = []*protoblocktx.Endorsements{test.CreateEndorsementsForSignatureRule( + toByteArray("s0", "s3", "s1"), + toByteArray("org0", "org3", "org1"), + toByteArray("id0", "id3", "id1"), + )} + require.NoError(t, nsVerifier.VerifyNs(fakeTxID, tx1, 0)) + + // org0, org3, and org2 sign. + tx1.Endorsements = []*protoblocktx.Endorsements{test.CreateEndorsementsForSignatureRule( + toByteArray("s0", "s3", "s2"), + toByteArray("org0", "org3", "org2"), + toByteArray("id0", "id3", "id2"), + )} + require.NoError(t, nsVerifier.VerifyNs(fakeTxID, tx1, 0)) + + tx1.Endorsements = []*protoblocktx.Endorsements{test.CreateEndorsementsForSignatureRule( + toByteArray("s0", "s3"), + toByteArray("org0", "org3"), + toByteArray("id0", "id3"), + )} + require.ErrorContains(t, nsVerifier.VerifyNs(fakeTxID, tx1, 0), "signature set did not satisfy policy") +} + +func toByteArray(items ...string) [][]byte { + itemBytes := make([][]byte, len(items)) + for i, it := range items { + itemBytes[i] = []byte(it) + } + return itemBytes +} diff --git a/utils/test/utils.go b/utils/test/utils.go index de9bf9dd..6157956e 100644 --- a/utils/test/utils.go +++ b/utils/test/utils.go @@ -431,3 +431,47 @@ func flattenEndpoint(in map[string]any) *connection.Endpoint { } return &connection.Endpoint{Host: hostStr, Port: int(portFloat)} } + +// CreateEndorsementsForThresholdRule creates a slice of EndorsementSet pointers from individual threshold signatures. +// Each signature provided is wrapped in its own EndorsementWithIdentity and then placed +// in its own new EndorsementSet. +func CreateEndorsementsForThresholdRule(signatures ...[]byte) []*protoblocktx.Endorsements { + sets := make([]*protoblocktx.Endorsements, 0, len(signatures)) + + for _, sig := range signatures { + set := &protoblocktx.Endorsements{ + EndorsementsWithIdentity: []*protoblocktx.EndorsementWithIdentity{{Endorsement: sig}}, + } + sets = append(sets, set) + } + + return sets +} + +// CreateEndorsementsForSignatureRule creates a EndorsementSet for a signature rule. +// It takes parallel slices of signatures, MSP IDs, and certificate bytes, +// and creates a EndorsementSet where each signature is paired with its corresponding +// identity (MSP ID and certificate). This is used when a set of signatures +// must all be present to satisfy a rule (e.g., an AND condition). +func CreateEndorsementsForSignatureRule(signatures, mspIDs, certBytes [][]byte) *protoblocktx.Endorsements { + set := &protoblocktx.Endorsements{} + for i, sig := range signatures { + set.EndorsementsWithIdentity = append(set.EndorsementsWithIdentity, + &protoblocktx.EndorsementWithIdentity{ + Endorsement: sig, + Identity: &protoblocktx.Identity{ + MspId: string(mspIDs[i]), + Creator: &protoblocktx.Identity_Certificate{Certificate: certBytes[i]}, + }, + }) + } + return set +} + +// AppendToEndorsementSetsForThresholdRule is a utility function that creates new signature sets +// for a threshold rule and appends them to an existing slice of signature sets. +func AppendToEndorsementSetsForThresholdRule( + ss []*protoblocktx.Endorsements, signatures ...[]byte, +) []*protoblocktx.Endorsements { + return append(ss, CreateEndorsementsForThresholdRule(signatures...)...) +}