diff --git a/build/transaction.go b/build/transaction.go index 88e17ab4c9..29926d7f28 100644 --- a/build/transaction.go +++ b/build/transaction.go @@ -57,7 +57,7 @@ func (b *TransactionBuilder) Mutate(muts ...TransactionMutator) error { // Hash returns the hash of this builder's transaction. func (b *TransactionBuilder) Hash() ([32]byte, error) { - return network.HashTransaction(b.TX, b.NetworkPassphrase) + return network.HashTransaction(*b.TX, b.NetworkPassphrase) } // HashHex returns the hex-encoded hash of this builder's transaction diff --git a/exp/services/webauth/internal/serve/challenge_test.go b/exp/services/webauth/internal/serve/challenge_test.go index 3dfee4865c..9c56f28047 100644 --- a/exp/services/webauth/internal/serve/challenge_test.go +++ b/exp/services/webauth/internal/serve/challenge_test.go @@ -57,7 +57,7 @@ func TestChallenge(t *testing.T) { assert.Equal(t, xdr.OperationTypeManageData, tx.Operations()[0].Body.Type) assert.Regexp(t, "^testserver auth", tx.Operations()[0].Body.ManageDataOp.DataName) - hash, err := network.HashTransaction(&tx.V1.Tx, res.NetworkPassphrase) + hash, err := network.HashTransactionInEnvelope(tx, res.NetworkPassphrase) require.NoError(t, err) assert.NoError(t, serverKey.FromAddress().Verify(hash[:], tx.V1.Signatures[0].Signature)) diff --git a/exp/services/webauth/internal/serve/token_test.go b/exp/services/webauth/internal/serve/token_test.go index e2d1561b73..bca90b1fdf 100644 --- a/exp/services/webauth/internal/serve/token_test.go +++ b/exp/services/webauth/internal/serve/token_test.go @@ -49,7 +49,7 @@ func TestToken_formInputSuccess(t *testing.T) { var tx xdr.TransactionEnvelope err = xdr.SafeUnmarshalBase64(chTx, &tx) require.NoError(t, err) - hash, err := network.HashTransaction(&tx.V1.Tx, network.TestNetworkPassphrase) + hash, err := network.HashTransactionInEnvelope(tx, network.TestNetworkPassphrase) require.NoError(t, err) sigDec, err := account.SignDecorated(hash[:]) require.NoError(t, err) @@ -149,7 +149,7 @@ func TestToken_jsonInputSuccess(t *testing.T) { var tx xdr.TransactionEnvelope err = xdr.SafeUnmarshalBase64(chTx, &tx) require.NoError(t, err) - hash, err := network.HashTransaction(&tx.V1.Tx, network.TestNetworkPassphrase) + hash, err := network.HashTransactionInEnvelope(tx, network.TestNetworkPassphrase) require.NoError(t, err) sigDec, err := account.SignDecorated(hash[:]) require.NoError(t, err) @@ -260,7 +260,7 @@ func TestToken_jsonInputValidMultipleSigners(t *testing.T) { var tx xdr.TransactionEnvelope err = xdr.SafeUnmarshalBase64(chTx, &tx) require.NoError(t, err) - hash, err := network.HashTransaction(&tx.V1.Tx, network.TestNetworkPassphrase) + hash, err := network.HashTransactionInEnvelope(tx, network.TestNetworkPassphrase) require.NoError(t, err) sigDec1, err := accountSigner1.SignDecorated(hash[:]) require.NoError(t, err) @@ -371,7 +371,7 @@ func TestToken_jsonInputNotEnoughWeight(t *testing.T) { var tx xdr.TransactionEnvelope err = xdr.SafeUnmarshalBase64(chTx, &tx) require.NoError(t, err) - hash, err := network.HashTransaction(&tx.V1.Tx, network.TestNetworkPassphrase) + hash, err := network.HashTransactionInEnvelope(tx, network.TestNetworkPassphrase) require.NoError(t, err) sigDec, err := account.SignDecorated(hash[:]) require.NoError(t, err) @@ -454,7 +454,7 @@ func TestToken_jsonInputUnrecognizedSigner(t *testing.T) { var tx xdr.TransactionEnvelope err = xdr.SafeUnmarshalBase64(chTx, &tx) require.NoError(t, err) - hash, err := network.HashTransaction(&tx.V1.Tx, network.TestNetworkPassphrase) + hash, err := network.HashTransactionInEnvelope(tx, network.TestNetworkPassphrase) require.NoError(t, err) sigDec, err := account.SignDecorated(hash[:]) require.NoError(t, err) @@ -537,7 +537,7 @@ func TestToken_jsonInputAccountNotExistSuccess(t *testing.T) { var tx xdr.TransactionEnvelope err = xdr.SafeUnmarshalBase64(chTx, &tx) require.NoError(t, err) - hash, err := network.HashTransaction(&tx.V1.Tx, network.TestNetworkPassphrase) + hash, err := network.HashTransactionInEnvelope(tx, network.TestNetworkPassphrase) require.NoError(t, err) sigDec, err := account.SignDecorated(hash[:]) require.NoError(t, err) @@ -641,7 +641,7 @@ func TestToken_jsonInputAccountNotExistFail(t *testing.T) { var tx xdr.TransactionEnvelope err = xdr.SafeUnmarshalBase64(chTx, &tx) require.NoError(t, err) - hash, err := network.HashTransaction(&tx.V1.Tx, network.TestNetworkPassphrase) + hash, err := network.HashTransactionInEnvelope(tx, network.TestNetworkPassphrase) require.NoError(t, err) sigDec, err := otherSigner.SignDecorated(hash[:]) require.NoError(t, err) @@ -720,7 +720,7 @@ func TestToken_jsonInputAccountNotExistNotAllowed(t *testing.T) { var tx xdr.TransactionEnvelope err = xdr.SafeUnmarshalBase64(chTx, &tx) require.NoError(t, err) - hash, err := network.HashTransaction(&tx.V1.Tx, network.TestNetworkPassphrase) + hash, err := network.HashTransactionInEnvelope(tx, network.TestNetworkPassphrase) require.NoError(t, err) sigDec, err := account.SignDecorated(hash[:]) require.NoError(t, err) diff --git a/network/main.go b/network/main.go index ef1e92f457..76c21d8c32 100644 --- a/network/main.go +++ b/network/main.go @@ -26,14 +26,34 @@ func ID(passphrase string) [32]byte { return hash.Hash([]byte(passphrase)) } +// HashTransactionInEnvelope derives the network specific hash for the transaction +// contained in the provided envelope using the network identified by the supplied passphrase. +// The resulting hash is the value that can be signed by stellar secret key to +// authorize the transaction identified by the hash to stellar validators. +func HashTransactionInEnvelope(envelope xdr.TransactionEnvelope, passphrase string) ([32]byte, error) { + var hash [32]byte + var err error + switch envelope.Type { + case xdr.EnvelopeTypeEnvelopeTypeTx: + hash, err = HashTransaction(envelope.V1.Tx, passphrase) + case xdr.EnvelopeTypeEnvelopeTypeTxV0: + hash, err = HashTransactionV0(envelope.V0.Tx, passphrase) + case xdr.EnvelopeTypeEnvelopeTypeTxFeeBump: + hash, err = HashFeeBumpTransaction(envelope.FeeBump.Tx, passphrase) + default: + err = errors.New("invalid transaction type") + } + return hash, err +} + // HashTransaction derives the network specific hash for the provided // transaction using the network identified by the supplied passphrase. The // resulting hash is the value that can be signed by stellar secret key to // authorize the transaction identified by the hash to stellar validators. -func HashTransaction(tx *xdr.Transaction, passphrase string) ([32]byte, error) { +func HashTransaction(tx xdr.Transaction, passphrase string) ([32]byte, error) { taggedTx := xdr.TransactionSignaturePayloadTaggedTransaction{ Type: xdr.EnvelopeTypeEnvelopeTypeTx, - Tx: tx, + Tx: &tx, } return hashTx(taggedTx, passphrase) } @@ -42,10 +62,10 @@ func HashTransaction(tx *xdr.Transaction, passphrase string) ([32]byte, error) { // fee bump transaction using the network identified by the supplied passphrase. The // resulting hash is the value that can be signed by stellar secret key to // authorize the transaction identified by the hash to stellar validators. -func HashFeeBumpTransaction(tx *xdr.FeeBumpTransaction, passphrase string) ([32]byte, error) { +func HashFeeBumpTransaction(tx xdr.FeeBumpTransaction, passphrase string) ([32]byte, error) { taggedTx := xdr.TransactionSignaturePayloadTaggedTransaction{ Type: xdr.EnvelopeTypeEnvelopeTypeTxFeeBump, - FeeBump: tx, + FeeBump: &tx, } return hashTx(taggedTx, passphrase) } @@ -54,12 +74,12 @@ func HashFeeBumpTransaction(tx *xdr.FeeBumpTransaction, passphrase string) ([32] // legacy transaction using the network identified by the supplied passphrase. The // resulting hash is the value that can be signed by stellar secret key to // authorize the transaction identified by the hash to stellar validators. -func HashTransactionV0(tx *xdr.TransactionV0, passphrase string) ([32]byte, error) { +func HashTransactionV0(tx xdr.TransactionV0, passphrase string) ([32]byte, error) { sa, err := xdr.NewMuxedAccount(xdr.CryptoKeyTypeKeyTypeEd25519, tx.SourceAccountEd25519) if err != nil { return [32]byte{}, err } - v1Tx := &xdr.Transaction{ + v1Tx := xdr.Transaction{ SourceAccount: sa, Fee: tx.Fee, Memo: tx.Memo, diff --git a/network/main_test.go b/network/main_test.go index 5397964858..4bfa499a18 100644 --- a/network/main_test.go +++ b/network/main_test.go @@ -20,11 +20,17 @@ func TestHashTransaction(t *testing.T) { 0xfd, 0x16, 0x2b, 0x03, 0x10, 0x64, 0x09, 0x8a, 0x0d, 0x78, 0x6b, 0x6e, 0x0a, 0x00, 0xfd, 0x74, } - actual, err := HashTransactionV0(&txe.V0.Tx, TestNetworkPassphrase) + actual, err := HashTransactionV0(txe.V0.Tx, TestNetworkPassphrase) assert.NoError(t, err) assert.Equal(t, expected, actual) - _, err = HashTransactionV0(&txe.V0.Tx, "") + actual, err = HashTransactionInEnvelope(txe, TestNetworkPassphrase) + assert.NoError(t, err) + assert.Equal(t, expected, actual) + + _, err = HashTransactionV0(txe.V0.Tx, "") + assert.Contains(t, err.Error(), "empty network passphrase") + _, err = HashTransactionInEnvelope(txe, "") assert.Contains(t, err.Error(), "empty network passphrase") tx := xdr.Transaction{ @@ -35,12 +41,23 @@ func TestHashTransaction(t *testing.T) { SeqNum: xdr.SequenceNumber(txe.SeqNum()), TimeBounds: txe.TimeBounds(), } - actual, err = HashTransaction(&tx, TestNetworkPassphrase) + actual, err = HashTransaction(tx, TestNetworkPassphrase) + assert.NoError(t, err) + assert.Equal(t, expected, actual) + + txe.Type = xdr.EnvelopeTypeEnvelopeTypeTx + txe.V0 = nil + txe.V1 = &xdr.TransactionV1Envelope{ + Tx: tx, + } + actual, err = HashTransactionInEnvelope(txe, TestNetworkPassphrase) assert.NoError(t, err) assert.Equal(t, expected, actual) // sadpath: empty passphrase - _, err = HashTransaction(&tx, "") + _, err = HashTransaction(tx, "") + assert.Contains(t, err.Error(), "empty network passphrase") + _, err = HashTransactionInEnvelope(txe, "") assert.Contains(t, err.Error(), "empty network passphrase") feeBumpTx := xdr.FeeBumpTransaction{ @@ -61,10 +78,21 @@ func TestHashTransaction(t *testing.T) { 0x32, 0x51, 0x72, 0x46, 0xd9, 0xfc, 0x23, 0xff, 0x8b, 0x7a, 0x85, 0xdd, 0x4b, 0xbc, 0xef, 0x5f, } - actual, err = HashFeeBumpTransaction(&feeBumpTx, TestNetworkPassphrase) + actual, err = HashFeeBumpTransaction(feeBumpTx, TestNetworkPassphrase) assert.NoError(t, err) assert.Equal(t, expected, actual) - _, err = HashFeeBumpTransaction(&feeBumpTx, "") + txe.Type = xdr.EnvelopeTypeEnvelopeTypeTxFeeBump + txe.V1 = nil + txe.FeeBump = &xdr.FeeBumpTransactionEnvelope{ + Tx: feeBumpTx, + } + actual, err = HashTransactionInEnvelope(txe, TestNetworkPassphrase) + assert.NoError(t, err) + assert.Equal(t, expected, actual) + + _, err = HashFeeBumpTransaction(feeBumpTx, "") + assert.Contains(t, err.Error(), "empty network passphrase") + _, err = HashTransactionInEnvelope(txe, "") assert.Contains(t, err.Error(), "empty network passphrase") } diff --git a/services/bridge/internal/submitter/transaction_submitter.go b/services/bridge/internal/submitter/transaction_submitter.go index d53e060b7e..c611b55783 100644 --- a/services/bridge/internal/submitter/transaction_submitter.go +++ b/services/bridge/internal/submitter/transaction_submitter.go @@ -128,7 +128,7 @@ func (ts *TransactionSubmitter) SignAndSubmitRawTransaction(paymentID *string, s tx.SeqNum = xdr.SequenceNumber(account.SequenceNumber) account.Mutex.Unlock() - hash, err := network.HashTransaction(tx, ts.Network) + hash, err := network.HashTransaction(*tx, ts.Network) if err != nil { ts.log.WithFields(logrus.Fields{"err": err}).Error("Error calculating transaction hash") return @@ -154,7 +154,7 @@ func (ts *TransactionSubmitter) SignAndSubmitRawTransaction(paymentID *string, s return } - transactionHashBytes, err := network.HashTransaction(tx, ts.Network) + transactionHashBytes, err := network.HashTransaction(*tx, ts.Network) if err != nil { ts.log.WithFields(logrus.Fields{"err": err}).Warn("Error calculating tx hash") return diff --git a/services/compliance/internal/handlers/request_handler_auth.go b/services/compliance/internal/handlers/request_handler_auth.go index 7d610ad59b..a8492bc661 100644 --- a/services/compliance/internal/handlers/request_handler_auth.go +++ b/services/compliance/internal/handlers/request_handler_auth.go @@ -136,7 +136,7 @@ func (rh *RequestHandler) HandlerAuth(w http.ResponseWriter, r *http.Request) { return } - transactionHash, err := network.HashTransaction(&tx, rh.Config.NetworkPassphrase) + transactionHash, err := network.HashTransaction(tx, rh.Config.NetworkPassphrase) if err != nil { log.WithFields(log.Fields{"err": err}).Warn("Error calculating tx hash") httpHelpers.Write(w, httpHelpers.InternalServerError) diff --git a/services/horizon/internal/actions_transaction.go b/services/horizon/internal/actions_transaction.go index 01710635ca..f7b5f6014a 100644 --- a/services/horizon/internal/actions_transaction.go +++ b/services/horizon/internal/actions_transaction.go @@ -10,7 +10,6 @@ import ( hProblem "github.com/stellar/go/services/horizon/internal/render/problem" "github.com/stellar/go/services/horizon/internal/resourceadapter" "github.com/stellar/go/services/horizon/internal/txsub" - "github.com/stellar/go/support/errors" "github.com/stellar/go/support/render/hal" "github.com/stellar/go/support/render/problem" "github.com/stellar/go/xdr" @@ -33,20 +32,10 @@ func extractEnvelopeInfo(raw string, passphrase string) (envelopeInfo, error) { } var hash [32]byte - switch result.parsed.Type { - case xdr.EnvelopeTypeEnvelopeTypeTx: - hash, err = network.HashTransaction(&result.parsed.V1.Tx, passphrase) - case xdr.EnvelopeTypeEnvelopeTypeTxV0: - hash, err = network.HashTransactionV0(&result.parsed.V0.Tx, passphrase) - case xdr.EnvelopeTypeEnvelopeTypeTxFeeBump: - hash, err = network.HashFeeBumpTransaction(&result.parsed.FeeBump.Tx, passphrase) - default: - return result, errors.New("invalid transaction type") - } + hash, err = network.HashTransactionInEnvelope(result.parsed, passphrase) if err != nil { return result, err } - result.hash = hex.EncodeToString(hash[:]) return result, nil } diff --git a/services/horizon/internal/db2/history/fee_bump_scenario.go b/services/horizon/internal/db2/history/fee_bump_scenario.go index 2380e754ff..f97b28bd3b 100644 --- a/services/horizon/internal/db2/history/fee_bump_scenario.go +++ b/services/horizon/internal/db2/history/fee_bump_scenario.go @@ -175,14 +175,14 @@ func FeeBumpScenario(tt *test.T, q *Q, successful bool) FeeBumpFixture { tt.Assert.NoError(err) innerHash, err := network.HashTransaction( - &fixture.Envelope.FeeBump.Tx.InnerTx.V1.Tx, + fixture.Envelope.FeeBump.Tx.InnerTx.V1.Tx, "Test SDF Network ; September 2015", ) tt.Assert.NoError(err) fixture.InnerHash = hex.EncodeToString(innerHash[:]) outerHash, err := network.HashFeeBumpTransaction( - &fixture.Envelope.FeeBump.Tx, + fixture.Envelope.FeeBump.Tx, "Test SDF Network ; September 2015", ) tt.Assert.NoError(err) diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index e57eaa4a90..9a655ceed0 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -51,7 +51,7 @@ type Transaction struct { // Hash provides a signable object representing the Transaction on the specified network. func (tx *Transaction) Hash() ([32]byte, error) { - return network.HashTransaction(&tx.xdrTransaction, tx.Network) + return network.HashTransaction(tx.xdrTransaction, tx.Network) } // MarshalBinary returns the binary XDR representation of the transaction envelope.