From a0e5a445bf87d2a3a108d7eea8e5f4ebd7e5f8e2 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 9 Oct 2024 11:10:29 +0100 Subject: [PATCH] Add transaction signer (#850) * Enhance Tx Builder * changing the builder to a tx.signer * tweaks + tests * Updating method signatures * pr comments * Update tx/signer.go Co-authored-by: libotony * pr comments * add more tests --------- Co-authored-by: libotony --- api/accounts/accounts_test.go | 16 ++---- api/blocks/blocks_test.go | 29 +++++----- api/debug/debug_test.go | 13 +---- api/subscriptions/block_reader_test.go | 7 +-- api/subscriptions/pending_tx_test.go | 34 +++++------ api/subscriptions/types_test.go | 32 ++++------- api/transactions/transactions_test.go | 41 +++++-------- cmd/thor/node/tx_stash_test.go | 9 +-- cmd/thor/solo/solo.go | 8 +-- consensus/consensus_test.go | 7 +-- packer/packer_test.go | 7 +-- runtime/resolved_tx_test.go | 4 +- tx/signer.go | 79 ++++++++++++++++++++++++++ tx/signer_test.go | 76 +++++++++++++++++++++++++ txpool/tx_object_test.go | 30 +++++----- 15 files changed, 246 insertions(+), 146 deletions(-) create mode 100644 tx/signer.go create mode 100644 tx/signer_test.go diff --git a/api/accounts/accounts_test.go b/api/accounts/accounts_test.go index 16f08e252..6431d1934 100644 --- a/api/accounts/accounts_test.go +++ b/api/accounts/accounts_test.go @@ -19,7 +19,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" ABI "github.com/vechain/thor/v2/abi" @@ -262,7 +261,7 @@ func initAccountServer(t *testing.T) { repo, _ := chain.NewRepository(db, b) claTransfer := tx.NewClause(&addr).WithValue(value) claDeploy := tx.NewClause(nil).WithData(bytecode) - transaction := buildTxWithClauses(t, repo.ChainTag(), claTransfer, claDeploy) + transaction := buildTxWithClauses(repo.ChainTag(), claTransfer, claDeploy) contractAddr = thor.CreateContractAddress(transaction.ID(), 1, 0) packTx(repo, stater, transaction, t) @@ -274,7 +273,7 @@ func initAccountServer(t *testing.T) { t.Fatal(err) } claCall := tx.NewClause(&contractAddr).WithData(input) - transactionCall := buildTxWithClauses(t, repo.ChainTag(), claCall) + transactionCall := buildTxWithClauses(repo.ChainTag(), claCall) packTx(repo, stater, transactionCall, t) router := mux.NewRouter() @@ -284,7 +283,7 @@ func initAccountServer(t *testing.T) { ts = httptest.NewServer(router) } -func buildTxWithClauses(t *testing.T, chaiTag byte, clauses ...*tx.Clause) *tx.Transaction { +func buildTxWithClauses(chaiTag byte, clauses ...*tx.Clause) *tx.Transaction { builder := new(tx.Builder). ChainTag(chaiTag). Expiration(10). @@ -293,12 +292,9 @@ func buildTxWithClauses(t *testing.T, chaiTag byte, clauses ...*tx.Clause) *tx.T builder.Clause(c) } - transaction := builder.Build() - sig, err := crypto.Sign(transaction.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - if err != nil { - t.Fatal(err) - } - return transaction.WithSignature(sig) + trx := builder.Build() + + return tx.MustSign(trx, genesis.DevAccounts()[0].PrivateKey) } func packTx(repo *chain.Repository, stater *state.Stater, transaction *tx.Transaction, t *testing.T) { diff --git a/api/blocks/blocks_test.go b/api/blocks/blocks_test.go index 0ad5350b5..4685e8080 100644 --- a/api/blocks/blocks_test.go +++ b/api/blocks/blocks_test.go @@ -17,7 +17,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/crypto" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -171,28 +170,26 @@ func initBlockServer(t *testing.T) { repo, _ := chain.NewRepository(db, b) addr := thor.BytesToAddress([]byte("to")) cla := tx.NewClause(&addr).WithValue(big.NewInt(10000)) - tx := new(tx.Builder). - ChainTag(repo.ChainTag()). - GasPriceCoef(1). - Expiration(10). - Gas(21000). - Nonce(1). - Clause(cla). - BlockRef(tx.NewBlockRef(0)). - Build() + trx := tx.MustSign( + new(tx.Builder). + ChainTag(repo.ChainTag()). + GasPriceCoef(1). + Expiration(10). + Gas(21000). + Nonce(1). + Clause(cla). + BlockRef(tx.NewBlockRef(0)). + Build(), + genesis.DevAccounts()[0].PrivateKey, + ) - sig, err := crypto.Sign(tx.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - if err != nil { - t.Fatal(err) - } - tx = tx.WithSignature(sig) packer := packer.New(repo, stater, genesis.DevAccounts()[0].Address, &genesis.DevAccounts()[0].Address, thor.NoFork) sum, _ := repo.GetBlockSummary(b.Header().ID()) flow, err := packer.Schedule(sum, uint64(time.Now().Unix())) if err != nil { t.Fatal(err) } - err = flow.Adopt(tx) + err = flow.Adopt(trx) if err != nil { t.Fatal(err) } diff --git a/api/debug/debug_test.go b/api/debug/debug_test.go index 3c61dfcab..8a8088e4d 100644 --- a/api/debug/debug_test.go +++ b/api/debug/debug_test.go @@ -20,7 +20,6 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/vechain/thor/v2/block" @@ -532,11 +531,7 @@ func initDebugServer(t *testing.T) { Expiration(10). Gas(21000). Build() - sig, err := crypto.Sign(noClausesTx.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - if err != nil { - t.Fatal(err) - } - noClausesTx = noClausesTx.WithSignature(sig) + noClausesTx = tx.MustSign(noClausesTx, genesis.DevAccounts()[0].PrivateKey) cla := tx.NewClause(&addr).WithValue(big.NewInt(10000)) cla2 := tx.NewClause(&addr).WithValue(big.NewInt(10000)) @@ -550,12 +545,8 @@ func initDebugServer(t *testing.T) { Clause(cla2). BlockRef(tx.NewBlockRef(0)). Build() + transaction = tx.MustSign(transaction, genesis.DevAccounts()[0].PrivateKey) - sig, err = crypto.Sign(transaction.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - if err != nil { - t.Fatal(err) - } - transaction = transaction.WithSignature(sig) packer := packer.New(repo, stater, genesis.DevAccounts()[0].Address, &genesis.DevAccounts()[0].Address, thor.NoFork) sum, _ := repo.GetBlockSummary(b.Header().ID()) flow, err := packer.Schedule(sum, uint64(time.Now().Unix())) diff --git a/api/subscriptions/block_reader_test.go b/api/subscriptions/block_reader_test.go index f6db221e5..f772ba389 100644 --- a/api/subscriptions/block_reader_test.go +++ b/api/subscriptions/block_reader_test.go @@ -10,7 +10,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" "github.com/vechain/thor/v2/block" "github.com/vechain/thor/v2/chain" @@ -86,12 +85,8 @@ func initChain(t *testing.T) (*chain.Repository, []*block.Block, *txpool.TxPool) Clause(cla). BlockRef(tx.NewBlockRef(0)). Build() + tr = tx.MustSign(tr, genesis.DevAccounts()[0].PrivateKey) - sig, err := crypto.Sign(tr.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - if err != nil { - t.Fatal(err) - } - tr = tr.WithSignature(sig) packer := packer.New(repo, stater, genesis.DevAccounts()[0].Address, &genesis.DevAccounts()[0].Address, thor.NoFork) sum, _ := repo.GetBlockSummary(b.Header().ID()) flow, err := packer.Schedule(sum, uint64(time.Now().Unix())) diff --git a/api/subscriptions/pending_tx_test.go b/api/subscriptions/pending_tx_test.go index 629b96e0c..a4deb2c89 100644 --- a/api/subscriptions/pending_tx_test.go +++ b/api/subscriptions/pending_tx_test.go @@ -10,7 +10,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" "github.com/vechain/thor/v2/block" "github.com/vechain/thor/v2/chain" @@ -77,7 +76,7 @@ func TestPendingTx_DispatchLoop(t *testing.T) { p.Subscribe(txCh) // Add a new tx to the mempool - transaction := createTx(t, repo, 0) + transaction := createTx(repo, 0) txPool.AddLocal(transaction) // Start the dispatch loop @@ -95,7 +94,7 @@ func TestPendingTx_DispatchLoop(t *testing.T) { p.Unsubscribe(txCh) // Add another tx to the mempool - tx2 := createTx(t, repo, 1) + tx2 := createTx(repo, 1) txPool.AddLocal(tx2) // Assert that the channel did not receive the second transaction @@ -129,23 +128,20 @@ func addNewBlock(repo *chain.Repository, stater *state.Stater, b0 *block.Block, } } -func createTx(t *testing.T, repo *chain.Repository, addressNumber uint) *tx.Transaction { +func createTx(repo *chain.Repository, addressNumber uint) *tx.Transaction { addr := thor.BytesToAddress([]byte("to")) cla := tx.NewClause(&addr).WithValue(big.NewInt(10000)) - tx := new(tx.Builder). - ChainTag(repo.ChainTag()). - GasPriceCoef(1). - Expiration(10). - Gas(21000). - Nonce(1). - Clause(cla). - BlockRef(tx.NewBlockRef(0)). - Build() - sig, err := crypto.Sign(tx.SigningHash().Bytes(), genesis.DevAccounts()[addressNumber].PrivateKey) - if err != nil { - t.Fatal(err) - } - tx = tx.WithSignature(sig) - return tx + return tx.MustSign( + new(tx.Builder). + ChainTag(repo.ChainTag()). + GasPriceCoef(1). + Expiration(10). + Gas(21000). + Nonce(1). + Clause(cla). + BlockRef(tx.NewBlockRef(0)). + Build(), + genesis.DevAccounts()[addressNumber].PrivateKey, + ) } diff --git a/api/subscriptions/types_test.go b/api/subscriptions/types_test.go index 05492dd98..8cf1bbd07 100644 --- a/api/subscriptions/types_test.go +++ b/api/subscriptions/types_test.go @@ -103,12 +103,7 @@ func TestConvertTransfer(t *testing.T) { Nonce(1). BlockRef(tx.NewBlockRef(0)). Build() - - sig, err := crypto.Sign(transaction.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - if err != nil { - t.Fatal(err) - } - transaction = transaction.WithSignature(sig) + transaction = tx.MustSign(transaction, genesis.DevAccounts()[0].PrivateKey) // New block blk := new(block.Builder). @@ -187,20 +182,17 @@ func TestConvertEvent(t *testing.T) { repo, _ := chain.NewRepository(db, b) // New tx - transaction := new(tx.Builder). - ChainTag(repo.ChainTag()). - GasPriceCoef(1). - Expiration(10). - Gas(21000). - Nonce(1). - BlockRef(tx.NewBlockRef(0)). - Build() - - sig, err := crypto.Sign(transaction.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - if err != nil { - t.Fatal(err) - } - transaction = transaction.WithSignature(sig) + transaction := tx.MustSign( + new(tx.Builder). + ChainTag(repo.ChainTag()). + GasPriceCoef(1). + Expiration(10). + Gas(21000). + Nonce(1). + BlockRef(tx.NewBlockRef(0)). + Build(), + genesis.DevAccounts()[0].PrivateKey, + ) // New block blk := new(block.Builder). diff --git a/api/transactions/transactions_test.go b/api/transactions/transactions_test.go index 2d86d13e3..697b617e9 100644 --- a/api/transactions/transactions_test.go +++ b/api/transactions/transactions_test.go @@ -18,7 +18,6 @@ import ( "time" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" @@ -112,18 +111,17 @@ func sendTx(t *testing.T) { var expiration = uint32(10) var gas = uint64(21000) - tx := new(tx.Builder). - BlockRef(blockRef). - ChainTag(chainTag). - Expiration(expiration). - Gas(gas). - Build() - sig, err := crypto.Sign(tx.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - if err != nil { - t.Fatal(err) - } - tx = tx.WithSignature(sig) - rlpTx, err := rlp.EncodeToBytes(tx) + trx := tx.MustSign( + new(tx.Builder). + BlockRef(blockRef). + ChainTag(chainTag). + Expiration(expiration). + Gas(gas). + Build(), + genesis.DevAccounts()[0].PrivateKey, + ) + + rlpTx, err := rlp.EncodeToBytes(trx) if err != nil { t.Fatal(err) } @@ -133,7 +131,7 @@ func sendTx(t *testing.T) { if err = json.Unmarshal(res, &txObj); err != nil { t.Fatal(err) } - assert.Equal(t, tx.ID().String(), txObj["id"], "should be the same transaction id") + assert.Equal(t, trx.ID().String(), txObj["id"], "should be the same transaction id") } func getTxWithBadID(t *testing.T) { @@ -296,6 +294,7 @@ func initTransactionServer(t *testing.T) { Clause(cla). BlockRef(tx.NewBlockRef(0)). Build() + transaction = tx.MustSign(transaction, genesis.DevAccounts()[0].PrivateKey) mempoolTx = new(tx.Builder). ChainTag(repo.ChainTag()). @@ -303,19 +302,7 @@ func initTransactionServer(t *testing.T) { Gas(21000). Nonce(1). Build() - - sig, err := crypto.Sign(transaction.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - if err != nil { - t.Fatal(err) - } - - sig2, err := crypto.Sign(mempoolTx.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - if err != nil { - t.Fatal(err) - } - - transaction = transaction.WithSignature(sig) - mempoolTx = mempoolTx.WithSignature(sig2) + mempoolTx = tx.MustSign(mempoolTx, genesis.DevAccounts()[0].PrivateKey) packer := packer.New(repo, stater, genesis.DevAccounts()[0].Address, &genesis.DevAccounts()[0].Address, thor.NoFork) sum, _ := repo.GetBlockSummary(b.Header().ID()) diff --git a/cmd/thor/node/tx_stash_test.go b/cmd/thor/node/tx_stash_test.go index a573b682a..5fcd317b9 100644 --- a/cmd/thor/node/tx_stash_test.go +++ b/cmd/thor/node/tx_stash_test.go @@ -11,7 +11,6 @@ import ( "sort" "testing" - "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/storage" @@ -20,9 +19,11 @@ import ( ) func newTx() *tx.Transaction { - tx := new(tx.Builder).Nonce(rand.Uint64()).Build() // nolint:gosec - sig, _ := crypto.Sign(tx.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - return tx.WithSignature(sig) + return tx.MustSign( + new(tx.Builder). + Nonce(rand.Uint64()).Build(), // nolint:gosec, + genesis.DevAccounts()[0].PrivateKey, + ) } func TestTxStash(t *testing.T) { diff --git a/cmd/thor/solo/solo.go b/cmd/thor/solo/solo.go index af498ca04..d177d3705 100644 --- a/cmd/thor/solo/solo.go +++ b/cmd/thor/solo/solo.go @@ -15,7 +15,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/crypto" "github.com/pkg/errors" "github.com/vechain/thor/v2/block" "github.com/vechain/thor/v2/builtin" @@ -261,14 +260,11 @@ func (s *Solo) newTx(clauses []*tx.Clause, from genesis.DevAccount) (*tx.Transac builder.Clause(c) } - newTx := builder.BlockRef(tx.NewBlockRef(0)). + trx := builder.BlockRef(tx.NewBlockRef(0)). Expiration(math.MaxUint32). Nonce(rand.Uint64()). // #nosec DependsOn(nil). Gas(1_000_000). Build() - - sig, err := crypto.Sign(newTx.SigningHash().Bytes(), from.PrivateKey) - - return newTx.WithSignature(sig), err + return tx.Sign(trx, from.PrivateKey) } diff --git a/consensus/consensus_test.go b/consensus/consensus_test.go index 10f01e04e..14bf7ec43 100644 --- a/consensus/consensus_test.go +++ b/consensus/consensus_test.go @@ -577,12 +577,11 @@ func TestValidateBlockBody(t *testing.T) { { "TxOriginBlocked", func(t *testing.T) { thor.MockBlocklist([]string{genesis.DevAccounts()[9].Address.String()}) - tx := txBuilder(tc.tag).Build() - sig, _ := crypto.Sign(tx.SigningHash().Bytes(), genesis.DevAccounts()[9].PrivateKey) - tx = tx.WithSignature(sig) + trx := txBuilder(tc.tag).Build() + trx = tx.MustSign(trx, genesis.DevAccounts()[9].PrivateKey) blk, err := tc.sign( - tc.builder(tc.original.Header()).Transaction(tx), + tc.builder(tc.original.Header()).Transaction(trx), ) if err != nil { t.Fatal(err) diff --git a/packer/packer_test.go b/packer/packer_test.go index 4898804e4..da8078379 100644 --- a/packer/packer_test.go +++ b/packer/packer_test.go @@ -50,15 +50,14 @@ func (ti *txIterator) Next() *tx.Transaction { data, _ := method.EncodeInput(a1.Address, big.NewInt(1)) - tx := new(tx.Builder). + trx := new(tx.Builder). ChainTag(ti.chainTag). Clause(tx.NewClause(&builtin.Energy.Address).WithData(data)). Gas(300000).GasPriceCoef(0).Nonce(nonce).Expiration(math.MaxUint32).Build() + trx = tx.MustSign(trx, a0.PrivateKey) nonce++ - sig, _ := crypto.Sign(tx.SigningHash().Bytes(), a0.PrivateKey) - tx = tx.WithSignature(sig) - return tx + return trx } func (ti *txIterator) OnProcessed(_ thor.Bytes32, _ error) { diff --git a/runtime/resolved_tx_test.go b/runtime/resolved_tx_test.go index 5a2a62f62..37a0e7425 100644 --- a/runtime/resolved_tx_test.go +++ b/runtime/resolved_tx_test.go @@ -12,7 +12,6 @@ import ( "testing" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/stretchr/testify/assert" "github.com/vechain/thor/v2/builtin" @@ -187,6 +186,5 @@ func txBuilder(tag byte) *tx.Builder { func txSign(builder *tx.Builder) *tx.Transaction { transaction := builder.Build() - sig, _ := crypto.Sign(transaction.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - return transaction.WithSignature(sig) + return tx.MustSign(transaction, genesis.DevAccounts()[0].PrivateKey) } diff --git a/tx/signer.go b/tx/signer.go new file mode 100644 index 000000000..c1c49e1cc --- /dev/null +++ b/tx/signer.go @@ -0,0 +1,79 @@ +// Copyright (c) 2024 The VeChainThor developers + +// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying +// file LICENSE or + +package tx + +import ( + "crypto/ecdsa" + "fmt" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + "github.com/vechain/thor/v2/thor" +) + +// MustSign signs a transaction using the provided private key and the default signing function. +// It panics if the signing process fails, returning a signed transaction upon success. +func MustSign(tx *Transaction, pk *ecdsa.PrivateKey) *Transaction { + trx, err := Sign(tx, pk) + if err != nil { + panic(err) + } + return trx +} + +// Sign signs a transaction using the provided private key and the default signing function. +// It returns the signed transaction or an error if the signing process fails. +func Sign(tx *Transaction, pk *ecdsa.PrivateKey) (*Transaction, error) { + // Generate the signature for the transaction's signing hash. + sig, err := crypto.Sign(tx.SigningHash().Bytes(), pk) + if err != nil { + return nil, fmt.Errorf("unable to sign transaction: %w", err) + } + + // Attach the signature to the transaction and return the signed transaction. + return tx.WithSignature(sig), nil +} + +// MustSignDelegated signs a transaction as a delegator using the provided private keys and the default signing function. +// It panics if the signing process fails, returning a signed transaction upon success. +func MustSignDelegated(tx *Transaction, originPK *ecdsa.PrivateKey, delegatorPK *ecdsa.PrivateKey) *Transaction { + trx, err := SignDelegated(tx, originPK, delegatorPK) + if err != nil { + panic(err) + } + return trx +} + +// SignDelegated signs a transaction with both origin and delegator's private key and the default signing function. +// It returns the signed transaction or an error if the signing process fails. +func SignDelegated(tx *Transaction, originPK *ecdsa.PrivateKey, delegatorPK *ecdsa.PrivateKey) (*Transaction, error) { + // Ensure the transaction has the delegated feature enabled. + if !tx.Features().IsDelegated() { + return nil, errors.New("transaction delegated feature is not enabled") + } + + // Sign the transaction using the origin's private key. + // Generate the signature for the transaction's signing hash. + originSig, err := crypto.Sign(tx.SigningHash().Bytes(), originPK) + if err != nil { + return nil, fmt.Errorf("unable to sign transaction: %w", err) + } + + // Convert the origin's public key to its corresponding address. + origin := thor.Address(crypto.PubkeyToAddress(originPK.PublicKey)) + + // Generate the delegator's signature using the transaction's delegator signing hash. + dSig, err := crypto.Sign(tx.DelegatorSigningHash(origin).Bytes(), delegatorPK) + if err != nil { + return nil, fmt.Errorf("unable to delegator sign transaction: %w", err) + } + + // Append the delegator's signature to the origin's signature. + sig := append(originSig, dSig...) + + // Attach the combined signature to the transaction and return the signed transaction. + return tx.WithSignature(sig), nil +} diff --git a/tx/signer_test.go b/tx/signer_test.go new file mode 100644 index 000000000..5a11fa62c --- /dev/null +++ b/tx/signer_test.go @@ -0,0 +1,76 @@ +// Copyright (c) 2024 The VeChainThor developers + +// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying +// file LICENSE or + +package tx + +import ( + "testing" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vechain/thor/v2/thor" +) + +func TestSign(t *testing.T) { + // Generate a new private key for testing + pk, err := crypto.GenerateKey() + assert.NoError(t, err) + + tx := new(Builder).Build() + + // Sign the transaction + signedTx, err := Sign(tx, pk) + assert.NoError(t, err) + + // Verify the transaction was signed + assert.NotNil(t, signedTx) + + // Verify address from Origin + addr, err := signedTx.Origin() + require.NoError(t, err) + assert.Equal(t, thor.Address(crypto.PubkeyToAddress(pk.PublicKey)), addr) + + // Verify the delegator + delegator, err := signedTx.Delegator() + require.NoError(t, err) + assert.Nil(t, delegator) +} + +func TestSignDelegated(t *testing.T) { + // Generate a new private key for testing + delegatorPK, err := crypto.GenerateKey() + assert.NoError(t, err) + + originPK, err := crypto.GenerateKey() + assert.NoError(t, err) + + tx := new(Builder).Build() + + // Feature not enabled + signedTx, err := SignDelegated(tx, originPK, delegatorPK) + assert.ErrorContains(t, err, "transaction delegated feature is not enabled") + assert.Nil(t, signedTx) + + // enable the feature + var features Features + features.SetDelegated(true) + tx = new(Builder).Features(features).Build() + + // Sign the transaction as a delegator + signedTx, err = SignDelegated(tx, originPK, delegatorPK) + assert.NoError(t, err) + assert.NotNil(t, signedTx) + + // Verify address from Origin + origin, err := signedTx.Origin() + require.NoError(t, err) + assert.Equal(t, thor.Address(crypto.PubkeyToAddress(originPK.PublicKey)), origin) + + // Verify the delegator + delegator, err := signedTx.Delegator() + require.NoError(t, err) + assert.Equal(t, thor.Address(crypto.PubkeyToAddress(delegatorPK.PublicKey)), *delegator) +} diff --git a/txpool/tx_object_test.go b/txpool/tx_object_test.go index fd384fdcb..5eb90d78a 100644 --- a/txpool/tx_object_test.go +++ b/txpool/tx_object_test.go @@ -11,7 +11,6 @@ import ( "math/rand" "testing" - "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" "github.com/vechain/thor/v2/block" "github.com/vechain/thor/v2/chain" @@ -29,25 +28,21 @@ func newChainRepo(db *muxdb.MuxDB) *chain.Repository { return repo } -func signTx(tx *tx.Transaction, acc genesis.DevAccount) *tx.Transaction { - sig, _ := crypto.Sign(tx.SigningHash().Bytes(), acc.PrivateKey) - return tx.WithSignature(sig) -} - func newTx(chainTag byte, clauses []*tx.Clause, gas uint64, blockRef tx.BlockRef, expiration uint32, dependsOn *thor.Bytes32, features tx.Features, from genesis.DevAccount) *tx.Transaction { builder := new(tx.Builder).ChainTag(chainTag) for _, c := range clauses { builder.Clause(c) } - tx := builder.BlockRef(blockRef). + return tx.MustSign(builder.BlockRef(blockRef). Expiration(expiration). Nonce(rand.Uint64()). // nolint:gosec DependsOn(dependsOn). Features(features). - Gas(gas).Build() - - return signTx(tx, from) + Gas(gas). + Build(), + from.PrivateKey, + ) } func newDelegatedTx(chainTag byte, clauses []*tx.Clause, gas uint64, blockRef tx.BlockRef, expiration uint32, dependsOn *thor.Bytes32, from genesis.DevAccount, delegator genesis.DevAccount) *tx.Transaction { @@ -59,18 +54,21 @@ func newDelegatedTx(chainTag byte, clauses []*tx.Clause, gas uint64, blockRef tx var features tx.Features features.SetDelegated(true) - tx := builder.BlockRef(blockRef). + trx := builder.BlockRef(blockRef). Expiration(expiration). Nonce(rand.Uint64()). // nolint:gosec DependsOn(dependsOn). Features(features). - Gas(gas).Build() + Gas(gas). + Build() - sig, _ := crypto.Sign(tx.SigningHash().Bytes(), from.PrivateKey) - dSig, _ := crypto.Sign(tx.DelegatorSigningHash(from.Address).Bytes(), delegator.PrivateKey) + trx = tx.MustSignDelegated( + trx, + from.PrivateKey, + delegator.PrivateKey, + ) - sig = append(sig, dSig...) - return tx.WithSignature(sig) + return trx } func SetupTest() (genesis.DevAccount, *chain.Repository, *block.Block, *state.State) {