From 436656f32c26783922558102ed748b3c2a4c206f Mon Sep 17 00:00:00 2001 From: Ethan Reesor Date: Thu, 21 Sep 2023 15:31:59 -0500 Subject: [PATCH] Fix delegated signature processing [#3418] --- .../core/execute/v2/block/sig_authority.go | 9 +- .../execute/v2/block/sig_authority_test.go | 142 ++++++++++++++++++ pkg/build/signature.go | 6 + 3 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 internal/core/execute/v2/block/sig_authority_test.go diff --git a/internal/core/execute/v2/block/sig_authority.go b/internal/core/execute/v2/block/sig_authority.go index 37138a5c8..9d0b4dd05 100644 --- a/internal/core/execute/v2/block/sig_authority.go +++ b/internal/core/execute/v2/block/sig_authority.go @@ -166,12 +166,17 @@ func (x AuthoritySignature) processDelegated(batch *database.Batch, ctx *Signatu } // Add the signature to the transaction's signature set and chain - err = addSignature(batch, ctx, signer, &database.SignatureSetEntry{ + entry := &database.SignatureSetEntry{ KeyIndex: uint64(keyIndex), Version: signer.GetVersion(), Hash: ctx.message.Hash(), Path: sig.Delegator, - }) + } + if ctx.GetActiveGlobals().ExecutorVersion.V2SignatureEthereumEnabled() { + // The path should not include the current signer + entry.Path = sig.Delegator[1:] + } + err = addSignature(batch, ctx, signer, entry) if err != nil { return errors.UnknownError.Wrap(err) } diff --git a/internal/core/execute/v2/block/sig_authority_test.go b/internal/core/execute/v2/block/sig_authority_test.go new file mode 100644 index 000000000..3f8e429a0 --- /dev/null +++ b/internal/core/execute/v2/block/sig_authority_test.go @@ -0,0 +1,142 @@ +// Copyright 2023 The Accumulate Authors +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +package block_test + +import ( + "crypto/ed25519" + "crypto/sha256" + "fmt" + "math/big" + "testing" + + "github.com/stretchr/testify/require" + "gitlab.com/accumulatenetwork/accumulate/internal/api/routing" + "gitlab.com/accumulatenetwork/accumulate/internal/database/smt/storage" + "gitlab.com/accumulatenetwork/accumulate/pkg/build" + "gitlab.com/accumulatenetwork/accumulate/pkg/types/messaging" + "gitlab.com/accumulatenetwork/accumulate/pkg/url" + "gitlab.com/accumulatenetwork/accumulate/protocol" + . "gitlab.com/accumulatenetwork/accumulate/test/harness" + . "gitlab.com/accumulatenetwork/accumulate/test/helpers" + "gitlab.com/accumulatenetwork/accumulate/test/simulator" +) + +func TestDelegationPath(t *testing.T) { + alice := protocol.AccountUrl("alice") + bob := protocol.AccountUrl("bob") + bobKey := generateKey(bob) + charlie := protocol.AccountUrl("charlie") + charlieKey := generateKey(charlie) + david := protocol.AccountUrl("david") + davidKey := generateKey(david) + + type Path struct { + Delegates []*url.URL + Key ed25519.PrivateKey + } + cases := []struct { + Paths []Path + }{ + // One delegated, one direct + {Paths: []Path{ + {Delegates: nil, Key: bobKey}, + {Delegates: []*url.URL{charlie}, Key: charlieKey}, + }}, + + // Both delegated, same path + {Paths: []Path{ + {Delegates: []*url.URL{bob}, Key: bobKey}, + {Delegates: []*url.URL{charlie}, Key: charlieKey}, + }}, + + // Both delegated, different paths + {Paths: []Path{ + {Delegates: []*url.URL{bob, david}, Key: davidKey}, + {Delegates: []*url.URL{charlie, david}, Key: davidKey}, + }}, + } + + // Run the tests + for i, c := range cases { + t.Run(fmt.Sprintf("Case %d", i+1), func(t *testing.T) { + var timestamp uint64 + + sim := NewSim(t, + simulator.SimpleNetwork(t.Name(), 1, 1), + simulator.GenesisWithVersion(GenesisTime, protocol.ExecutorVersionV2SignatureEthereum), + ) + + MakeIdentity(t, sim.DatabaseFor(alice), alice) + MakeIdentity(t, sim.DatabaseFor(bob), bob, bobKey[32:]) + MakeIdentity(t, sim.DatabaseFor(charlie), charlie, charlieKey[32:]) + MakeIdentity(t, sim.DatabaseFor(david), david, davidKey[32:]) + MakeAccount(t, sim.DatabaseFor(alice), &protocol.TokenAccount{Url: alice.JoinPath("tokens"), TokenUrl: protocol.AcmeUrl(), Balance: *big.NewInt(1e15)}) + + UpdateAccount(t, sim.DatabaseFor(alice), alice.JoinPath("book", "1"), func(p *protocol.KeyPage) { + p.CreditBalance = 1e9 + p.AddKeySpec(&protocol.KeySpec{Delegate: bob.JoinPath("book"), PublicKeyHash: doSha256(bobKey[32:])}) + p.AddKeySpec(&protocol.KeySpec{Delegate: charlie.JoinPath("book"), PublicKeyHash: doSha256(charlieKey[32:])}) + p.AcceptThreshold = 2 + }) + UpdateAccount(t, sim.DatabaseFor(bob), bob.JoinPath("book", "1"), func(p *protocol.KeyPage) { + p.CreditBalance = 1e9 + p.AddKeySpec(&protocol.KeySpec{Delegate: david.JoinPath("book"), PublicKeyHash: doSha256(davidKey[32:])}) + }) + UpdateAccount(t, sim.DatabaseFor(charlie), charlie.JoinPath("book", "1"), func(p *protocol.KeyPage) { + p.CreditBalance = 1e9 + p.AddKeySpec(&protocol.KeySpec{Delegate: david.JoinPath("book"), PublicKeyHash: doSha256(davidKey[32:])}) + }) + UpdateAccount(t, sim.DatabaseFor(david), david.JoinPath("book", "1"), func(p *protocol.KeyPage) { + p.CreditBalance = 1e9 + }) + + txn, err := build.Transaction().For(alice, "tokens").BurnTokens(1, 0). + Done() + require.NoError(t, err) + + var st []*protocol.TransactionStatus + for _, p := range c.Paths { + var path []*url.URL + for i := len(p.Delegates) - 1; i >= 0; i-- { + path = append(path, p.Delegates[i].JoinPath("book", "1")) + } + path = append(path, alice.JoinPath("book", "1")) + st = sim.BuildAndSubmitSuccessfully( + build.SignatureForTransaction(txn). + Url(path[0]). + Delegators(path[1:]...). + Version(1). + Timestamp(×tamp). + PrivateKey(p.Key)) + } + + sim.StepUntil( + Txn(st[0].TxID).Completes(), + Sig(st[1].TxID).AuthoritySignature().Completes()) + }) + } +} + +func generateKey(seed ...interface{}) ed25519.PrivateKey { + h := storage.MakeKey(seed...) + return ed25519.NewKeyFromSeed(h[:]) +} + +func doSha256(data []byte) []byte { + hash := sha256.Sum256(data) + return hash[:] +} + +type routerFunc func(*url.URL) (string, error) + +func (f routerFunc) RouteAccount(u *url.URL) (string, error) { + return f(u) +} + +func (f routerFunc) Route(env ...*messaging.Envelope) (string, error) { + return routing.RouteEnvelopes(f, env...) +} diff --git a/pkg/build/signature.go b/pkg/build/signature.go index e4e6a9f09..84d739050 100644 --- a/pkg/build/signature.go +++ b/pkg/build/signature.go @@ -10,6 +10,7 @@ import ( "gitlab.com/accumulatenetwork/accumulate/pkg/client/signing" "gitlab.com/accumulatenetwork/accumulate/pkg/errors" "gitlab.com/accumulatenetwork/accumulate/pkg/types/messaging" + "gitlab.com/accumulatenetwork/accumulate/pkg/url" "gitlab.com/accumulatenetwork/accumulate/protocol" ) @@ -44,6 +45,11 @@ func (b SignatureBuilder) Delegator(delegator any, path ...string) SignatureBuil return b } +func (b SignatureBuilder) Delegators(delegators ...*url.URL) SignatureBuilder { + b.signer.Delegators = append(b.signer.Delegators, delegators...) + return b +} + func (b SignatureBuilder) Accept() SignatureBuilder { b.signer.Vote = protocol.VoteTypeAccept return b