Skip to content

Commit

Permalink
network: Add network.HashTransactionInEnvelope() (#2490)
Browse files Browse the repository at this point in the history
* Add network.HashTransactionInEnvelope() for hashing the transaction contained in a given tx envelope

* Make the the hash functions take a transaction value parameter instead of a pointer to a transaction
  • Loading branch information
tamirms authored Apr 17, 2020
1 parent 3bdf862 commit 1897d39
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 40 deletions.
2 changes: 1 addition & 1 deletion build/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion exp/services/webauth/internal/serve/challenge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
16 changes: 8 additions & 8 deletions exp/services/webauth/internal/serve/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
32 changes: 26 additions & 6 deletions network/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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,
Expand Down
40 changes: 34 additions & 6 deletions network/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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{
Expand All @@ -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")
}
4 changes: 2 additions & 2 deletions services/bridge/internal/submitter/transaction_submitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 1 addition & 12 deletions services/horizon/internal/actions_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions services/horizon/internal/db2/history/fee_bump_scenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion txnbuild/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit 1897d39

Please sign in to comment.