diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a227f6c69..4085c795e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -81,6 +81,7 @@ jobs: tests: [ dlog-fabric-t1, dlog-fabric-t2, + dlog-fabric-t2.1, dlog-fabric-t3, dlog-fabric-t4, dlog-fabric-t5, diff --git a/cmd/tokengen/cobra/pp/dlog/update.go b/cmd/tokengen/cobra/pp/dlog/update.go index 0d72ce875..095cad175 100644 --- a/cmd/tokengen/cobra/pp/dlog/update.go +++ b/cmd/tokengen/cobra/pp/dlog/update.go @@ -13,7 +13,6 @@ import ( "github.com/hyperledger-labs/fabric-token-sdk/cmd/tokengen/cobra/pp/common" v1 "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/setup" - "github.com/hyperledger-labs/fabric-token-sdk/token/driver" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -90,10 +89,10 @@ func Update(args *UpdateArgs) error { // Clear auditor and issuers if provided, and add them again. // If not provided, do not change them. if len(args.Auditors) > 0 { - pp.Auditor = []byte{} + pp.SetAuditors(nil) } if len(args.Issuers) > 0 { - pp.IssuerIDs = []driver.Identity{} + pp.SetIssuers(nil) } if err := common.SetupIssuersAndAuditors(pp, args.Auditors, args.Issuers); err != nil { return err diff --git a/cmd/tokengen/main_test.go b/cmd/tokengen/main_test.go index 6ebeaf2f1..eb1ea74d5 100644 --- a/cmd/tokengen/main_test.go +++ b/cmd/tokengen/main_test.go @@ -227,7 +227,7 @@ func validateOutputEquivalent(gt *WithT, tempOutput, auditorsMSPdir, issuersMSPd gt.Expect(err).NotTo(HaveOccurred()) gt.Expect(auditors[0]).To(Equal(auditor)) - issuers := pp.IssuerIDs + issuers := pp.Issuers() issuer, err := common.GetX509Identity(issuersMSPdir) gt.Expect(err).NotTo(HaveOccurred()) gt.Expect(issuers[0]).To(BeEquivalentTo(issuer)) diff --git a/fungible.mk b/fungible.mk index cd7643ba9..d9ad3d75a 100644 --- a/fungible.mk +++ b/fungible.mk @@ -6,6 +6,10 @@ integration-tests-dlog-fabric-t1: integration-tests-dlog-fabric-t2: make integration-tests-dlog-fabric TEST_FILTER="T2" +.PHONY: integration-tests-dlog-fabric-t2.1 +integration-tests-dlog-fabric-t2.1: + make integration-tests-dlog-fabric TEST_FILTER="T2.1" + .PHONY: integration-tests-dlog-fabric-t3 integration-tests-dlog-fabric-t3: make integration-tests-dlog-fabric TEST_FILTER="T3" diff --git a/integration/token/fungible/dlog/dlog_test.go b/integration/token/fungible/dlog/dlog_test.go index 385599fb6..27c28ad8b 100644 --- a/integration/token/fungible/dlog/dlog_test.go +++ b/integration/token/fungible/dlog/dlog_test.go @@ -41,14 +41,26 @@ var _ = Describe("EndToEnd", func() { ts, selector := newTestSuite(t.CommType, Aries|WebEnabled, t.ReplicationFactor, "", "alice", "bob", "charlie") BeforeEach(ts.Setup) AfterEach(ts.TearDown) - It("Update public params", Label("T2"), func() { + It("Update public params (new auditor and issuer)", Label("T2"), func() { fungible.TestPublicParamsUpdate( ts.II, "newAuditor", - fungible.PrepareUpdatedPublicParams(ts.II, "newAuditor", "default"), + fungible.PrepareUpdatedPublicParams(ts.II, "newAuditor", "newIssuer", "default", false), "default", false, selector, + false, + ) + }) + It("Update public params (append new auditor and issuer)", Label("T2.1"), func() { + fungible.TestPublicParamsUpdate( + ts.II, + "newAuditor", + fungible.PrepareUpdatedPublicParams(ts.II, "newAuditor", "newIssuer", "default", true), + "default", + false, + selector, + true, ) }) It("Test Identity Revocation", Label("T3"), func() { fungible.TestRevokeIdentity(ts.II, "auditor", selector) }) @@ -65,10 +77,11 @@ var _ = Describe("EndToEnd", func() { fungible.TestPublicParamsUpdate( ts.II, "newIssuer", - fungible.PrepareUpdatedPublicParams(ts.II, "newIssuer", "default"), + fungible.PrepareUpdatedPublicParams(ts.II, "newIssuer", "newIssuer", "default", false), "default", true, selector, + false, ) }) }) diff --git a/integration/token/fungible/fabtoken/fabtoken_test.go b/integration/token/fungible/fabtoken/fabtoken_test.go index cae13e1e8..ef738838d 100644 --- a/integration/token/fungible/fabtoken/fabtoken_test.go +++ b/integration/token/fungible/fabtoken/fabtoken_test.go @@ -53,7 +53,7 @@ func UpdatePublicParams(network *integration.Infrastructure, selector *token2.Re ppBytes, err := publicParam.Serialize() Expect(err).NotTo(HaveOccurred()) - fungible.TestPublicParamsUpdate(network, "newAuditor", ppBytes, "default", false, selector) + fungible.TestPublicParamsUpdate(network, "newAuditor", ppBytes, "default", false, selector, false) } func newTestSuite(commType fsc.P2PCommunicationType, factor int, names ...string) (*token2.TestSuite, *token2.ReplicaSelector) { diff --git a/integration/token/fungible/support.go b/integration/token/fungible/support.go index a6fbf9081..f8e9179a7 100644 --- a/integration/token/fungible/support.go +++ b/integration/token/fungible/support.go @@ -93,23 +93,41 @@ func IssueCash(network *integration.Infrastructure, wallet string, typ token.Typ } func IssueSuccessfulCash(network *integration.Infrastructure, wallet string, typ token.Type, amount uint64, receiver *token3.NodeReference, auditor *token3.NodeReference, anonymous bool, issuer *token3.NodeReference, finalities ...*token3.NodeReference) string { - return issueCashForTMSID(network, wallet, typ, amount, receiver, auditor, anonymous, issuer, nil, finalities, []string{}) + return issueCashForTMSID(network, wallet, typ, amount, receiver, auditor, anonymous, issuer, nil, finalities, false, []string{}) } func IssueCashForTMSID(network *integration.Infrastructure, wallet string, typ token.Type, amount uint64, receiver *token3.NodeReference, auditor *token3.NodeReference, anonymous bool, issuer *token3.NodeReference, tmsId *token2.TMSID, expectedErrorMsgs ...string) string { - return issueCashForTMSID(network, wallet, typ, amount, receiver, auditor, anonymous, issuer, tmsId, []*token3.NodeReference{}, expectedErrorMsgs) -} - -func issueCashForTMSID(network *integration.Infrastructure, wallet string, typ token.Type, amount uint64, receiver *token3.NodeReference, auditor *token3.NodeReference, anonymous bool, issuer *token3.NodeReference, tmsId *token2.TMSID, endorsers []*token3.NodeReference, expectedErrorMsgs []string) string { + return issueCashForTMSID(network, wallet, typ, amount, receiver, auditor, anonymous, issuer, tmsId, []*token3.NodeReference{}, false, expectedErrorMsgs) +} + +func IssueCashWithNoAuditorSigVerification(network *integration.Infrastructure, wallet string, typ token.Type, amount uint64, receiver *token3.NodeReference, auditor *token3.NodeReference, anonymous bool, issuer *token3.NodeReference, expectedErrorMsgs ...string) string { + return issueCashForTMSID(network, wallet, typ, amount, receiver, auditor, anonymous, issuer, nil, []*token3.NodeReference{}, true, expectedErrorMsgs) +} + +func issueCashForTMSID( + network *integration.Infrastructure, + wallet string, + typ token.Type, + amount uint64, + receiver *token3.NodeReference, + auditor *token3.NodeReference, + anonymous bool, + issuer *token3.NodeReference, + tmsId *token2.TMSID, + endorsers []*token3.NodeReference, + skipAuditorSignatureVerification bool, + expectedErrorMsgs []string, +) string { txIDBoxed, err := network.Client(issuer.ReplicaName()).CallView("issue", common.JSONMarshall(&views.IssueCash{ - Anonymous: anonymous, - Auditor: auditor.Id(), - IssuerWallet: wallet, - TokenType: typ, - Quantity: amount, - Recipient: network.Identity(receiver.Id()), - RecipientEID: receiver.Id(), - TMSID: tmsId, + Anonymous: anonymous, + Auditor: auditor.Id(), + IssuerWallet: wallet, + TokenType: typ, + Quantity: amount, + Recipient: network.Identity(receiver.Id()), + RecipientEID: receiver.Id(), + TMSID: tmsId, + SkipAuditorSignatureVerification: skipAuditorSignatureVerification, })) topology.ToOptions(network.FscPlatform.Peers[0].Options).Endorser() @@ -1369,10 +1387,10 @@ func MultiSigSpendCashForTMSID(network *integration.Infrastructure, sender *toke } -func PrepareUpdatedPublicParams(network *integration.Infrastructure, auditor string, networkName string) []byte { +func PrepareUpdatedPublicParams(network *integration.Infrastructure, auditor string, issuer string, networkName string, appendIdentities bool) []byte { tms := GetTMSByNetworkName(network, networkName) auditorId := GetAuditorIdentity(tms, auditor) - issuerId := GetIssuerIdentity(tms, "newIssuer") + issuerId := GetIssuerIdentity(tms, issuer) tokenPlatform, ok := network.Ctx.PlatformsByName["token"].(*tplatform.Platform) Expect(ok).To(BeTrue(), "failed to get token platform from context") @@ -1389,6 +1407,8 @@ func PrepareUpdatedPublicParams(network *integration.Infrastructure, auditor str Serialize() ([]byte, error) SetIssuers(identities []driver.Identity) SetAuditors(identities []driver.Identity) + AddAuditor(identity2 driver.Identity) + AddIssuer(identity2 driver.Identity) } var pp PP switch genericPP.Identifier { @@ -1399,12 +1419,17 @@ func PrepareUpdatedPublicParams(network *integration.Infrastructure, auditor str pp, err = fabtokenv1.NewPublicParamsFromBytes(ppBytes, fabtokenv1.PublicParameters) Expect(err).NotTo(HaveOccurred()) default: - Expect(false).To(BeTrue(), "unknown pp identitfier [%s]", genericPP.Identifier) + Expect(false).To(BeTrue(), "unknown pp identifier [%s]", genericPP.Identifier) } Expect(pp.Validate()).NotTo(HaveOccurred()) - pp.SetAuditors([]driver.Identity{auditorId}) - pp.SetIssuers([]driver.Identity{issuerId}) + if appendIdentities { + pp.AddAuditor(auditorId) + pp.AddIssuer(issuerId) + } else { + pp.SetAuditors([]driver.Identity{auditorId}) + pp.SetIssuers([]driver.Identity{issuerId}) + } // Serialize ppBytes, err = pp.Serialize() diff --git a/integration/token/fungible/tests.go b/integration/token/fungible/tests.go index 4fc845c04..1c8f486b5 100644 --- a/integration/token/fungible/tests.go +++ b/integration/token/fungible/tests.go @@ -847,7 +847,7 @@ func TestSelector(network *integration.Infrastructure, auditorId string, sel *to TransferCash(network, alice, "", "USD", 160, bob, auditor, "insufficient funds, only [150] tokens of type [USD] are available") } -func TestPublicParamsUpdate(network *integration.Infrastructure, newAuditorID string, ppBytes []byte, networkName string, issuerAsAuditor bool, sel *token3.ReplicaSelector) { +func TestPublicParamsUpdate(network *integration.Infrastructure, newAuditorID string, ppBytes []byte, networkName string, issuerAsAuditor bool, sel *token3.ReplicaSelector, updateWithAppend bool) { newAuditor := sel.Get(newAuditorID) tms := GetTMSByNetworkName(network, networkName) newIssuer := sel.Get("newIssuer") @@ -855,7 +855,6 @@ func TestPublicParamsUpdate(network *integration.Infrastructure, newAuditorID st alice := sel.Get("alice") manager := sel.Get("manager") auditor := sel.Get("auditor") - errorMessage := "is not in issuers" if issuerAsAuditor { auditor = issuer } @@ -886,7 +885,18 @@ func TestPublicParamsUpdate(network *integration.Infrastructure, newAuditorID st Expect(txId).NotTo(BeEmpty()) CheckBalance(network, alice, "", "USD", 220) CheckHolding(network, alice, "", "USD", 110, newAuditor) - IssueCash(network, "", "USD", 110, alice, newAuditor, true, issuer, errorMessage) + if updateWithAppend { + IssueCash(network, "", "USD", 110, alice, newAuditor, true, issuer) + } else { + IssueCash(network, "", "USD", 110, alice, newAuditor, true, issuer, "is not in issuers") + } + if newAuditorID != "auditor" { + if updateWithAppend { + IssueCash(network, "", "USD", 110, alice, auditor, true, newIssuer) + } else { + IssueCashWithNoAuditorSigVerification(network, "", "USD", 110, alice, auditor, true, newIssuer, "is not in auditors") + } + } CheckOwnerWalletIDs(network, manager, "manager.id1", "manager.id2", "manager.id3") } diff --git a/integration/token/fungible/views/issue.go b/integration/token/fungible/views/issue.go index 01fe15699..3fd10ab4e 100644 --- a/integration/token/fungible/views/issue.go +++ b/integration/token/fungible/views/issue.go @@ -38,6 +38,8 @@ type IssueCash struct { RecipientWalletID string // RecipientEID is the expected enrolment id of the recipient RecipientEID string + // SkipAuditorSignatureVerification set to true to skip the verification of the auditor signature during endorsement collection + SkipAuditorSignatureVerification bool } type IssueCashView struct { @@ -116,7 +118,11 @@ func (p *IssueCashView) Call(context view.Context) (interface{}, error) { // Before completing, all recipients receive the approved transaction. // Depending on the token driver implementation, the recipient's signature might or might not be needed to make // the token transaction valid. - _, err = context.RunView(ttx.NewCollectEndorsementsView(tx)) + var eOpts []ttx.EndorsementsOpt + if p.SkipAuditorSignatureVerification { + eOpts = append(eOpts, ttx.WithSkipAuditorSignatureVerification()) + } + _, err = context.RunView(ttx.NewCollectEndorsementsView(tx, eOpts...)) assert.NoError(err, "failed to sign issue transaction for "+tx.ID()) // Sanity checks: diff --git a/token/core/common/validator.go b/token/core/common/validator.go index c4f5f2f87..2ca896d12 100644 --- a/token/core/common/validator.go +++ b/token/core/common/validator.go @@ -25,6 +25,7 @@ const ( type Context[P driver.PublicParameters, T any, TA driver.TransferAction, IA driver.IssueAction, DS driver.Deserializer] struct { Logger logging.Logger PP P + TokenRequest *driver.TokenRequest Deserializer DS SignatureProvider driver.SignatureProvider Signatures [][]byte @@ -44,6 +45,8 @@ type ValidateTransferFunc[P driver.PublicParameters, T any, TA driver.TransferAc type ValidateIssueFunc[P driver.PublicParameters, T any, TA driver.TransferAction, IA driver.IssueAction, DS driver.Deserializer] func(ctx *Context[P, T, TA, IA, DS]) error +type ValidateAuditingFunc[P driver.PublicParameters, T any, TA driver.TransferAction, IA driver.IssueAction, DS driver.Deserializer] func(ctx *Context[P, T, TA, IA, DS]) error + type ActionDeserializer[TA driver.TransferAction, IA driver.IssueAction] interface { DeserializeActions(tr *driver.TokenRequest) ([]IA, []TA, error) } @@ -53,6 +56,8 @@ type Validator[P driver.PublicParameters, T any, TA driver.TransferAction, IA dr PublicParams P Deserializer DS ActionDeserializer ActionDeserializer[TA, IA] + + AuditingValidators []ValidateAuditingFunc[P, T, TA, IA, DS] TransferValidators []ValidateTransferFunc[P, T, TA, IA, DS] IssueValidators []ValidateIssueFunc[P, T, TA, IA, DS] } @@ -64,6 +69,7 @@ func NewValidator[P driver.PublicParameters, T any, TA driver.TransferAction, IA actionDeserializer ActionDeserializer[TA, IA], transferValidators []ValidateTransferFunc[P, T, TA, IA, DS], issueValidators []ValidateIssueFunc[P, T, TA, IA, DS], + auditingValidators []ValidateAuditingFunc[P, T, TA, IA, DS], ) *Validator[P, T, TA, IA, DS] { return &Validator[P, T, TA, IA, DS]{ Logger: Logger, @@ -72,6 +78,7 @@ func NewValidator[P driver.PublicParameters, T any, TA driver.TransferAction, IA ActionDeserializer: actionDeserializer, TransferValidators: transferValidators, IssueValidators: issueValidators, + AuditingValidators: auditingValidators, } } @@ -90,13 +97,11 @@ func (v *Validator[P, T, TA, IA, DS]) VerifyTokenRequestFromRaw(ctx context.Cont if err != nil { return nil, nil, errors.Wrap(err, "failed to marshal signed token request") } - var signatures [][]byte - if len(v.PublicParams.Auditors()) != 0 { - signatures = append(signatures, tr.AuditorSignatures...) - signatures = append(signatures, tr.Signatures...) - } else { - signatures = tr.Signatures + signatures := make([][]byte, 0, len(tr.AuditorSignatures)+len(tr.Signatures)) + for _, sig := range tr.AuditorSignatures { + signatures = append(signatures, sig.Signature) } + signatures = append(signatures, tr.Signatures...) attributes := make(driver.ValidationAttributes) attributes[TokenRequestToSign] = signed @@ -110,18 +115,18 @@ func (v *Validator[P, T, TA, IA, DS]) VerifyTokenRequestFromRaw(ctx context.Cont } func (v *Validator[P, T, TA, IA, DS]) VerifyTokenRequest(ledger driver.Ledger, signatureProvider driver.SignatureProvider, anchor string, tr *driver.TokenRequest, attributes driver.ValidationAttributes) ([]interface{}, driver.ValidationAttributes, error) { - if err := v.verifyAuditorSignature(signatureProvider, attributes); err != nil { + if err := v.verifyAuditing(tr, ledger, signatureProvider, attributes); err != nil { return nil, nil, errors.Wrapf(err, "failed to verifier auditor's signature [%s]", anchor) } ia, ta, err := v.ActionDeserializer.DeserializeActions(tr) if err != nil { return nil, nil, errors.Wrapf(err, "failed to unmarshal actions [%s]", anchor) } - err = v.verifyIssues(ledger, ia, signatureProvider, attributes) + err = v.verifyIssues(tr, ledger, ia, signatureProvider, attributes) if err != nil { return nil, nil, errors.Wrapf(err, "failed to verify issue actions [%s]", anchor) } - err = v.verifyTransfers(ledger, ta, signatureProvider, attributes) + err = v.verifyTransfers(tr, ledger, ta, signatureProvider, attributes) if err != nil { return nil, nil, errors.Wrapf(err, "failed to verify transfer actions [%s]", anchor) } @@ -157,35 +162,34 @@ func (v *Validator[P, T, TA, IA, DS]) UnmarshalActions(raw []byte) ([]interface{ return res, nil } -func (v *Validator[P, T, TA, IA, DS]) verifyAuditorSignature(signatureProvider driver.SignatureProvider, attributes driver.ValidationAttributes) error { - if len(v.PublicParams.Auditors()) != 0 { - auditor := v.PublicParams.Auditors()[0] - verifier, err := v.Deserializer.GetAuditorVerifier(auditor) - if err != nil { - return errors.Errorf("failed to deserialize auditor's public key") - } - v.Logger.Infof("verify auditor signature for [%s]", auditor) - _, err = signatureProvider.HasBeenSignedBy(auditor, verifier) - return err - } - return nil -} - -func (v *Validator[P, T, TA, IA, DS]) verifyIssues(ledger driver.Ledger, issues []IA, signatureProvider driver.SignatureProvider, attributes driver.ValidationAttributes) error { +func (v *Validator[P, T, TA, IA, DS]) verifyIssues( + tokenRequest *driver.TokenRequest, + ledger driver.Ledger, + issues []IA, + signatureProvider driver.SignatureProvider, + attributes driver.ValidationAttributes, +) error { for i, issue := range issues { - if err := v.verifyIssue(issue, ledger, signatureProvider, attributes); err != nil { + if err := v.verifyIssue(tokenRequest, issue, ledger, signatureProvider, attributes); err != nil { return errors.Wrapf(err, "failed to verify issue action at [%d]", i) } } return nil } -func (v *Validator[P, T, TA, IA, DS]) verifyIssue(tr IA, ledger driver.Ledger, signatureProvider driver.SignatureProvider, attributes driver.ValidationAttributes) error { +func (v *Validator[P, T, TA, IA, DS]) verifyIssue( + tokenRequest *driver.TokenRequest, + action IA, + ledger driver.Ledger, + signatureProvider driver.SignatureProvider, + attributes driver.ValidationAttributes, +) error { context := &Context[P, T, TA, IA, DS]{ Logger: v.Logger, PP: v.PublicParams, + TokenRequest: tokenRequest, Deserializer: v.Deserializer, - IssueAction: tr, + IssueAction: action, Ledger: ledger, SignatureProvider: signatureProvider, MetadataCounter: map[string]int{}, @@ -205,30 +209,43 @@ func (v *Validator[P, T, TA, IA, DS]) verifyIssue(tr IA, ledger driver.Ledger, s } counter += c } - if len(tr.GetMetadata()) != counter { - return errors.Errorf("more metadata than those validated [%d]!=[%d], [%v]!=[%v]", len(tr.GetMetadata()), counter, tr.GetMetadata(), context.MetadataCounter) + if len(action.GetMetadata()) != counter { + return errors.Errorf("more metadata than those validated [%d]!=[%d], [%v]!=[%v]", len(action.GetMetadata()), counter, action.GetMetadata(), context.MetadataCounter) } return nil } -func (v *Validator[P, T, TA, IA, DS]) verifyTransfers(ledger driver.Ledger, transferActions []TA, signatureProvider driver.SignatureProvider, attributes driver.ValidationAttributes) error { +func (v *Validator[P, T, TA, IA, DS]) verifyTransfers( + tokenRequest *driver.TokenRequest, + ledger driver.Ledger, + transferActions []TA, + signatureProvider driver.SignatureProvider, + attributes driver.ValidationAttributes, +) error { v.Logger.Debugf("check sender start...") defer v.Logger.Debugf("check sender finished.") for i, action := range transferActions { - if err := v.verifyTransfer(action, ledger, signatureProvider, attributes); err != nil { + if err := v.verifyTransfer(tokenRequest, action, ledger, signatureProvider, attributes); err != nil { return errors.Wrapf(err, "failed to verify transfer action at [%d]", i) } } return nil } -func (v *Validator[P, T, TA, IA, DS]) verifyTransfer(tr TA, ledger driver.Ledger, signatureProvider driver.SignatureProvider, attributes driver.ValidationAttributes) error { +func (v *Validator[P, T, TA, IA, DS]) verifyTransfer( + tokenRequest *driver.TokenRequest, + action TA, + ledger driver.Ledger, + signatureProvider driver.SignatureProvider, + attributes driver.ValidationAttributes, +) error { context := &Context[P, T, TA, IA, DS]{ Logger: v.Logger, PP: v.PublicParams, + TokenRequest: tokenRequest, Deserializer: v.Deserializer, - TransferAction: tr, + TransferAction: action, Ledger: ledger, SignatureProvider: signatureProvider, MetadataCounter: map[MetadataCounterID]int{}, @@ -248,13 +265,36 @@ func (v *Validator[P, T, TA, IA, DS]) verifyTransfer(tr TA, ledger driver.Ledger } counter += c } - if len(tr.GetMetadata()) != counter { - return errors.Errorf("more metadata than those validated [%d]!=[%d], [%v]!=[%v]", len(tr.GetMetadata()), counter, tr.GetMetadata(), context.MetadataCounter) + if len(action.GetMetadata()) != counter { + return errors.Errorf("more metadata than those validated [%d]!=[%d], [%v]!=[%v]", len(action.GetMetadata()), counter, action.GetMetadata(), context.MetadataCounter) } return nil } +func (v *Validator[P, T, TA, IA, DS]) verifyAuditing( + tokenRequest *driver.TokenRequest, + ledger driver.Ledger, + signatureProvider driver.SignatureProvider, + attributes driver.ValidationAttributes, +) error { + context := &Context[P, T, TA, IA, DS]{ + Logger: v.Logger, + PP: v.PublicParams, + TokenRequest: tokenRequest, + Deserializer: v.Deserializer, + Ledger: ledger, + SignatureProvider: signatureProvider, + Attributes: attributes, + } + for _, v := range v.AuditingValidators { + if err := v(context); err != nil { + return err + } + } + return nil +} + func IsAnyNil[T any](args ...*T) bool { for _, arg := range args { if arg == nil { diff --git a/token/core/common/validator_auditing.go b/token/core/common/validator_auditing.go new file mode 100644 index 000000000..b4da8689b --- /dev/null +++ b/token/core/common/validator_auditing.go @@ -0,0 +1,43 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package common + +import ( + "slices" + + "github.com/hyperledger-labs/fabric-token-sdk/token/driver" + "github.com/pkg/errors" +) + +func AuditingSignaturesValidate[P driver.PublicParameters, T any, TA driver.TransferAction, IA driver.IssueAction, DS driver.Deserializer](ctx *Context[P, T, TA, IA, DS]) error { + if len(ctx.PP.Auditors()) == 0 { + // enforce no auditor signatures are attached + if len(ctx.TokenRequest.AuditorSignatures) != 0 { + return errors.New("auditor signatures are not empty") + } + return nil + } + + auditors := ctx.PP.Auditors() + for _, auditorSignature := range ctx.TokenRequest.AuditorSignatures { + auditor := auditorSignature.Identity + // check that issuer of this issue action is authorized + if !slices.ContainsFunc(auditors, auditorSignature.Identity.Equal) { + return errors.Errorf("auditor [%s] is not in auditors", auditor) + } + + verifier, err := ctx.Deserializer.GetAuditorVerifier(auditor) + if err != nil { + return errors.Wrapf(err, "failed to deserialize auditor's public key") + } + _, err = ctx.SignatureProvider.HasBeenSignedBy(auditor, verifier) + if err != nil { + return errors.Wrap(err, "failed to verify auditor's signature") + } + } + return nil +} diff --git a/token/core/common/validator_auditing_test.go b/token/core/common/validator_auditing_test.go new file mode 100644 index 000000000..aaa344d27 --- /dev/null +++ b/token/core/common/validator_auditing_test.go @@ -0,0 +1,193 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package common + +import ( + "testing" + + "github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors" + "github.com/hyperledger-labs/fabric-smart-client/platform/common/services/identity" + "github.com/hyperledger-labs/fabric-token-sdk/token/driver" + "github.com/hyperledger-labs/fabric-token-sdk/token/driver/mock" + "github.com/stretchr/testify/assert" +) + +type ( + TestContext = Context[driver.PublicParameters, any, driver.TransferAction, driver.IssueAction, driver.Deserializer] + TestCheck = func() bool +) + +func TestAuditingSignaturesValidate(t *testing.T) { + tests := []struct { + name string + err bool + errMsg string + context func() (*TestContext, TestCheck) + }{ + { + name: "No auditors but token requests with auditor signatures", + err: true, + errMsg: "auditor signatures are not empty", + context: func() (*TestContext, TestCheck) { + pp := &mock.PublicParameters{} + pp.AuditorsReturns(nil) + return &TestContext{ + PP: pp, + TokenRequest: &driver.TokenRequest{ + AuditorSignatures: []*driver.AuditorSignature{ + { + Identity: driver.Identity("auditor"), + Signature: []byte("auditor's signature"), + }, + }, + }, + }, nil + }, + }, + { + name: "No auditors and no token requests with auditor signatures", + err: false, + context: func() (*TestContext, TestCheck) { + pp := &mock.PublicParameters{} + pp.AuditorsReturns(nil) + return &TestContext{ + PP: pp, + TokenRequest: &driver.TokenRequest{}, + }, nil + }, + }, + { + name: "it is not an auditor", + err: true, + errMsg: "auditor [LERVQYVKJM22xRRnp0G1rmcuYpOTY4x0mWJ5V21ZQ5I=] is not in auditors", + context: func() (*TestContext, TestCheck) { + pp := &mock.PublicParameters{} + pp.AuditorsReturns([]identity.Identity{driver.Identity("auditor1")}) + return &TestContext{ + PP: pp, + TokenRequest: &driver.TokenRequest{ + AuditorSignatures: []*driver.AuditorSignature{ + { + Identity: driver.Identity("auditor2"), + Signature: []byte("auditor 2's signature"), + }, + }, + }, + }, nil + }, + }, + { + name: "it is an auditor but I cannot deserialize it", + err: true, + errMsg: "failed to deserialize auditor's public key: auditor deserialize fail", + context: func() (*TestContext, TestCheck) { + pp := &mock.PublicParameters{} + pp.AuditorsReturns([]identity.Identity{driver.Identity("auditor")}) + + des := &mock.Deserializer{} + des.GetAuditorVerifierReturns(nil, errors.Errorf("auditor deserialize fail")) + return &TestContext{ + PP: pp, + TokenRequest: &driver.TokenRequest{ + AuditorSignatures: []*driver.AuditorSignature{ + { + Identity: driver.Identity("auditor"), + Signature: []byte("auditor's signature"), + }, + }, + }, + Deserializer: des, + }, nil + }, + }, + { + name: "it is an auditor but I cannot verify its signature", + err: true, + errMsg: "failed to verify auditor's signature: signature is not valid", + context: func() (*TestContext, TestCheck) { + auditor := driver.Identity("auditor") + pp := &mock.PublicParameters{} + pp.AuditorsReturns([]identity.Identity{auditor}) + ver := &mock.Verifier{} + ver.VerifyReturns(errors.New("signature is not valid")) + des := &mock.Deserializer{} + des.GetAuditorVerifierReturns(ver, nil) + sp := &mock.SignatureProvider{} + sp.HasBeenSignedByReturns(nil, errors.New("signature is not valid")) + return &TestContext{ + PP: pp, + TokenRequest: &driver.TokenRequest{ + AuditorSignatures: []*driver.AuditorSignature{ + { + Identity: auditor, + Signature: []byte("auditor's signature"), + }, + }, + }, + Deserializer: des, + SignatureProvider: sp, + }, func() bool { + id, ver2 := sp.HasBeenSignedByArgsForCall(0) + if ver2 != ver { + return false + } + return auditor.Equal(id) + } + }, + }, + { + name: "it is an auditor and the signature is valid", + err: false, + context: func() (*TestContext, TestCheck) { + auditor := driver.Identity("auditor") + pp := &mock.PublicParameters{} + pp.AuditorsReturns([]identity.Identity{auditor}) + ver := &mock.Verifier{} + ver.VerifyReturns(errors.New("signature is not valid")) + des := &mock.Deserializer{} + des.GetAuditorVerifierReturns(ver, nil) + sp := &mock.SignatureProvider{} + sp.HasBeenSignedByReturns(nil, nil) + return &TestContext{ + PP: pp, + TokenRequest: &driver.TokenRequest{ + AuditorSignatures: []*driver.AuditorSignature{ + { + Identity: auditor, + Signature: []byte("auditor's signature"), + }, + }, + }, + Deserializer: des, + SignatureProvider: sp, + }, func() bool { + id, ver2 := sp.HasBeenSignedByArgsForCall(0) + if ver2 != ver { + return false + } + return auditor.Equal(id) + } + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx, check := tt.context() + err := AuditingSignaturesValidate(ctx) + if tt.err { + assert.Error(t, err) + assert.EqualError(t, err, tt.errMsg) + } else { + assert.NoError(t, err) + } + if check != nil { + assert.True(t, check()) + } + }) + } +} diff --git a/token/core/fabtoken/v1/validator/validator.go b/token/core/fabtoken/v1/validator/validator.go index b77f519e7..685b294f7 100644 --- a/token/core/fabtoken/v1/validator/validator.go +++ b/token/core/fabtoken/v1/validator/validator.go @@ -18,6 +18,8 @@ type ValidateTransferFunc = common.ValidateTransferFunc[*setup.PublicParams, *ac type ValidateIssueFunc = common.ValidateIssueFunc[*setup.PublicParams, *actions.Output, *actions.TransferAction, *actions.IssueAction, driver.Deserializer] +type ValidateAuditingFunc = common.ValidateAuditingFunc[*setup.PublicParams, *actions.Output, *actions.TransferAction, *actions.IssueAction, driver.Deserializer] + type ActionDeserializer struct{} func (a *ActionDeserializer) DeserializeActions(tr *driver.TokenRequest) ([]*actions.IssueAction, []*actions.TransferAction, error) { @@ -59,6 +61,10 @@ func NewValidator(logger logging.Logger, pp *setup.PublicParams, deserializer dr IssueValidate, } + auditingValidators := []ValidateAuditingFunc{ + common.AuditingSignaturesValidate[*setup.PublicParams, *actions.Output, *actions.TransferAction, *actions.IssueAction, driver.Deserializer], + } + return common.NewValidator[*setup.PublicParams, *actions.Output, *actions.TransferAction, *actions.IssueAction, driver.Deserializer]( logger, pp, @@ -66,5 +72,6 @@ func NewValidator(logger logging.Logger, pp *setup.PublicParams, deserializer dr &ActionDeserializer{}, transferValidators, issueValidators, + auditingValidators, ) } diff --git a/token/core/zkatdlog/nogh/protos-go/pp/noghpp.pb.go b/token/core/zkatdlog/nogh/protos-go/pp/noghpp.pb.go index 96e1d0dc9..1aa45afa3 100644 --- a/token/core/zkatdlog/nogh/protos-go/pp/noghpp.pb.go +++ b/token/core/zkatdlog/nogh/protos-go/pp/noghpp.pb.go @@ -228,7 +228,7 @@ type PublicParameters struct { PedersenGenerators []*math.G1 `protobuf:"bytes,4,rep,name=pedersen_generators,json=pedersenGenerators,proto3" json:"pedersen_generators,omitempty"` // contains the public parameters for the Pedersen commitment scheme. RangeProofParams *RangeProofParams `protobuf:"bytes,5,opt,name=range_proof_params,json=rangeProofParams,proto3" json:"range_proof_params,omitempty"` // contains the public parameters for the range proof scheme. IdemixIssuerPublicKeys []*IdemixIssuerPublicKey `protobuf:"bytes,6,rep,name=idemix_issuer_public_keys,json=idemixIssuerPublicKeys,proto3" json:"idemix_issuer_public_keys,omitempty"` // contains the idemix issuer public keys. Wallets should prefer the use of keys valid under the public key whose index in the array is the smallest. - Auditor *Identity `protobuf:"bytes,7,opt,name=auditor,proto3" json:"auditor,omitempty"` // is the public key of the auditor. + Auditors []*Identity `protobuf:"bytes,7,rep,name=auditors,proto3" json:"auditors,omitempty"` // is the public key of the auditor. Issuers []*Identity `protobuf:"bytes,8,rep,name=issuers,proto3" json:"issuers,omitempty"` // is a list of public keys of the entities that can issue tokens. MaxToken uint64 `protobuf:"varint,9,opt,name=max_token,json=maxToken,proto3" json:"max_token,omitempty"` // is the maximum quantity a token can hold QuantityPrecision uint64 `protobuf:"varint,10,opt,name=quantity_precision,json=quantityPrecision,proto3" json:"quantity_precision,omitempty"` // is the precision used to represent quantities @@ -308,9 +308,9 @@ func (x *PublicParameters) GetIdemixIssuerPublicKeys() []*IdemixIssuerPublicKey return nil } -func (x *PublicParameters) GetAuditor() *Identity { +func (x *PublicParameters) GetAuditors() []*Identity { if x != nil { - return x.Auditor + return x.Auditors } return nil } @@ -364,7 +364,7 @@ var file_noghpp_proto_rawDesc = []byte{ 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, 0x69, 0x74, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0xef, 0x03, 0x0a, + 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0xf1, 0x03, 0x0a, 0x10, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, @@ -385,23 +385,23 @@ var file_noghpp_proto_rawDesc = []byte{ 0x65, 0x79, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x6f, 0x67, 0x68, 0x2e, 0x49, 0x64, 0x65, 0x6d, 0x69, 0x78, 0x49, 0x73, 0x73, 0x75, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x16, 0x69, 0x64, 0x65, 0x6d, 0x69, 0x78, 0x49, 0x73, - 0x73, 0x75, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x28, - 0x0a, 0x07, 0x61, 0x75, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0e, 0x2e, 0x6e, 0x6f, 0x67, 0x68, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, - 0x07, 0x61, 0x75, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x07, 0x69, 0x73, 0x73, 0x75, - 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x6f, 0x67, 0x68, - 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x07, 0x69, 0x73, 0x73, 0x75, 0x65, - 0x72, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, - 0x2d, 0x0a, 0x12, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x63, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x71, 0x75, 0x61, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x54, - 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x79, 0x70, - 0x65, 0x72, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x66, 0x61, - 0x62, 0x72, 0x69, 0x63, 0x2d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x7a, 0x6b, 0x61, 0x74, 0x64, 0x6c, - 0x6f, 0x67, 0x2f, 0x6e, 0x6f, 0x67, 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2d, 0x67, - 0x6f, 0x2f, 0x70, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x75, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x2a, + 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x6e, 0x6f, 0x67, 0x68, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x69, 0x73, + 0x73, 0x75, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x6f, + 0x67, 0x68, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x07, 0x69, 0x73, 0x73, + 0x75, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x72, + 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x71, + 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x42, 0x54, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, + 0x79, 0x70, 0x65, 0x72, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, + 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2d, 0x73, 0x64, 0x6b, + 0x2f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x7a, 0x6b, 0x61, 0x74, + 0x64, 0x6c, 0x6f, 0x67, 0x2f, 0x6e, 0x6f, 0x67, 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, + 0x2d, 0x67, 0x6f, 0x2f, 0x70, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -435,7 +435,7 @@ var file_noghpp_proto_depIdxs = []int32{ 5, // 6: nogh.PublicParameters.pedersen_generators:type_name -> nogh.G1 2, // 7: nogh.PublicParameters.range_proof_params:type_name -> nogh.RangeProofParams 1, // 8: nogh.PublicParameters.idemix_issuer_public_keys:type_name -> nogh.IdemixIssuerPublicKey - 0, // 9: nogh.PublicParameters.auditor:type_name -> nogh.Identity + 0, // 9: nogh.PublicParameters.auditors:type_name -> nogh.Identity 0, // 10: nogh.PublicParameters.issuers:type_name -> nogh.Identity 11, // [11:11] is the sub-list for method output_type 11, // [11:11] is the sub-list for method input_type diff --git a/token/core/zkatdlog/nogh/protos/noghpp.proto b/token/core/zkatdlog/nogh/protos/noghpp.proto index cc23a1ebe..461388d9b 100644 --- a/token/core/zkatdlog/nogh/protos/noghpp.proto +++ b/token/core/zkatdlog/nogh/protos/noghpp.proto @@ -38,7 +38,7 @@ message PublicParameters { repeated G1 pedersen_generators = 4; // contains the public parameters for the Pedersen commitment scheme. RangeProofParams range_proof_params = 5; // contains the public parameters for the range proof scheme. repeated IdemixIssuerPublicKey idemix_issuer_public_keys = 6; // contains the idemix issuer public keys. Wallets should prefer the use of keys valid under the public key whose index in the array is the smallest. - Identity auditor = 7; // is the public key of the auditor. + repeated Identity auditors = 7; // is the public key of the auditor. repeated Identity issuers = 8; // is a list of public keys of the entities that can issue tokens. uint64 max_token = 9; // is the maximum quantity a token can hold uint64 quantity_precision = 10; // is the precision used to represent quantities diff --git a/token/core/zkatdlog/nogh/v1/setup/setup.go b/token/core/zkatdlog/nogh/v1/setup/setup.go index f6c85622e..b176c1d4f 100644 --- a/token/core/zkatdlog/nogh/v1/setup/setup.go +++ b/token/core/zkatdlog/nogh/v1/setup/setup.go @@ -170,8 +170,8 @@ type PublicParams struct { // IdemixIssuerPublicKeys contains the idemix issuer public keys // Wallets should prefer the use of keys valid under the public key whose index in the array is the smallest. IdemixIssuerPublicKeys []*IdemixIssuerPublicKey - // Auditor is the public key of the auditor. - Auditor driver.Identity + // Auditors is a list of the public keys of the auditor(s). + AuditorIDs []driver.Identity // IssuerIDs is a list of public keys of the entities that can issue tokens. IssuerIDs []driver.Identity // MaxToken is the maximum quantity a token can hold @@ -253,10 +253,7 @@ func (p *PublicParams) Bytes() ([]byte, error) { } func (p *PublicParams) Auditors() []driver.Identity { - if len(p.Auditor) == 0 { - return []driver.Identity{} - } - return []driver.Identity{p.Auditor} + return p.AuditorIDs } // Issuers returns the list of authorized issuers @@ -285,6 +282,14 @@ func (p *PublicParams) Serialize() ([]byte, error) { if err != nil { return nil, errors.Wrapf(err, "failed to serialize issuer") } + auditors, err := protos.ToProtosSliceFunc(p.AuditorIDs, func(id driver.Identity) (*pp.Identity, error) { + return &pp.Identity{ + Raw: id, + }, nil + }) + if err != nil { + return nil, errors.Wrapf(err, "failed to serialize auditor") + } idemixIssuerPublicKeys, err := protos.ToProtosSlice[pp.IdemixIssuerPublicKey, *IdemixIssuerPublicKey](p.IdemixIssuerPublicKeys) if err != nil { return nil, errors.Wrapf(err, "failed to serialize idemix issuer public keys") @@ -299,12 +304,10 @@ func (p *PublicParams) Serialize() ([]byte, error) { PedersenGenerators: pg, RangeProofParams: rpp, IdemixIssuerPublicKeys: idemixIssuerPublicKeys, - Auditor: &pp.Identity{ - Raw: p.Auditor, - }, - Issuers: issuers, - MaxToken: p.MaxToken, - QuantityPrecision: p.QuantityPrecision, + Auditors: auditors, + Issuers: issuers, + MaxToken: p.MaxToken, + QuantityPrecision: p.QuantityPrecision, } raw, err := proto.Marshal(publicParams) if err != nil { @@ -353,15 +356,22 @@ func (p *PublicParams) Deserialize(raw []byte) error { return errors.Wrapf(err, "failed to deserialize issuers") } p.IssuerIDs = issuers + auditors, err := protos.FromProtosSliceFunc2(publicParams.Auditors, func(id *pp.Identity) (driver.Identity, error) { + if id == nil { + return nil, nil + } + return id.Raw, nil + }) + if err != nil { + return errors.Wrapf(err, "failed to deserialize issuers") + } + p.AuditorIDs = auditors p.IdemixIssuerPublicKeys = slices.GenericSliceOfPointers[IdemixIssuerPublicKey](len(publicParams.IdemixIssuerPublicKeys)) err = protos.FromProtosSlice[pp.IdemixIssuerPublicKey, *IdemixIssuerPublicKey](publicParams.IdemixIssuerPublicKeys, p.IdemixIssuerPublicKeys) if err != nil { return errors.Wrapf(err, "failed to deserialize idemix issuer public keys") } - if publicParams.Auditor != nil { - p.Auditor = publicParams.Auditor.Raw - } p.RangeProofParams = &RangeProofParams{} if err := p.RangeProofParams.FromProto(publicParams.RangeProofParams); err != nil { @@ -406,7 +416,7 @@ func (p *PublicParams) GenerateRangeProofParameters(bitLength uint64) error { } func (p *PublicParams) AddAuditor(auditor driver.Identity) { - p.Auditor = auditor + p.AuditorIDs = append(p.AuditorIDs, auditor) } func (p *PublicParams) AddIssuer(id driver.Identity) { @@ -420,7 +430,7 @@ func (p *PublicParams) SetIssuers(ids []driver.Identity) { // SetAuditors sets the auditors to the passed identities func (p *PublicParams) SetAuditors(ids []driver.Identity) { - p.Auditor = ids[0] + p.AuditorIDs = ids } func (p *PublicParams) ComputeHash() ([]byte, error) { diff --git a/token/core/zkatdlog/nogh/v1/validator/validator.go b/token/core/zkatdlog/nogh/v1/validator/validator.go index dddb4ba6c..237aa8d62 100644 --- a/token/core/zkatdlog/nogh/v1/validator/validator.go +++ b/token/core/zkatdlog/nogh/v1/validator/validator.go @@ -20,6 +20,8 @@ type ValidateTransferFunc = common.ValidateTransferFunc[*v1.PublicParams, *token type ValidateIssueFunc = common.ValidateIssueFunc[*v1.PublicParams, *token.Token, *transfer.Action, *issue.Action, driver.Deserializer] +type ValidateAuditingFunc = common.ValidateAuditingFunc[*v1.PublicParams, *token.Token, *transfer.Action, *issue.Action, driver.Deserializer] + type Context = common.Context[*v1.PublicParams, *token.Token, *transfer.Action, *issue.Action, driver.Deserializer] type ActionDeserializer struct { @@ -69,6 +71,10 @@ func New( IssueValidate, } + auditingValidators := []ValidateAuditingFunc{ + common.AuditingSignaturesValidate[*v1.PublicParams, *token.Token, *transfer.Action, *issue.Action, driver.Deserializer], + } + return common.NewValidator[*v1.PublicParams, *token.Token, *transfer.Action, *issue.Action, driver.Deserializer]( logger, pp, @@ -76,5 +82,6 @@ func New( &ActionDeserializer{}, transferValidators, issueValidators, + auditingValidators, ) } diff --git a/token/core/zkatdlog/nogh/v1/validator/validator_issue.go b/token/core/zkatdlog/nogh/v1/validator/validator_issue.go index f03c1ba94..9dd6da11a 100644 --- a/token/core/zkatdlog/nogh/v1/validator/validator_issue.go +++ b/token/core/zkatdlog/nogh/v1/validator/validator_issue.go @@ -31,7 +31,7 @@ func IssueValidate(ctx *Context) error { return err } - issuers := ctx.PP.IssuerIDs + issuers := ctx.PP.Issuers() if len(issuers) != 0 { // Check the issuer is among those known found := false diff --git a/token/core/zkatdlog/nogh/v1/validator/validator_test.go b/token/core/zkatdlog/nogh/v1/validator/validator_test.go index 169417d66..4dc058c21 100644 --- a/token/core/zkatdlog/nogh/v1/validator/validator_test.go +++ b/token/core/zkatdlog/nogh/v1/validator/validator_test.go @@ -92,7 +92,7 @@ var _ = Describe("validator", func() { auditor = audit.NewAuditor(logging.MustGetLogger("auditor"), &noop.Tracer{}, des, pp.PedersenGenerators, asigner, c) araw, err := asigner.Serialize() Expect(err).NotTo(HaveOccurred()) - pp.Auditor = araw + pp.SetAuditors([]driver.Identity{araw}) // initialize enginw with pp deserializer, err := zkatdlog.NewDeserializer(pp) @@ -134,7 +134,10 @@ var _ = Describe("validator", func() { Expect(err).NotTo(HaveOccurred()) sigma, err := auditor.Endorse(ar, "2") Expect(err).NotTo(HaveOccurred()) - ar.AuditorSignatures = append(ar.AuditorSignatures, sigma) + ar.AuditorSignatures = append(ar.AuditorSignatures, &driver.AuditorSignature{ + Identity: araw, + Signature: sigma, + }) ar.Signatures = append(ar.Signatures, signatures...) }) @@ -474,7 +477,12 @@ func prepareIssue(auditor *audit.Auditor, issuer *issue2.Issuer, issuerIdentity Expect(err).NotTo(HaveOccurred()) sigma, err := auditor.Endorse(ir, "1") Expect(err).NotTo(HaveOccurred()) - ir.AuditorSignatures = append(ir.AuditorSignatures, sigma) + araw, err := auditor.Signer.Serialize() + Expect(err).NotTo(HaveOccurred()) + ir.AuditorSignatures = append(ir.AuditorSignatures, &driver.AuditorSignature{ + Identity: araw, + Signature: sigma, + }) return ir, issueMetadata } @@ -571,7 +579,12 @@ func prepareTransfer(pp *v1.PublicParams, signer driver.SigningIdentity, auditor sigma, err := auditor.Endorse(tr, "1") Expect(err).NotTo(HaveOccurred()) - tr.AuditorSignatures = append(tr.AuditorSignatures, sigma) + araw, err := auditor.Signer.Serialize() + Expect(err).NotTo(HaveOccurred()) + tr.AuditorSignatures = append(tr.AuditorSignatures, &driver.AuditorSignature{ + Identity: araw, + Signature: sigma, + }) signatures, err := sender.SignTokenActions(raw) Expect(err).NotTo(HaveOccurred()) diff --git a/token/driver/mock/signature_provider.go b/token/driver/mock/signature_provider.go new file mode 100644 index 000000000..40a7cf183 --- /dev/null +++ b/token/driver/mock/signature_provider.go @@ -0,0 +1,184 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package mock + +import ( + "sync" + + "github.com/hyperledger-labs/fabric-smart-client/platform/common/services/identity" + "github.com/hyperledger-labs/fabric-token-sdk/token/driver" +) + +type SignatureProvider struct { + HasBeenSignedByStub func(identity.Identity, driver.Verifier) ([]byte, error) + hasBeenSignedByMutex sync.RWMutex + hasBeenSignedByArgsForCall []struct { + arg1 identity.Identity + arg2 driver.Verifier + } + hasBeenSignedByReturns struct { + result1 []byte + result2 error + } + hasBeenSignedByReturnsOnCall map[int]struct { + result1 []byte + result2 error + } + SignaturesStub func() [][]byte + signaturesMutex sync.RWMutex + signaturesArgsForCall []struct { + } + signaturesReturns struct { + result1 [][]byte + } + signaturesReturnsOnCall map[int]struct { + result1 [][]byte + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *SignatureProvider) HasBeenSignedBy(arg1 identity.Identity, arg2 driver.Verifier) ([]byte, error) { + fake.hasBeenSignedByMutex.Lock() + ret, specificReturn := fake.hasBeenSignedByReturnsOnCall[len(fake.hasBeenSignedByArgsForCall)] + fake.hasBeenSignedByArgsForCall = append(fake.hasBeenSignedByArgsForCall, struct { + arg1 identity.Identity + arg2 driver.Verifier + }{arg1, arg2}) + stub := fake.HasBeenSignedByStub + fakeReturns := fake.hasBeenSignedByReturns + fake.recordInvocation("HasBeenSignedBy", []interface{}{arg1, arg2}) + fake.hasBeenSignedByMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *SignatureProvider) HasBeenSignedByCallCount() int { + fake.hasBeenSignedByMutex.RLock() + defer fake.hasBeenSignedByMutex.RUnlock() + return len(fake.hasBeenSignedByArgsForCall) +} + +func (fake *SignatureProvider) HasBeenSignedByCalls(stub func(identity.Identity, driver.Verifier) ([]byte, error)) { + fake.hasBeenSignedByMutex.Lock() + defer fake.hasBeenSignedByMutex.Unlock() + fake.HasBeenSignedByStub = stub +} + +func (fake *SignatureProvider) HasBeenSignedByArgsForCall(i int) (identity.Identity, driver.Verifier) { + fake.hasBeenSignedByMutex.RLock() + defer fake.hasBeenSignedByMutex.RUnlock() + argsForCall := fake.hasBeenSignedByArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *SignatureProvider) HasBeenSignedByReturns(result1 []byte, result2 error) { + fake.hasBeenSignedByMutex.Lock() + defer fake.hasBeenSignedByMutex.Unlock() + fake.HasBeenSignedByStub = nil + fake.hasBeenSignedByReturns = struct { + result1 []byte + result2 error + }{result1, result2} +} + +func (fake *SignatureProvider) HasBeenSignedByReturnsOnCall(i int, result1 []byte, result2 error) { + fake.hasBeenSignedByMutex.Lock() + defer fake.hasBeenSignedByMutex.Unlock() + fake.HasBeenSignedByStub = nil + if fake.hasBeenSignedByReturnsOnCall == nil { + fake.hasBeenSignedByReturnsOnCall = make(map[int]struct { + result1 []byte + result2 error + }) + } + fake.hasBeenSignedByReturnsOnCall[i] = struct { + result1 []byte + result2 error + }{result1, result2} +} + +func (fake *SignatureProvider) Signatures() [][]byte { + fake.signaturesMutex.Lock() + ret, specificReturn := fake.signaturesReturnsOnCall[len(fake.signaturesArgsForCall)] + fake.signaturesArgsForCall = append(fake.signaturesArgsForCall, struct { + }{}) + stub := fake.SignaturesStub + fakeReturns := fake.signaturesReturns + fake.recordInvocation("Signatures", []interface{}{}) + fake.signaturesMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *SignatureProvider) SignaturesCallCount() int { + fake.signaturesMutex.RLock() + defer fake.signaturesMutex.RUnlock() + return len(fake.signaturesArgsForCall) +} + +func (fake *SignatureProvider) SignaturesCalls(stub func() [][]byte) { + fake.signaturesMutex.Lock() + defer fake.signaturesMutex.Unlock() + fake.SignaturesStub = stub +} + +func (fake *SignatureProvider) SignaturesReturns(result1 [][]byte) { + fake.signaturesMutex.Lock() + defer fake.signaturesMutex.Unlock() + fake.SignaturesStub = nil + fake.signaturesReturns = struct { + result1 [][]byte + }{result1} +} + +func (fake *SignatureProvider) SignaturesReturnsOnCall(i int, result1 [][]byte) { + fake.signaturesMutex.Lock() + defer fake.signaturesMutex.Unlock() + fake.SignaturesStub = nil + if fake.signaturesReturnsOnCall == nil { + fake.signaturesReturnsOnCall = make(map[int]struct { + result1 [][]byte + }) + } + fake.signaturesReturnsOnCall[i] = struct { + result1 [][]byte + }{result1} +} + +func (fake *SignatureProvider) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.hasBeenSignedByMutex.RLock() + defer fake.hasBeenSignedByMutex.RUnlock() + fake.signaturesMutex.RLock() + defer fake.signaturesMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *SignatureProvider) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ driver.SignatureProvider = new(SignatureProvider) diff --git a/token/driver/protos-go/request/request.pb.go b/token/driver/protos-go/request/request.pb.go index 72e2042c3..0c9738046 100644 --- a/token/driver/protos-go/request/request.pb.go +++ b/token/driver/protos-go/request/request.pb.go @@ -160,7 +160,7 @@ func (x *AuditableIdentity) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Identity.ProtoReflect.Descriptor instead. +// Deprecated: Use AuditableIdentity.ProtoReflect.Descriptor instead. func (*AuditableIdentity) Descriptor() ([]byte, []int) { return file_request_proto_rawDescGZIP(), []int{1} } @@ -786,22 +786,126 @@ func (x *Signature) GetRaw() []byte { return nil } +// Represent a signature of an auditor +type AuditorSignature struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Identity *Identity `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` // The identity of the auditor that signed + Signature *Signature `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` // Its signatures +} + +func (x *AuditorSignature) Reset() { + *x = AuditorSignature{} + if protoimpl.UnsafeEnabled { + mi := &file_request_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AuditorSignature) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AuditorSignature) ProtoMessage() {} + +func (x *AuditorSignature) ProtoReflect() protoreflect.Message { + mi := &file_request_proto_msgTypes[12] + 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 AuditorSignature.ProtoReflect.Descriptor instead. +func (*AuditorSignature) Descriptor() ([]byte, []int) { + return file_request_proto_rawDescGZIP(), []int{12} +} + +func (x *AuditorSignature) GetIdentity() *Identity { + if x != nil { + return x.Identity + } + return nil +} + +func (x *AuditorSignature) GetSignature() *Signature { + if x != nil { + return x.Signature + } + return nil +} + +// Auditing is the section dedicated to the auditing +type Auditing struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Signatures []*AuditorSignature `protobuf:"bytes,1,rep,name=signatures,proto3" json:"signatures,omitempty"` // Signatures of the auditors +} + +func (x *Auditing) Reset() { + *x = Auditing{} + if protoimpl.UnsafeEnabled { + mi := &file_request_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Auditing) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Auditing) ProtoMessage() {} + +func (x *Auditing) ProtoReflect() protoreflect.Message { + mi := &file_request_proto_msgTypes[13] + 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 Auditing.ProtoReflect.Descriptor instead. +func (*Auditing) Descriptor() ([]byte, []int) { + return file_request_proto_rawDescGZIP(), []int{13} +} + +func (x *Auditing) GetSignatures() []*AuditorSignature { + if x != nil { + return x.Signatures + } + return nil +} + // Token request containing multiple actions and their signatures type TokenRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` // Version number - Actions []*Action `protobuf:"bytes,2,rep,name=actions,proto3" json:"actions,omitempty"` // List of token actions to perform - Signatures []*Signature `protobuf:"bytes,3,rep,name=signatures,proto3" json:"signatures,omitempty"` // Signatures for the actions - AuditorSignatures []*Signature `protobuf:"bytes,4,rep,name=auditor_signatures,json=auditorSignatures,proto3" json:"auditor_signatures,omitempty"` // Additional signatures from auditors + Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` // Version number + Actions []*Action `protobuf:"bytes,2,rep,name=actions,proto3" json:"actions,omitempty"` // List of token actions to perform + Signatures []*Signature `protobuf:"bytes,3,rep,name=signatures,proto3" json:"signatures,omitempty"` // Signatures for the actions + Auditing *Auditing `protobuf:"bytes,4,opt,name=auditing,proto3" json:"auditing,omitempty"` // Section dedicated to the auditing } func (x *TokenRequest) Reset() { *x = TokenRequest{} if protoimpl.UnsafeEnabled { - mi := &file_request_proto_msgTypes[12] + mi := &file_request_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -814,7 +918,7 @@ func (x *TokenRequest) String() string { func (*TokenRequest) ProtoMessage() {} func (x *TokenRequest) ProtoReflect() protoreflect.Message { - mi := &file_request_proto_msgTypes[12] + mi := &file_request_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -827,7 +931,7 @@ func (x *TokenRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TokenRequest.ProtoReflect.Descriptor instead. func (*TokenRequest) Descriptor() ([]byte, []int) { - return file_request_proto_rawDescGZIP(), []int{12} + return file_request_proto_rawDescGZIP(), []int{14} } func (x *TokenRequest) GetVersion() uint32 { @@ -851,9 +955,9 @@ func (x *TokenRequest) GetSignatures() []*Signature { return nil } -func (x *TokenRequest) GetAuditorSignatures() []*Signature { +func (x *TokenRequest) GetAuditing() *Auditing { if x != nil { - return x.AuditorSignatures + return x.Auditing } return nil } @@ -872,7 +976,7 @@ type TokenRequestWithMetadata struct { func (x *TokenRequestWithMetadata) Reset() { *x = TokenRequestWithMetadata{} if protoimpl.UnsafeEnabled { - mi := &file_request_proto_msgTypes[13] + mi := &file_request_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -885,7 +989,7 @@ func (x *TokenRequestWithMetadata) String() string { func (*TokenRequestWithMetadata) ProtoMessage() {} func (x *TokenRequestWithMetadata) ProtoReflect() protoreflect.Message { - mi := &file_request_proto_msgTypes[13] + mi := &file_request_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -898,7 +1002,7 @@ func (x *TokenRequestWithMetadata) ProtoReflect() protoreflect.Message { // Deprecated: Use TokenRequestWithMetadata.ProtoReflect.Descriptor instead. func (*TokenRequestWithMetadata) Descriptor() ([]byte, []int) { - return file_request_proto_rawDescGZIP(), []int{13} + return file_request_proto_rawDescGZIP(), []int{15} } func (x *TokenRequestWithMetadata) GetVersion() uint32 { @@ -1022,36 +1126,46 @@ var file_request_proto_rawDesc = []byte{ 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x22, 0x1d, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x22, 0xc7, 0x01, 0x0a, 0x0c, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x31, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x53, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x12, 0x40, 0x0a, 0x12, 0x61, 0x75, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x52, 0x11, 0x61, 0x75, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x73, 0x22, 0xb6, 0x01, 0x0a, 0x18, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x61, - 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x6e, 0x63, - 0x68, 0x6f, 0x72, 0x12, 0x2e, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2a, 0x25, 0x0a, - 0x0a, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x49, - 0x53, 0x53, 0x55, 0x45, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, - 0x45, 0x52, 0x10, 0x01, 0x42, 0x16, 0x5a, 0x14, 0x2e, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x73, 0x2d, 0x67, 0x6f, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x22, 0x71, 0x0a, 0x10, 0x41, 0x75, + 0x64, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x2c, + 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x52, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x2f, 0x0a, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x44, 0x0a, + 0x08, 0x41, 0x75, 0x64, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x38, 0x0a, 0x0a, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x22, 0xb3, 0x01, 0x0a, 0x0c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, + 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x31, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, + 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x08, 0x61, + 0x75, 0x64, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x52, + 0x08, 0x61, 0x75, 0x64, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x22, 0xb6, 0x01, 0x0a, 0x18, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x12, 0x2e, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x73, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, + 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x2a, 0x25, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x09, 0x0a, 0x05, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x54, + 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x10, 0x01, 0x42, 0x16, 0x5a, 0x14, 0x2e, 0x2e, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2d, 0x67, 0x6f, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1067,11 +1181,11 @@ func file_request_proto_rawDescGZIP() []byte { } var file_request_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_request_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_request_proto_msgTypes = make([]protoimpl.MessageInfo, 17) var file_request_proto_goTypes = []interface{}{ (ActionType)(0), // 0: protos.ActionType (*Identity)(nil), // 1: protos.Identity - (*AuditableIdentity)(nil), // 2: protos.Identity + (*AuditableIdentity)(nil), // 2: protos.AuditableIdentity (*TokenID)(nil), // 3: protos.TokenID (*TransferInputMetadata)(nil), // 4: protos.TransferInputMetadata (*OutputMetadata)(nil), // 5: protos.OutputMetadata @@ -1082,38 +1196,43 @@ var file_request_proto_goTypes = []interface{}{ (*TokenRequestMetadata)(nil), // 10: protos.TokenRequestMetadata (*Action)(nil), // 11: protos.Action (*Signature)(nil), // 12: protos.Signature - (*TokenRequest)(nil), // 13: protos.TokenRequest - (*TokenRequestWithMetadata)(nil), // 14: protos.TokenRequestWithMetadata - nil, // 15: protos.TokenRequestMetadata.ApplicationEntry + (*AuditorSignature)(nil), // 13: protos.AuditorSignature + (*Auditing)(nil), // 14: protos.Auditing + (*TokenRequest)(nil), // 15: protos.TokenRequest + (*TokenRequestWithMetadata)(nil), // 16: protos.TokenRequestWithMetadata + nil, // 17: protos.TokenRequestMetadata.ApplicationEntry } var file_request_proto_depIdxs = []int32{ - 1, // 0: protos.Identity.identity:type_name -> protos.Identity + 1, // 0: protos.AuditableIdentity.identity:type_name -> protos.Identity 3, // 1: protos.TransferInputMetadata.token_id:type_name -> protos.TokenID - 2, // 2: protos.TransferInputMetadata.senders:type_name -> protos.Identity - 2, // 3: protos.OutputMetadata.receivers:type_name -> protos.Identity + 2, // 2: protos.TransferInputMetadata.senders:type_name -> protos.AuditableIdentity + 2, // 3: protos.OutputMetadata.receivers:type_name -> protos.AuditableIdentity 4, // 4: protos.TransferMetadata.inputs:type_name -> protos.TransferInputMetadata 5, // 5: protos.TransferMetadata.outputs:type_name -> protos.OutputMetadata 1, // 6: protos.TransferMetadata.extra_signers:type_name -> protos.Identity 3, // 7: protos.IssueInputMetadata.token_id:type_name -> protos.TokenID - 2, // 8: protos.IssueMetadata.issuer:type_name -> protos.Identity + 2, // 8: protos.IssueMetadata.issuer:type_name -> protos.AuditableIdentity 7, // 9: protos.IssueMetadata.inputs:type_name -> protos.IssueInputMetadata 5, // 10: protos.IssueMetadata.outputs:type_name -> protos.OutputMetadata 1, // 11: protos.IssueMetadata.extra_signers:type_name -> protos.Identity 8, // 12: protos.ActionMetadata.issue_metadata:type_name -> protos.IssueMetadata 6, // 13: protos.ActionMetadata.transfer_metadata:type_name -> protos.TransferMetadata 9, // 14: protos.TokenRequestMetadata.metadata:type_name -> protos.ActionMetadata - 15, // 15: protos.TokenRequestMetadata.application:type_name -> protos.TokenRequestMetadata.ApplicationEntry + 17, // 15: protos.TokenRequestMetadata.application:type_name -> protos.TokenRequestMetadata.ApplicationEntry 0, // 16: protos.Action.type:type_name -> protos.ActionType - 11, // 17: protos.TokenRequest.actions:type_name -> protos.Action - 12, // 18: protos.TokenRequest.signatures:type_name -> protos.Signature - 12, // 19: protos.TokenRequest.auditor_signatures:type_name -> protos.Signature - 13, // 20: protos.TokenRequestWithMetadata.request:type_name -> protos.TokenRequest - 10, // 21: protos.TokenRequestWithMetadata.metadata:type_name -> protos.TokenRequestMetadata - 22, // [22:22] is the sub-list for method output_type - 22, // [22:22] is the sub-list for method input_type - 22, // [22:22] is the sub-list for extension type_name - 22, // [22:22] is the sub-list for extension extendee - 0, // [0:22] is the sub-list for field type_name + 1, // 17: protos.AuditorSignature.identity:type_name -> protos.Identity + 12, // 18: protos.AuditorSignature.signature:type_name -> protos.Signature + 13, // 19: protos.Auditing.signatures:type_name -> protos.AuditorSignature + 11, // 20: protos.TokenRequest.actions:type_name -> protos.Action + 12, // 21: protos.TokenRequest.signatures:type_name -> protos.Signature + 14, // 22: protos.TokenRequest.auditing:type_name -> protos.Auditing + 15, // 23: protos.TokenRequestWithMetadata.request:type_name -> protos.TokenRequest + 10, // 24: protos.TokenRequestWithMetadata.metadata:type_name -> protos.TokenRequestMetadata + 25, // [25:25] is the sub-list for method output_type + 25, // [25:25] is the sub-list for method input_type + 25, // [25:25] is the sub-list for extension type_name + 25, // [25:25] is the sub-list for extension extendee + 0, // [0:25] is the sub-list for field type_name } func init() { file_request_proto_init() } @@ -1267,7 +1386,7 @@ func file_request_proto_init() { } } file_request_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TokenRequest); i { + switch v := v.(*AuditorSignature); i { case 0: return &v.state case 1: @@ -1279,6 +1398,30 @@ func file_request_proto_init() { } } file_request_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Auditing); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_request_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TokenRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_request_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TokenRequestWithMetadata); i { case 0: return &v.state @@ -1301,7 +1444,7 @@ func file_request_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_request_proto_rawDesc, NumEnums: 1, - NumMessages: 15, + NumMessages: 17, NumExtensions: 0, NumServices: 0, }, diff --git a/token/driver/protos/request.proto b/token/driver/protos/request.proto index 93395507c..55dea47b6 100644 --- a/token/driver/protos/request.proto +++ b/token/driver/protos/request.proto @@ -91,12 +91,23 @@ message Signature { bytes raw = 1; // Raw bytes of the signature } +// Represent a signature of an auditor +message AuditorSignature { + Identity identity = 1; // The identity of the auditor that signed + Signature signature = 2; // Its signatures +} + +// Auditing is the section dedicated to the auditing +message Auditing { + repeated AuditorSignature signatures = 1; // Signatures of the auditors +} + // Token request containing multiple actions and their signatures message TokenRequest { uint32 version = 1; // Version number repeated Action actions = 2; // List of token actions to perform repeated Signature signatures = 3; // Signatures for the actions - repeated Signature auditor_signatures = 4; // Additional signatures from auditors + Auditing auditing = 4; // Section dedicated to the auditing } message TokenRequestWithMetadata { diff --git a/token/driver/request.go b/token/driver/request.go index 7145bffc8..58b76f455 100644 --- a/token/driver/request.go +++ b/token/driver/request.go @@ -22,6 +22,32 @@ const ( ProtocolV1 = 1 ) +type AuditorSignature struct { + Identity Identity + Signature []byte +} + +func (r *AuditorSignature) ToProtos() (*request.AuditorSignature, error) { + return &request.AuditorSignature{ + Identity: &request.Identity{ + Raw: r.Identity, + }, + Signature: &request.Signature{ + Raw: r.Signature, + }, + }, nil +} + +func (r *AuditorSignature) FromProtos(tr *request.AuditorSignature) error { + if tr.Identity != nil { + r.Identity = tr.Identity.Raw + } + if tr.Signature != nil { + r.Signature = tr.Signature.Raw + } + return nil +} + // TokenRequest is a collection of Token Action: // Issues, to create new Tokens; // Transfers, to manipulate Tokens (e.g., transfer ownership or redeem) @@ -32,7 +58,7 @@ type TokenRequest struct { Issues [][]byte Transfers [][]byte Signatures [][]byte - AuditorSignatures [][]byte + AuditorSignatures []*AuditorSignature } func (r *TokenRequest) Bytes() ([]byte, error) { @@ -53,16 +79,25 @@ func (r *TokenRequest) FromBytes(raw []byte) error { } func (r *TokenRequest) ToProtos() (*request.TokenRequest, error) { - tr := &request.TokenRequest{ - Version: ProtocolV1, - } - tr.Actions = append( + actions := append( utils.ToActionSlice(request.ActionType_ISSUE, r.Issues), utils.ToActionSlice(request.ActionType_TRANSFER, r.Transfers)..., ) - tr.Signatures = utils.ToSignatureSlice(r.Signatures) - tr.AuditorSignatures = utils.ToSignatureSlice(r.AuditorSignatures) - return tr, nil + signatures := utils.ToSignatureSlice(r.Signatures) + auditorSignatures, err := protos.ToProtosSlice[request.AuditorSignature, *AuditorSignature](r.AuditorSignatures) + if err != nil { + return nil, errors.Wrap(err, "failed converting auditor signatures") + } + auditing := &request.Auditing{ + Signatures: auditorSignatures, + } + + return &request.TokenRequest{ + Version: ProtocolV1, + Actions: actions, + Signatures: signatures, + Auditing: auditing, + }, nil } func (r *TokenRequest) FromProtos(tr *request.TokenRequest) error { @@ -90,11 +125,12 @@ func (r *TokenRequest) FromProtos(tr *request.TokenRequest) error { } r.Signatures = append(r.Signatures, signature.Raw) } - for _, signature := range tr.AuditorSignatures { - if signature == nil || len(signature.Raw) == 0 { - return errors.New("nil auditor signature found") + if tr.Auditing != nil { + r.AuditorSignatures = make([]*AuditorSignature, len(tr.Auditing.Signatures)) + r.AuditorSignatures = slices.GenericSliceOfPointers[AuditorSignature](len(tr.Auditing.Signatures)) + if err := protos.FromProtosSlice(tr.Auditing.Signatures, r.AuditorSignatures); err != nil { + return errors.Wrap(err, "failed converting auditor signatures") } - r.AuditorSignatures = append(r.AuditorSignatures, signature.Raw) } return nil } diff --git a/token/driver/request_test.go b/token/driver/request_test.go index 392ab8070..f45969a7f 100644 --- a/token/driver/request_test.go +++ b/token/driver/request_test.go @@ -19,9 +19,14 @@ func TestTokenRequestSerialization(t *testing.T) { []byte("issue1"), []byte("issue2"), }, - Transfers: [][]byte{[]byte("transfer1")}, - Signatures: [][]byte{[]byte("signature1")}, - AuditorSignatures: [][]byte{[]byte("auditor_signature1")}, + Transfers: [][]byte{[]byte("transfer1")}, + Signatures: [][]byte{[]byte("signature1")}, + AuditorSignatures: []*AuditorSignature{ + { + Identity: Identity("auditor1"), + Signature: []byte("signature1"), + }, + }, } raw, err := req.Bytes() assert.NoError(t, err) diff --git a/token/driver/validator.go b/token/driver/validator.go index b3f76fafa..94c1fe78b 100644 --- a/token/driver/validator.go +++ b/token/driver/validator.go @@ -27,6 +27,8 @@ type Ledger interface { GetState(id token.ID) ([]byte, error) } +//go:generate counterfeiter -o mock/signature_provider.go -fake-name SignatureProvider . SignatureProvider + type SignatureProvider interface { // HasBeenSignedBy returns true and the verified signature if the provider contains a valid signature for the passed identity and verifier HasBeenSignedBy(id Identity, verifier Verifier) ([]byte, error) diff --git a/token/request.go b/token/request.go index 56c65e302..9a4d2f584 100644 --- a/token/request.go +++ b/token/request.go @@ -1039,8 +1039,11 @@ func (r *Request) FromBytes(raw []byte) error { } // AddAuditorSignature adds an auditor signature to the request. -func (r *Request) AddAuditorSignature(sigma []byte) { - r.Actions.AuditorSignatures = append(r.Actions.AuditorSignatures, sigma) +func (r *Request) AddAuditorSignature(identity Identity, sigma []byte) { + r.Actions.AuditorSignatures = append(r.Actions.AuditorSignatures, &driver.AuditorSignature{ + Identity: identity, + Signature: sigma, + }) } func (r *Request) SetSignatures(sigmas map[string][]byte) bool { diff --git a/token/request_test.go b/token/request_test.go index 7d5f7caa9..adf062334 100644 --- a/token/request_test.go +++ b/token/request_test.go @@ -20,9 +20,14 @@ func TestRequestSerialization(t *testing.T) { []byte("issue1"), []byte("issue2"), }, - Transfers: [][]byte{[]byte("transfer1")}, - Signatures: [][]byte{[]byte("signature1")}, - AuditorSignatures: [][]byte{[]byte("auditor_signature1")}, + Transfers: [][]byte{[]byte("transfer1")}, + Signatures: [][]byte{[]byte("signature1")}, + AuditorSignatures: []*driver.AuditorSignature{ + { + Identity: Identity("auditor1"), + Signature: []byte("signature1"), + }, + }, } raw, err := r.Bytes() assert.NoError(t, err) diff --git a/token/services/ttx/auditor.go b/token/services/ttx/auditor.go index c12a510f9..69c7eaf7c 100644 --- a/token/services/ttx/auditor.go +++ b/token/services/ttx/auditor.go @@ -122,12 +122,13 @@ func (r *RegisterAuditorView) Call(context view.Context) (interface{}, error) { } type AuditingViewInitiator struct { - tx *Transaction - local bool + tx *Transaction + local bool + skipAuditorSignatureVerification bool } -func newAuditingViewInitiator(tx *Transaction, local bool) *AuditingViewInitiator { - return &AuditingViewInitiator{tx: tx, local: local} +func newAuditingViewInitiator(tx *Transaction, local, skipAuditorSignatureVerification bool) *AuditingViewInitiator { + return &AuditingViewInitiator{tx: tx, local: local, skipAuditorSignatureVerification: skipAuditorSignatureVerification} } func (a *AuditingViewInitiator) Call(context view.Context) (interface{}, error) { @@ -156,39 +157,13 @@ func (a *AuditingViewInitiator) Call(context view.Context) (interface{}, error) span.AddEvent("received_message") logger.Debugf("reply received from %s", a.tx.Opts.Auditor) - // Check signature - signed, err := a.tx.MarshallToAudit() + auditorIdentity, err := a.verifyAuditorSignature(context, signature) if err != nil { - return nil, errors.Wrapf(err, "failed marshalling message to sign") - } - if logger.IsEnabledFor(zapcore.DebugLevel) { - logger.Debugf("Verifying auditor signature on [%s][%s][%s]", a.tx.Opts.Auditor.UniqueID(), hash.Hashable(signed).String(), a.tx.ID()) + return nil, errors.Wrapf(err, "failed verifying auditor signature") } - validAuditing := false - span.AddEvent("validate_auditing") - for _, auditorID := range a.tx.TokenService().PublicParametersManager().PublicParameters().Auditors() { - v, err := a.tx.TokenService().SigService().AuditorVerifier(auditorID) - if err != nil { - logger.Debugf("failed to get auditor verifier for [%s]", auditorID) - continue - } - span.AddEvent("verify_auditor_signature") - if err := v.Verify(signed, signature); err != nil { - logger.Errorf("failed verifying auditor signature [%s][%s][%s]", auditorID, hash.Hashable(signed).String(), a.tx.TokenRequest.Anchor) - } else { - if logger.IsEnabledFor(zapcore.DebugLevel) { - logger.Debugf("auditor signature verified [%s][%s][%s]", auditorID, base64.StdEncoding.EncodeToString(signature), hash.Hashable(signed)) - } - validAuditing = true - break - } - } - if !validAuditing { - return nil, errors.Errorf("failed verifying auditor signature [%s][%s]", hash.Hashable(signed).String(), a.tx.TokenRequest.Anchor) - } span.AddEvent("append_auditor_signature") - a.tx.TokenRequest.AddAuditorSignature(signature) + a.tx.TokenRequest.AddAuditorSignature(auditorIdentity, signature) logger.Debug("auditor signature verified") return session, nil @@ -259,6 +234,42 @@ func (a *AuditingViewInitiator) startLocal(context view.Context) (view.Session, return left, nil } +func (a *AuditingViewInitiator) verifyAuditorSignature(context view.Context, signature []byte) (token.Identity, error) { + span := trace.SpanFromContext(context.Context()) + span.AddEvent("validate_auditing") + + if a.skipAuditorSignatureVerification { + return a.tx.Opts.Auditor, nil + } + + // check the signature + signed, err := a.tx.MarshallToAudit() + if err != nil { + return nil, errors.Wrapf(err, "failed marshalling message to sign") + } + if logger.IsEnabledFor(zapcore.DebugLevel) { + logger.Debugf("Verifying auditor signature on [%s][%s][%s]", a.tx.Opts.Auditor.UniqueID(), hash.Hashable(signed).String(), a.tx.ID()) + } + + for _, auditorID := range a.tx.TokenService().PublicParametersManager().PublicParameters().Auditors() { + v, err := a.tx.TokenService().SigService().AuditorVerifier(auditorID) + if err != nil { + logger.Debugf("failed to get auditor verifier for [%s]", auditorID) + continue + } + span.AddEvent("verify_auditor_signature") + if err := v.Verify(signed, signature); err != nil { + logger.Errorf("failed verifying auditor signature [%s][%s][%s]", auditorID, hash.Hashable(signed).String(), a.tx.TokenRequest.Anchor) + } else { + if logger.IsEnabledFor(zapcore.DebugLevel) { + logger.Debugf("auditor signature verified [%s][%s][%s]", auditorID, base64.StdEncoding.EncodeToString(signature), hash.Hashable(signed)) + } + return auditorID, nil + } + } + return nil, errors.Errorf("failed verifying auditor signature [%s][%s]", hash.Hashable(signed).String(), a.tx.TokenRequest.Anchor) +} + type AuditApproveView struct { w *token.AuditorWallet tx *Transaction diff --git a/token/services/ttx/endorse.go b/token/services/ttx/endorse.go index 869a4031c..0e7888213 100644 --- a/token/services/ttx/endorse.go +++ b/token/services/ttx/endorse.go @@ -420,7 +420,7 @@ func (c *CollectEndorsementsView) requestAudit(context view.Context) ([]view.Ide logger.Debugf("ask auditing to [%s]", c.tx.Opts.Auditor) } local := view2.GetSigService(context).IsMe(c.tx.Opts.Auditor) - sessionBoxed, err := context.RunView(newAuditingViewInitiator(c.tx, local)) + sessionBoxed, err := context.RunView(newAuditingViewInitiator(c.tx, local, c.Opts.SkipAuditorSignatureVerification)) if err != nil { return nil, errors.WithMessagef(err, "failed requesting auditing from [%s]", c.tx.Opts.Auditor.String()) } diff --git a/token/services/ttx/endorse_opts.go b/token/services/ttx/endorse_opts.go index 7bff89546..a9dec56b2 100644 --- a/token/services/ttx/endorse_opts.go +++ b/token/services/ttx/endorse_opts.go @@ -10,6 +10,8 @@ package ttx type EndorsementsOpts struct { // SkipAuditing set it to true to skip the auditing phase SkipAuditing bool + // SkipAuditorSignatureVerification set it to true to skip the verification of the auditor signature + SkipAuditorSignatureVerification bool // SkipApproval set it to true to skip the approval phase SkipApproval bool // SkipDistributeEnv set it to true to skip the distribution phase @@ -47,6 +49,14 @@ func WithSkipAuditing() EndorsementsOpt { } } +// WithSkipAuditorSignatureVerification to skip auditor signature verification +func WithSkipAuditorSignatureVerification() EndorsementsOpt { + return func(o *EndorsementsOpts) error { + o.SkipAuditorSignatureVerification = true + return nil + } +} + // WithSkipApproval to skip approval func WithSkipApproval() EndorsementsOpt { return func(o *EndorsementsOpts) error {