From 14f8088fdd40ff78c58f9493f8ef5db5bd6ee380 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Thu, 16 Apr 2020 15:45:03 +0200 Subject: [PATCH] all: Implement CAP 27 and SEP 23 This doesn't provide a full integration with CAP27 though: * The ingestion code will simply transform MuxedAccounts into AccountIds (by dropping the memo ID). This means that queries will only show AccountId G-strkeys. * The POST transaction endpoint will accept transactions using CAP27s XDR, so clients need to be adapted to that. --- build/account_merge.go | 4 +- build/account_merge_test.go | 14 +- build/asset_test.go | 2 +- build/operation.go | 4 +- build/payment.go | 4 +- build/set_options_test.go | 2 +- build/transaction.go | 4 +- build/util.go | 13 + main_test.go | 4 +- network/main.go | 19 +- .../internal/actions_transaction_test.go | 74 ++++- .../horizon/internal/db2/core/transaction.go | 2 +- .../internal/db2/core/transaction_test.go | 30 ++ .../internal/db2/history/fee_bump_scenario.go | 12 +- .../transaction_batch_insert_builder.go | 2 +- .../transaction_batch_insert_builder_test.go | 65 +++++ .../internal/expingest/fake_ledger_backend.go | 2 +- .../expingest/processors/effects_processor.go | 64 ++--- .../processors/effects_processor_test.go | 262 +++++++++++++----- .../processors/ledgers_processor_test.go | 2 +- .../processors/operations_processor.go | 25 +- .../processors/operations_processor_test.go | 55 ++++ .../processors/participants_processor.go | 2 +- .../processors/participants_processor_test.go | 91 +++--- .../expingest/processors/trades_processor.go | 5 +- .../processors/trades_processor_test.go | 171 +++++++----- .../transaction_operation_wrapper_test.go | 6 +- services/horizon/internal/txsub/system.go | 5 +- .../horizon/internal/txsub/system_test.go | 70 ++++- strkey/decode_test.go | 74 +++++ strkey/encode_test.go | 12 + strkey/main.go | 56 +++- txnbuild/account_merge.go | 2 +- txnbuild/asset_test.go | 4 +- txnbuild/operation.go | 4 +- txnbuild/operation_test.go | 17 +- txnbuild/path_payment.go | 2 +- txnbuild/path_payment_strict_send.go | 2 +- txnbuild/payment.go | 6 +- txnbuild/transaction.go | 12 + xdr/Stellar-transaction.x | 43 +-- xdr/Stellar-types.x | 1 + xdr/account_id_test.go | 18 +- xdr/asset.go | 7 +- xdr/muxed_account.go | 148 ++++++++++ xdr/muxed_account_test.go | 154 ++++++++++ xdr/transaction_envelope.go | 6 +- xdr/transaction_envelope_test.go | 16 +- xdr/xdr_generated.go | 226 ++++++++++++--- 49 files changed, 1460 insertions(+), 365 deletions(-) create mode 100644 services/horizon/internal/db2/history/transaction_batch_insert_builder_test.go create mode 100644 xdr/muxed_account.go create mode 100644 xdr/muxed_account_test.go diff --git a/build/account_merge.go b/build/account_merge.go index 0a30631ce0..67ae757e9e 100644 --- a/build/account_merge.go +++ b/build/account_merge.go @@ -23,7 +23,7 @@ type AccountMergeMutator interface { // Deprecated use txnbuild.AccountMerge instead type AccountMergeBuilder struct { O xdr.Operation - Destination xdr.AccountId + Destination xdr.MuxedAccount Err error } @@ -49,5 +49,5 @@ func (b *AccountMergeBuilder) Mutate(muts ...interface{}) { // MutateAccountMerge for Destination sets the AccountMergeBuilder's Destination field func (m Destination) MutateAccountMerge(o *AccountMergeBuilder) error { - return setAccountId(m.AddressOrSeed, &o.Destination) + return setMuxedAccount(m.AddressOrSeed, &o.Destination) } diff --git a/build/account_merge_test.go b/build/account_merge_test.go index 85f64157d4..25328115dc 100644 --- a/build/account_merge_test.go +++ b/build/account_merge_test.go @@ -29,10 +29,10 @@ var _ = Describe("AccountMergeBuilder Mutators", func() { Expect(subject.Err).NotTo(HaveOccurred()) }) - It("sets the destination to the correct xdr.AccountId", func() { - var aid xdr.AccountId - aid.SetAddress(address) - Expect(subject.Destination.Equals(aid)).To(BeTrue()) + It("sets the destination to the correct xdr.MuxedAccount", func() { + var muxed xdr.MuxedAccount + muxed.SetAddress(address) + Expect(subject.Destination.Equals(muxed)).To(BeTrue()) }) }) @@ -51,9 +51,9 @@ var _ = Describe("AccountMergeBuilder Mutators", func() { }) It("sets the destination to the correct xdr.AccountId", func() { - var aid xdr.AccountId - aid.SetAddress(address) - Expect(subject.O.SourceAccount.Equals(aid)).To(BeTrue()) + var muxed xdr.MuxedAccount + muxed.SetAddress(address) + Expect(subject.O.SourceAccount.Equals(muxed)).To(BeTrue()) }) }) diff --git a/build/asset_test.go b/build/asset_test.go index f4bc65f811..96c4f72403 100644 --- a/build/asset_test.go +++ b/build/asset_test.go @@ -55,7 +55,7 @@ func TestAsset_ToXDR(t *testing.T) { { Name: "bad issuer", Asset: CreditAsset("USD", "FUNK"), - ExpectedErr: "base32 decode failed: illegal base32 data at input byte 0", + ExpectedErr: "strkey is 4 bytes long; minimum valid length is 5", }, { Name: "bad code", diff --git a/build/operation.go b/build/operation.go index 672b0f26c3..7a7bfe4014 100644 --- a/build/operation.go +++ b/build/operation.go @@ -14,6 +14,6 @@ type OperationMutator interface { // MutateOperation for SourceAccount sets the operation's SourceAccount // to the pubilic key for the address provided func (m SourceAccount) MutateOperation(o *xdr.Operation) error { - o.SourceAccount = &xdr.AccountId{} - return setAccountId(m.AddressOrSeed, o.SourceAccount) + o.SourceAccount = &xdr.MuxedAccount{} + return setMuxedAccount(m.AddressOrSeed, o.SourceAccount) } diff --git a/build/payment.go b/build/payment.go index aa93bc9067..26c8e6b087 100644 --- a/build/payment.go +++ b/build/payment.go @@ -90,9 +90,9 @@ func (m Destination) MutatePayment(o interface{}) error { default: return errors.New("Unexpected operation type") case *xdr.PaymentOp: - return setAccountId(m.AddressOrSeed, &o.Destination) + return setMuxedAccount(m.AddressOrSeed, &o.Destination) case *xdr.PathPaymentStrictReceiveOp: - return setAccountId(m.AddressOrSeed, &o.Destination) + return setMuxedAccount(m.AddressOrSeed, &o.Destination) } } diff --git a/build/set_options_test.go b/build/set_options_test.go index a444b0d448..17e705c244 100644 --- a/build/set_options_test.go +++ b/build/set_options_test.go @@ -35,7 +35,7 @@ func TestSetOptions_Signer(t *testing.T) { Name: "Bad", Address: "foo", Weight: 1, - Error: "base32 decode failed", + Error: "minimum valid length is 5", }, } diff --git a/build/transaction.go b/build/transaction.go index 3bdd947ca0..88e17ab4c9 100644 --- a/build/transaction.go +++ b/build/transaction.go @@ -139,7 +139,7 @@ func (m AllowTrustBuilder) MutateTransaction(o *TransactionBuilder) error { func (m AutoSequence) MutateTransaction(o *TransactionBuilder) error { source := o.TX.SourceAccount - if source == (xdr.AccountId{}) { + if source == (xdr.MuxedAccount{}) { return errors.New("auto sequence used prior to setting source account") } @@ -334,7 +334,7 @@ func (m Sequence) MutateTransaction(o *TransactionBuilder) error { // MutateTransaction for SourceAccount sets the transaction's SourceAccount // to the pubilic key for the address provided func (m SourceAccount) MutateTransaction(o *TransactionBuilder) error { - return setAccountId(m.AddressOrSeed, &o.TX.SourceAccount) + return setMuxedAccount(m.AddressOrSeed, &o.TX.SourceAccount) } // MutateTransaction for BaseFee sets the base fee diff --git a/build/util.go b/build/util.go index dcb3d1aa0c..66b0bb40fd 100644 --- a/build/util.go +++ b/build/util.go @@ -19,6 +19,19 @@ func setAccountId(addressOrSeed string, aid *xdr.AccountId) error { return aid.SetAddress(kp.Address()) } +func setMuxedAccount(addressOrSeed string, m *xdr.MuxedAccount) error { + kp, err := keypair.Parse(addressOrSeed) + if err != nil { + return err + } + + if m == nil { + return errors.New("m is nil in setMuxedAccount") + } + + return m.SetAddress(kp.Address()) +} + func createAlphaNumAsset(code, issuerAccountId string) (xdr.Asset, error) { var issuer xdr.AccountId err := setAccountId(issuerAccountId, &issuer) diff --git a/main_test.go b/main_test.go index 1f3da6663e..7af5a9053c 100644 --- a/main_test.go +++ b/main_test.go @@ -78,7 +78,7 @@ func ExampleLowLevelTransaction() { panic(err) } - var destination xdr.AccountId + var destination xdr.MuxedAccount err = destination.SetAddress(dkp.Address()) if err != nil { panic(err) @@ -92,7 +92,7 @@ func ExampleLowLevelTransaction() { memo, err := xdr.NewMemo(xdr.MemoTypeMemoNone, nil) - var source xdr.AccountId + var source xdr.MuxedAccount err = source.SetAddress(skp.Address()) if err != nil { panic(err) diff --git a/network/main.go b/network/main.go index f9184fb110..ef1e92f457 100644 --- a/network/main.go +++ b/network/main.go @@ -55,16 +55,17 @@ func HashFeeBumpTransaction(tx *xdr.FeeBumpTransaction, passphrase string) ([32] // 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) { + sa, err := xdr.NewMuxedAccount(xdr.CryptoKeyTypeKeyTypeEd25519, tx.SourceAccountEd25519) + if err != nil { + return [32]byte{}, err + } v1Tx := &xdr.Transaction{ - SourceAccount: xdr.AccountId{ - Type: xdr.PublicKeyTypePublicKeyTypeEd25519, - Ed25519: &tx.SourceAccountEd25519, - }, - Fee: tx.Fee, - Memo: tx.Memo, - Operations: tx.Operations, - SeqNum: tx.SeqNum, - TimeBounds: tx.TimeBounds, + SourceAccount: sa, + Fee: tx.Fee, + Memo: tx.Memo, + Operations: tx.Operations, + SeqNum: tx.SeqNum, + TimeBounds: tx.TimeBounds, } return HashTransaction(v1Tx, passphrase) } diff --git a/services/horizon/internal/actions_transaction_test.go b/services/horizon/internal/actions_transaction_test.go index 12dc761e0d..4f3d984f6c 100644 --- a/services/horizon/internal/actions_transaction_test.go +++ b/services/horizon/internal/actions_transaction_test.go @@ -5,6 +5,8 @@ import ( "net/url" "testing" + "github.com/stretchr/testify/assert" + "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/services/horizon/internal/db2/history" "github.com/stellar/go/services/horizon/internal/expingest" @@ -240,7 +242,37 @@ func TestTransactionActions_Post(t *testing.T) { ht := StartHTTPTest(t, "base") defer ht.Finish() - form := url.Values{"tx": []string{"AAAAAGL8HQvQkbK2HA3WVjRrKmjX00fG8sLI7m0ERwJW/AX3AAAAZAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAArqN6LeOagjxMaUP96Bzfs9e0corNZXzBWJkFoK7kvkwAAAAAO5rKAAAAAAAAAAABVvwF9wAAAECDzqvkQBQoNAJifPRXDoLhvtycT3lFPCQ51gkdsFHaBNWw05S/VhW0Xgkr0CBPE4NaFV2Kmcs3ZwLmib4TRrML"}} + tx := xdr.TransactionEnvelope{ + Type: xdr.EnvelopeTypeEnvelopeTypeTxV0, + V0: &xdr.TransactionV0Envelope{ + Tx: xdr.TransactionV0{ + SourceAccountEd25519: *xdr.MustAddress("GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H").Ed25519, + Fee: 100, + SeqNum: 1, + Operations: []xdr.Operation{ + { + Body: xdr.OperationBody{ + Type: xdr.OperationTypeCreateAccount, + CreateAccountOp: &xdr.CreateAccountOp{ + Destination: xdr.MustAddress("GCXKG6RN4ONIEPCMNFB732A436Z5PNDSRLGWK7GBLCMQLIFO4S7EYWVU"), + StartingBalance: 1000000000, + }, + }, + }, + }, + }, + Signatures: []xdr.DecoratedSignature{ + { + Hint: xdr.SignatureHint{86, 252, 5, 247}, + Signature: xdr.Signature{131, 206, 171, 228, 64, 20, 40, 52, 2, 98, 124, 244, 87, 14, 130, 225, 190, 220, 156, 79, 121, 69, 60, 36, 57, 214, 9, 29, 176, 81, 218, 4, 213, 176, 211, 148, 191, 86, 21, 180, 94, 9, 43, 208, 32, 79, 19, 131, 90, 21, 93, 138, 153, 203, 55, 103, 2, 230, 137, 190, 19, 70, 179, 11}, + }, + }, + }, + } + + txStr, err := xdr.MarshalBase64(tx) + assert.NoError(t, err) + form := url.Values{"tx": []string{txStr}} // existing transaction w := ht.Post("/transactions", form) @@ -260,8 +292,46 @@ func TestTransactionActions_PostSuccessful(t *testing.T) { ht := StartHTTPTest(t, "failed_transactions") defer ht.Finish() + tx2 := xdr.TransactionEnvelope{ + Type: xdr.EnvelopeTypeEnvelopeTypeTxV0, + V0: &xdr.TransactionV0Envelope{ + Tx: xdr.TransactionV0{ + SourceAccountEd25519: *xdr.MustAddress("GCXKG6RN4ONIEPCMNFB732A436Z5PNDSRLGWK7GBLCMQLIFO4S7EYWVU").Ed25519, + Fee: 100, + SeqNum: 8589934593, + Operations: []xdr.Operation{ + { + Body: xdr.OperationBody{ + Type: xdr.OperationTypePayment, + PaymentOp: &xdr.PaymentOp{ + Destination: xdr.MustMuxedAccountAddress("GBXGQJWVLWOYHFLVTKWV5FGHA3LNYY2JQKM7OAJAUEQFU6LPCSEFVXON"), + Asset: xdr.Asset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AssetAlphaNum4{ + AssetCode: xdr.AssetCode4{85, 83, 68, 0}, + Issuer: xdr.MustAddress("GCXKG6RN4ONIEPCMNFB732A436Z5PNDSRLGWK7GBLCMQLIFO4S7EYWVU"), + }, + }, + Amount: 1000000000, + }, + }, + }, + }, + }, + Signatures: []xdr.DecoratedSignature{ + { + Hint: xdr.SignatureHint{174, 228, 190, 76}, + Signature: xdr.Signature{73, 202, 13, 176, 216, 188, 169, 9, 141, 130, 180, 106, 187, 225, 22, 89, 254, 24, 173, 62, 236, 12, 186, 131, 70, 190, 214, 24, 209, 69, 233, 68, 1, 238, 48, 154, 55, 170, 53, 196, 96, 218, 110, 2, 159, 187, 120, 2, 50, 115, 2, 192, 208, 35, 72, 151, 106, 17, 155, 160, 147, 200, 52, 12}, + }, + }, + }, + } + + txStr, err := xdr.MarshalBase64(tx2) + assert.NoError(t, err) + // 56e3216045d579bea40f2d35a09406de3a894ecb5be70dbda5ec9c0427a0d5a1 - form := url.Values{"tx": []string{"AAAAAK6jei3jmoI8TGlD/egc37PXtHKKzWV8wViZBaCu5L5MAAAAZAAAAAIAAAABAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAbmgm1V2dg5V1mq1elMcG1txjSYKZ9wEgoSBaeW8UiFoAAAABVVNEAAAAAACuo3ot45qCPExpQ/3oHN+z17Ryis1lfMFYmQWgruS+TAAAAAA7msoAAAAAAAAAAAGu5L5MAAAAQEnKDbDYvKkJjYK0arvhFln+GK0+7Ay6g0a+1hjRRelEAe4wmjeqNcRg2m4Cn7t4AjJzAsDQI0iXahGboJPINAw="}} + form := url.Values{"tx": []string{txStr}} w := ht.Post("/transactions", form) ht.Assert.Equal(200, w.Code) diff --git a/services/horizon/internal/db2/core/transaction.go b/services/horizon/internal/db2/core/transaction.go index c106729244..9a7de776ae 100644 --- a/services/horizon/internal/db2/core/transaction.go +++ b/services/horizon/internal/db2/core/transaction.go @@ -125,7 +125,7 @@ func (tx *Transaction) Sequence() int64 { // SourceAddress returns the strkey-encoded account id that paid the fee for // `tx`. func (tx *Transaction) SourceAddress() string { - sa := tx.Envelope.SourceAccount() + sa := tx.Envelope.SourceAccount().ToAccountId() return sa.Address() } diff --git a/services/horizon/internal/db2/core/transaction_test.go b/services/horizon/internal/db2/core/transaction_test.go index 76b8698db6..ec5e42303e 100644 --- a/services/horizon/internal/db2/core/transaction_test.go +++ b/services/horizon/internal/db2/core/transaction_test.go @@ -3,6 +3,8 @@ package core import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/stellar/go/services/horizon/internal/test" "github.com/stellar/go/xdr" ) @@ -61,3 +63,31 @@ func TestSignatures(t *testing.T) { tt.Assert.Equal("8qkkeKaKfsbgInyIkzXJhqJE5/Ufxri2LdxmyKkgkT6I3sPmvrs5cPWQSzEQyhV750IW2ds97xTHqTpOfuZCAg==", signatures[0]) tt.Assert.Equal("", signatures[1]) } + +func TestTransaction_SourceAddress_MuxedAccount(t *testing.T) { + muxed := xdr.MustMuxedAccountAddress("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG") + var tx Transaction + tx.Envelope = xdr.TransactionEnvelope{ + Type: xdr.EnvelopeTypeEnvelopeTypeTx, + V1: &xdr.TransactionV1Envelope{ + Tx: xdr.Transaction{ + SourceAccount: muxed, + Operations: []xdr.Operation{ + { + SourceAccount: &muxed, + Body: xdr.OperationBody{ + Type: xdr.OperationTypePayment, + PaymentOp: &xdr.PaymentOp{ + Destination: muxed, + Asset: xdr.Asset{Type: xdr.AssetTypeAssetTypeNative}, + Amount: 100, + }, + }, + }, + }, + }, + }, + } + + assert.Equal(t, "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ", tx.SourceAddress()) +} diff --git a/services/horizon/internal/db2/history/fee_bump_scenario.go b/services/horizon/internal/db2/history/fee_bump_scenario.go index 0a4cbd462d..2380e754ff 100644 --- a/services/horizon/internal/db2/history/fee_bump_scenario.go +++ b/services/horizon/internal/db2/history/fee_bump_scenario.go @@ -128,8 +128,8 @@ func FeeBumpScenario(tt *test.T, q *Q, successful bool) FeeBumpFixture { Type: xdr.EnvelopeTypeEnvelopeTypeTx, V1: &xdr.TransactionV1Envelope{ Tx: xdr.Transaction{ - SourceAccount: xdr.AccountId{ - Type: xdr.PublicKeyTypePublicKeyTypeEd25519, + SourceAccount: xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeEd25519, Ed25519: &xdr.Uint256{ 3, 3, 3, }, @@ -144,7 +144,7 @@ func FeeBumpScenario(tt *test.T, q *Q, successful bool) FeeBumpFixture { MaxTime: 4, }, Operations: []xdr.Operation{ - xdr.Operation{ + { Body: xdr.OperationBody{ Type: xdr.OperationTypeBumpSequence, BumpSequenceOp: &xdr.BumpSequenceOp{ @@ -155,7 +155,7 @@ func FeeBumpScenario(tt *test.T, q *Q, successful bool) FeeBumpFixture { }, }, Signatures: []xdr.DecoratedSignature{ - xdr.DecoratedSignature{ + { Hint: xdr.SignatureHint{2, 2, 2, 2}, Signature: xdr.Signature{20, 20, 20}, }, @@ -164,7 +164,7 @@ func FeeBumpScenario(tt *test.T, q *Q, successful bool) FeeBumpFixture { }, }, Signatures: []xdr.DecoratedSignature{ - xdr.DecoratedSignature{ + { Hint: xdr.SignatureHint{3, 3, 3, 3}, Signature: xdr.Signature{30, 30, 30}, }, @@ -202,7 +202,7 @@ func FeeBumpScenario(tt *test.T, q *Q, successful bool) FeeBumpFixture { Result: xdr.InnerTransactionResultResult{ Code: xdr.TransactionResultCodeTxSuccess, Results: &[]xdr.OperationResult{ - xdr.OperationResult{ + { Tr: &xdr.OperationResultTr{ Type: xdr.OperationTypeBumpSequence, BumpSeqResult: &xdr.BumpSequenceResult{ diff --git a/services/horizon/internal/db2/history/transaction_batch_insert_builder.go b/services/horizon/internal/db2/history/transaction_batch_insert_builder.go index b8a9e21cfe..07e372b03e 100644 --- a/services/horizon/internal/db2/history/transaction_batch_insert_builder.go +++ b/services/horizon/internal/db2/history/transaction_batch_insert_builder.go @@ -148,7 +148,7 @@ func transactionToMap(transaction io.LedgerTransaction, sequence uint32) (map[st return nil, err } - sourceAccount := transaction.Envelope.SourceAccount() + sourceAccount := transaction.Envelope.SourceAccount().ToAccountId() m := map[string]interface{}{ "id": toid.New(int32(sequence), int32(transaction.Index), 0).ToInt64(), "transaction_hash": hex.EncodeToString(transaction.Result.TransactionHash[:]), diff --git a/services/horizon/internal/db2/history/transaction_batch_insert_builder_test.go b/services/horizon/internal/db2/history/transaction_batch_insert_builder_test.go new file mode 100644 index 0000000000..2a2270c75b --- /dev/null +++ b/services/horizon/internal/db2/history/transaction_batch_insert_builder_test.go @@ -0,0 +1,65 @@ +package history + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/stellar/go/exp/ingest/io" + "github.com/stellar/go/xdr" +) + +func TestTransactionToMap_muxed(t *testing.T) { + muxed := xdr.MustMuxedAccountAddress("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG") + tx := io.LedgerTransaction{ + Index: 1, + Envelope: xdr.TransactionEnvelope{ + Type: xdr.EnvelopeTypeEnvelopeTypeTx, + V1: &xdr.TransactionV1Envelope{ + Tx: xdr.Transaction{ + SourceAccount: muxed, + Operations: []xdr.Operation{ + { + SourceAccount: &muxed, + Body: xdr.OperationBody{ + Type: xdr.OperationTypePayment, + PaymentOp: &xdr.PaymentOp{ + Destination: muxed, + Asset: xdr.Asset{Type: xdr.AssetTypeAssetTypeNative}, + Amount: 100, + }, + }, + }, + }, + }, + }, + }, + Result: xdr.TransactionResultPair{ + Result: xdr.TransactionResult{ + Result: xdr.TransactionResultResult{ + InnerResultPair: &xdr.InnerTransactionResultPair{ + Result: xdr.InnerTransactionResult{ + Result: xdr.InnerTransactionResultResult{ + Results: &[]xdr.OperationResult{}, + }, + }, + }, + Results: &[]xdr.OperationResult{}, + }, + }, + }, + Meta: xdr.TransactionMeta{ + V: 1, + Operations: &[]xdr.OperationMeta{}, + V1: &xdr.TransactionMetaV1{ + TxChanges: []xdr.LedgerEntryChange{}, + Operations: []xdr.OperationMeta{}, + }, + }, + } + result, err := transactionToMap(tx, 20) + if assert.NoError(t, err) { + assert.Equal(t, "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ", result["account"]) + } + +} diff --git a/services/horizon/internal/expingest/fake_ledger_backend.go b/services/horizon/internal/expingest/fake_ledger_backend.go index 6a0dbd0900..3d23d4a7af 100644 --- a/services/horizon/internal/expingest/fake_ledger_backend.go +++ b/services/horizon/internal/expingest/fake_ledger_backend.go @@ -125,7 +125,7 @@ func (f fakeLedgerBackend) GetLedger(sequence uint32) (bool, ledgerbackend.Ledge Type: xdr.EnvelopeTypeEnvelopeTypeTx, V1: &xdr.TransactionV1Envelope{ Tx: xdr.Transaction{ - SourceAccount: xdr.MustAddress("GAHK7EEG2WWHVKDNT4CEQFZGKF2LGDSW2IVM4S5DP42RBW3K6BTODB4A"), + SourceAccount: xdr.MustMuxedAccountAddress("GAHK7EEG2WWHVKDNT4CEQFZGKF2LGDSW2IVM4S5DP42RBW3K6BTODB4A"), }, }, } diff --git a/services/horizon/internal/expingest/processors/effects_processor.go b/services/horizon/internal/expingest/processors/effects_processor.go index 70acaa58b5..e99b0d46e1 100644 --- a/services/horizon/internal/expingest/processors/effects_processor.go +++ b/services/horizon/internal/expingest/processors/effects_processor.go @@ -156,9 +156,9 @@ type effectsWrapper struct { operation *transactionOperationWrapper } -func (e *effectsWrapper) add(address string, effectType history.EffectType, details map[string]interface{}) { +func (e *effectsWrapper) add(acc xdr.AccountId, effectType history.EffectType, details map[string]interface{}) { e.effects = append(e.effects, effect{ - address: address, + address: acc.Address(), operationID: e.operation.ID(), effectType: effectType, order: uint32(len(e.effects) + 1), @@ -218,14 +218,14 @@ func (operation *transactionOperationWrapper) accountCreatedEffects() []effect { } effects.add( - op.Destination.Address(), + op.Destination, history.EffectAccountCreated, map[string]interface{}{ "starting_balance": amount.String(op.StartingBalance), }, ) effects.add( - operation.SourceAccount().Address(), + *operation.SourceAccount(), history.EffectAccountDebited, map[string]interface{}{ "asset_type": "native", @@ -233,7 +233,7 @@ func (operation *transactionOperationWrapper) accountCreatedEffects() []effect { }, ) effects.add( - op.Destination.Address(), + op.Destination, history.EffectSignerCreated, map[string]interface{}{ "public_key": op.Destination.Address(), @@ -255,12 +255,12 @@ func (operation *transactionOperationWrapper) paymentEffects() []effect { assetDetails(details, op.Asset, "") effects.add( - op.Destination.Address(), + op.Destination.ToAccountId(), history.EffectAccountCredited, details, ) effects.add( - operation.SourceAccount().Address(), + *operation.SourceAccount(), history.EffectAccountDebited, details, ) @@ -282,7 +282,7 @@ func (operation *transactionOperationWrapper) pathPaymentStrictReceiveEffects() } effects.add( - op.Destination.Address(), + op.Destination.ToAccountId(), history.EffectAccountCredited, details, ) @@ -292,7 +292,7 @@ func (operation *transactionOperationWrapper) pathPaymentStrictReceiveEffects() assetDetails(details, op.SendAsset, "") effects.add( - source.Address(), + *source, history.EffectAccountDebited, details, ) @@ -314,11 +314,11 @@ func (operation *transactionOperationWrapper) pathPaymentStrictSendEffects() []e details := map[string]interface{}{"amount": amount.String(result.DestAmount())} assetDetails(details, op.DestAsset, "") - effects.add(op.Destination.Address(), history.EffectAccountCredited, details) + effects.add(op.Destination.ToAccountId(), history.EffectAccountCredited, details) details = map[string]interface{}{"amount": amount.String(op.SendAmount)} assetDetails(details, op.SendAsset, "") - effects.add(source.Address(), history.EffectAccountDebited, details) + effects.add(*source, history.EffectAccountDebited, details) ingestTradeEffects(&effects, *source, resultSuccess.Offers) @@ -382,7 +382,7 @@ func (operation *transactionOperationWrapper) setOptionsEffects() ([]effect, err } if op.HomeDomain != nil { - effects.add(source.Address(), history.EffectAccountHomeDomainUpdated, + effects.add(*source, history.EffectAccountHomeDomainUpdated, map[string]interface{}{ "home_domain": string(*op.HomeDomain), }, @@ -404,7 +404,7 @@ func (operation *transactionOperationWrapper) setOptionsEffects() ([]effect, err } if len(thresholdDetails) > 0 { - effects.add(source.Address(), history.EffectAccountThresholdsUpdated, thresholdDetails) + effects.add(*source, history.EffectAccountThresholdsUpdated, thresholdDetails) } flagDetails := map[string]interface{}{} @@ -412,11 +412,11 @@ func (operation *transactionOperationWrapper) setOptionsEffects() ([]effect, err effectFlagDetails(flagDetails, op.ClearFlags, false) if len(flagDetails) > 0 { - effects.add(source.Address(), history.EffectAccountFlagsUpdated, flagDetails) + effects.add(*source, history.EffectAccountFlagsUpdated, flagDetails) } if op.InflationDest != nil { - effects.add(source.Address(), history.EffectAccountInflationDestinationUpdated, + effects.add(*source, history.EffectAccountInflationDestinationUpdated, map[string]interface{}{ "inflation_destination": op.InflationDest.Address(), }, @@ -452,14 +452,14 @@ func (operation *transactionOperationWrapper) setOptionsEffects() ([]effect, err for _, addy := range beforeSortedSigners { weight, ok := after[addy] if !ok { - effects.add(source.Address(), history.EffectSignerRemoved, map[string]interface{}{ + effects.add(*source, history.EffectSignerRemoved, map[string]interface{}{ "public_key": addy, }) continue } if weight != before[addy] { - effects.add(source.Address(), history.EffectSignerUpdated, map[string]interface{}{ + effects.add(*source, history.EffectSignerUpdated, map[string]interface{}{ "public_key": addy, "weight": weight, }) @@ -481,7 +481,7 @@ func (operation *transactionOperationWrapper) setOptionsEffects() ([]effect, err continue } - effects.add(source.Address(), history.EffectSignerCreated, map[string]interface{}{ + effects.add(*source, history.EffectSignerCreated, map[string]interface{}{ "public_key": addy, "weight": weight, }) @@ -530,7 +530,7 @@ func (operation *transactionOperationWrapper) changeTrustEffects() ([]effect, er break } - effects.add(source.Address(), effect, details) + effects.add(*source, effect, details) } return effects.effects, nil @@ -551,15 +551,15 @@ func (operation *transactionOperationWrapper) allowTrustEffects() []effect { switch { case xdr.TrustLineFlags(op.Authorize).IsAuthorized(): - effects.add(source.Address(), history.EffectTrustlineAuthorized, details) + effects.add(*source, history.EffectTrustlineAuthorized, details) case xdr.TrustLineFlags(op.Authorize).IsAuthorizedToMaintainLiabilitiesFlag(): effects.add( - source.Address(), + *source, history.EffectTrustlineAuthorizedToMaintainLiabilities, details, ) default: - effects.add(source.Address(), history.EffectTrustlineDeauthorized, details) + effects.add(*source, history.EffectTrustlineDeauthorized, details) } return effects.effects @@ -572,16 +572,16 @@ func (operation *transactionOperationWrapper) accountMergeEffects() []effect { operation: operation, } - dest := operation.operation.Body.MustDestination() + dest := operation.operation.Body.MustDestination().ToAccountId() result := operation.OperationResult().MustAccountMergeResult() details := map[string]interface{}{ "amount": amount.String(result.MustSourceAccountBalance()), "asset_type": "native", } - effects.add(source.Address(), history.EffectAccountDebited, details) - effects.add(dest.Address(), history.EffectAccountCredited, details) - effects.add(source.Address(), history.EffectAccountRemoved, map[string]interface{}{}) + effects.add(*source, history.EffectAccountDebited, details) + effects.add(dest, history.EffectAccountCredited, details) + effects.add(*source, history.EffectAccountRemoved, map[string]interface{}{}) return effects.effects } @@ -593,7 +593,7 @@ func (operation *transactionOperationWrapper) inflationEffects() []effect { } payouts := operation.OperationResult().MustInflationResult().MustPayouts() for _, payout := range payouts { - effects.add(payout.Destination.Address(), history.EffectAccountCredited, + effects.add(payout.Destination, history.EffectAccountCredited, map[string]interface{}{ "amount": amount.String(payout.Amount), "asset_type": "native", @@ -645,7 +645,7 @@ func (operation *transactionOperationWrapper) manageDataEffects() ([]effect, err break } - effects.add(source.Address(), effect, details) + effects.add(*source, effect, details) return effects.effects, nil } @@ -674,7 +674,7 @@ func (operation *transactionOperationWrapper) bumpSequenceEffects() ([]effect, e if beforeAccount.SeqNum != afterAccount.SeqNum { details := map[string]interface{}{"new_seq": afterAccount.SeqNum} - effects.add(source.Address(), history.EffectSequenceBumped, details) + effects.add(*source, history.EffectSequenceBumped, details) } break @@ -709,20 +709,20 @@ func ingestTradeEffects(effects *effectsWrapper, buyer xdr.AccountId, claims []x bd, sd := tradeDetails(buyer, seller, claim) effects.add( - buyer.Address(), + buyer, history.EffectTrade, bd, ) effects.add( - seller.Address(), + seller, history.EffectTrade, sd, ) } } -func tradeDetails(buyer, seller xdr.AccountId, claim xdr.ClaimOfferAtom) (bd map[string]interface{}, sd map[string]interface{}) { +func tradeDetails(buyer xdr.AccountId, seller xdr.AccountId, claim xdr.ClaimOfferAtom) (bd map[string]interface{}, sd map[string]interface{}) { bd = map[string]interface{}{ "offer_id": claim.OfferId, "seller": seller.Address(), diff --git a/services/horizon/internal/expingest/processors/effects_processor_test.go b/services/horizon/internal/expingest/processors/effects_processor_test.go index 5f11414b62..1b27fad0d7 100644 --- a/services/horizon/internal/expingest/processors/effects_processor_test.go +++ b/services/horizon/internal/expingest/processors/effects_processor_test.go @@ -3,15 +3,16 @@ package processors import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "github.com/stellar/go/exp/ingest/io" "github.com/stellar/go/services/horizon/internal/db2/history" . "github.com/stellar/go/services/horizon/internal/test/transactions" "github.com/stellar/go/services/horizon/internal/toid" "github.com/stellar/go/support/errors" "github.com/stellar/go/xdr" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" ) type EffectsProcessorTestSuiteLedger struct { @@ -245,6 +246,61 @@ func (s *EffectsProcessorTestSuiteLedger) TestBatchAddFails() { } func TestOperationEffects(t *testing.T) { + + strictPaymentWithMuxedAccountsTx := xdr.TransactionEnvelope{ + Type: xdr.EnvelopeTypeEnvelopeTypeTx, + V1: &xdr.TransactionV1Envelope{ + Tx: xdr.Transaction{ + SourceAccount: xdr.MustMuxedAccountAddress("MDFP5OV6ZL7LVPXWYYOHVRTMX3CBJMN45XCYB2PHTQZ7FWSXFTVTX2S2SBNGGQVPR67LA"), + Fee: 100, + SeqNum: 3684420515004429, + Operations: []xdr.Operation{ + { + Body: xdr.OperationBody{ + Type: xdr.OperationTypePathPaymentStrictSend, + PathPaymentStrictSendOp: &xdr.PathPaymentStrictSendOp{ + SendAsset: xdr.Asset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AssetAlphaNum4{ + AssetCode: xdr.AssetCode4{66, 82, 76, 0}, + Issuer: xdr.MustAddress("GCXI6Q73J7F6EUSBZTPW4G4OUGVDHABPYF2U4KO7MVEX52OH5VMVUCRF"), + }, + }, + SendAmount: 300000, + Destination: xdr.MustMuxedAccountAddress("MAAAB6X27LHM5TWI5LIGWFRG5Z4H4MH5MP6CKJE6DL3CAEF4L43IKERDJ66XVO43ANKEI"), + DestAsset: xdr.Asset{ + Type: 1, + AlphaNum4: &xdr.AssetAlphaNum4{ + AssetCode: xdr.AssetCode4{65, 82, 83, 0}, + Issuer: xdr.MustAddress("GCXI6Q73J7F6EUSBZTPW4G4OUGVDHABPYF2U4KO7MVEX52OH5VMVUCRF"), + }, + }, + DestMin: 10000000, + Path: []xdr.Asset{ + { + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AssetAlphaNum4{ + AssetCode: xdr.AssetCode4{65, 82, 83, 0}, + Issuer: xdr.MustAddress("GCXI6Q73J7F6EUSBZTPW4G4OUGVDHABPYF2U4KO7MVEX52OH5VMVUCRF"), + }, + }, + }, + }, + }, + }, + }, + }, + Signatures: []xdr.DecoratedSignature{ + { + Hint: xdr.SignatureHint{99, 66, 175, 143}, + Signature: xdr.Signature{244, 107, 139, 92, 189, 156, 207, 79, 84, 56, 2, 70, 75, 22, 237, 50, 100, 242, 159, 177, 27, 240, 66, 122, 182, 45, 189, 78, 5, 127, 26, 61, 179, 238, 229, 76, 32, 206, 122, 13, 154, 133, 148, 149, 29, 250, 48, 132, 44, 86, 163, 56, 32, 44, 75, 87, 226, 251, 76, 4, 59, 182, 132, 8}, + }, + }, + }, + } + strictPaymentWithMuxedAccountsTxBase64, err := xdr.MarshalBase64(strictPaymentWithMuxedAccountsTx) + assert.NoError(t, err) + testCases := []struct { desc string envelopeXDR string @@ -266,7 +322,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 57, expected: []effect{ - effect{ + { address: "GCQZP3IU7XU6EJ63JZXKCQOYT2RNXN3HB5CNHENNUEUHSMA4VUJJJSEN", operationID: int64(244813139969), details: map[string]interface{}{ @@ -275,7 +331,7 @@ func TestOperationEffects(t *testing.T) { effectType: history.EffectAccountCreated, order: uint32(1), }, - effect{ + { address: "GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H", operationID: int64(244813139969), details: map[string]interface{}{ @@ -285,7 +341,7 @@ func TestOperationEffects(t *testing.T) { effectType: history.EffectAccountDebited, order: uint32(2), }, - effect{ + { address: "GCQZP3IU7XU6EJ63JZXKCQOYT2RNXN3HB5CNHENNUEUHSMA4VUJJJSEN", operationID: int64(244813139969), details: map[string]interface{}{ @@ -307,7 +363,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 56, expected: []effect{ - effect{ + { address: "GANFZDRBCNTUXIODCJEYMACPMCSZEVE4WZGZ3CZDZ3P2SXK4KH75IK6Y", details: map[string]interface{}{ "amount": "10.0000000", @@ -317,7 +373,7 @@ func TestOperationEffects(t *testing.T) { operationID: int64(240518172673), order: uint32(1), }, - effect{ + { address: "GANFZDRBCNTUXIODCJEYMACPMCSZEVE4WZGZ3CZDZ3P2SXK4KH75IK6Y", details: map[string]interface{}{ "amount": "10.0000000", @@ -339,7 +395,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 20, expected: []effect{ - effect{ + { address: "GDEOVUDLCYTO46D6GD6WH7BFESPBV5RACC6F6NUFCIRU7PL2XONQHVGJ", details: map[string]interface{}{ "amount": "1.0000000", @@ -351,7 +407,7 @@ func TestOperationEffects(t *testing.T) { operationID: int64(85899350017), order: uint32(1), }, - effect{ + { address: "GD3MMHD2YZWL5RAUWG6O3RMA5HTZYM7S3JLSZ2Z35JNJAWTDIKXY737V", details: map[string]interface{}{ "amount": "0.0300000", @@ -363,7 +419,7 @@ func TestOperationEffects(t *testing.T) { operationID: int64(85899350017), order: uint32(2), }, - effect{ + { address: "GD3MMHD2YZWL5RAUWG6O3RMA5HTZYM7S3JLSZ2Z35JNJAWTDIKXY737V", details: map[string]interface{}{ "seller": "GDEOVUDLCYTO46D6GD6WH7BFESPBV5RACC6F6NUFCIRU7PL2XONQHVGJ", @@ -381,7 +437,79 @@ func TestOperationEffects(t *testing.T) { operationID: int64(85899350017), order: uint32(3), }, - effect{ + { + address: "GDEOVUDLCYTO46D6GD6WH7BFESPBV5RACC6F6NUFCIRU7PL2XONQHVGJ", + details: map[string]interface{}{ + "seller": "GD3MMHD2YZWL5RAUWG6O3RMA5HTZYM7S3JLSZ2Z35JNJAWTDIKXY737V", + "offer_id": xdr.Int64(10072128), + "sold_amount": "1.0000000", + "bought_amount": "0.0300000", + "sold_asset_code": "ARS", + "sold_asset_type": "credit_alphanum4", + "bought_asset_code": "BRL", + "bought_asset_type": "credit_alphanum4", + "sold_asset_issuer": "GCXI6Q73J7F6EUSBZTPW4G4OUGVDHABPYF2U4KO7MVEX52OH5VMVUCRF", + "bought_asset_issuer": "GCXI6Q73J7F6EUSBZTPW4G4OUGVDHABPYF2U4KO7MVEX52OH5VMVUCRF", + }, + effectType: history.EffectTrade, + operationID: int64(85899350017), + order: uint32(4), + }, + }, + }, + { + desc: "pathPaymentStrictSend with muxed accounts", + envelopeXDR: strictPaymentWithMuxedAccountsTxBase64, + resultXDR: "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAANAAAAAAAAAAEAAAAAyOrQaxYm7nh+MP1j/CUknhr2IBC8XzaFEiNPvXq7mwMAAAAAAJmwQAAAAAFBUlMAAAAAAK6PQ/tPy+JSQczfbhuOoaozgC/BdU4p32VJfunH7VlaAAAAAACYloAAAAABQlJMAAAAAACuj0P7T8viUkHM324bjqGqM4AvwXVOKd9lSX7px+1ZWgAAAAAABJPgAAAAAMjq0GsWJu54fjD9Y/wlJJ4a9iAQvF82hRIjT716u5sDAAAAAUFSUwAAAAAAro9D+0/L4lJBzN9uG46hqjOAL8F1TinfZUl+6cftWVoAAAAAAJiWgAAAAAA=", + metaXDR: "AAAAAQAAAAIAAAADAA0aVQAAAAAAAAAA9sYcesZsvsQUsbztxYDp55wz8tpXLOs76lqQWmNCr48AAAAXSHbi7AANFvYAAAAMAAAAAwAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAA0aVQAAAAAAAAAA9sYcesZsvsQUsbztxYDp55wz8tpXLOs76lqQWmNCr48AAAAXSHbi7AANFvYAAAANAAAAAwAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAACAAAAAMADRo0AAAAAQAAAAD2xhx6xmy+xBSxvO3FgOnnnDPy2lcs6zvqWpBaY0KvjwAAAAFCUkwAAAAAAK6PQ/tPy+JSQczfbhuOoaozgC/BdU4p32VJfunH7VlaAAAAAB22gaB//////////wAAAAEAAAABAAAAAAC3GwAAAAAAAAAAAAAAAAAAAAAAAAAAAQANGlUAAAABAAAAAPbGHHrGbL7EFLG87cWA6eecM/LaVyzrO+pakFpjQq+PAAAAAUJSTAAAAAAAro9D+0/L4lJBzN9uG46hqjOAL8F1TinfZUl+6cftWVoAAAAAHbHtwH//////////AAAAAQAAAAEAAAAAALcbAAAAAAAAAAAAAAAAAAAAAAAAAAADAA0aNAAAAAIAAAAAyOrQaxYm7nh+MP1j/CUknhr2IBC8XzaFEiNPvXq7mwMAAAAAAJmwQAAAAAFBUlMAAAAAAK6PQ/tPy+JSQczfbhuOoaozgC/BdU4p32VJfunH7VlaAAAAAUJSTAAAAAAAro9D+0/L4lJBzN9uG46hqjOAL8F1TinfZUl+6cftWVoAAAAAFNyTgAAAAAMAAABkAAAAAAAAAAAAAAAAAAAAAQANGlUAAAACAAAAAMjq0GsWJu54fjD9Y/wlJJ4a9iAQvF82hRIjT716u5sDAAAAAACZsEAAAAABQVJTAAAAAACuj0P7T8viUkHM324bjqGqM4AvwXVOKd9lSX7px+1ZWgAAAAFCUkwAAAAAAK6PQ/tPy+JSQczfbhuOoaozgC/BdU4p32VJfunH7VlaAAAAABRD/QAAAAADAAAAZAAAAAAAAAAAAAAAAAAAAAMADRo0AAAAAQAAAADI6tBrFibueH4w/WP8JSSeGvYgELxfNoUSI0+9erubAwAAAAFCUkwAAAAAAK6PQ/tPy+JSQczfbhuOoaozgC/BdU4p32VJfunH7VlaAAAAAB3kSGB//////////wAAAAEAAAABAAAAAACgN6AAAAAAAAAAAAAAAAAAAAAAAAAAAQANGlUAAAABAAAAAMjq0GsWJu54fjD9Y/wlJJ4a9iAQvF82hRIjT716u5sDAAAAAUJSTAAAAAAAro9D+0/L4lJBzN9uG46hqjOAL8F1TinfZUl+6cftWVoAAAAAHejcQH//////////AAAAAQAAAAEAAAAAAJujwAAAAAAAAAAAAAAAAAAAAAAAAAADAA0aNAAAAAEAAAAAyOrQaxYm7nh+MP1j/CUknhr2IBC8XzaFEiNPvXq7mwMAAAABQVJTAAAAAACuj0P7T8viUkHM324bjqGqM4AvwXVOKd9lSX7px+1ZWgAAAAB2BGcAf/////////8AAAABAAAAAQAAAAAAAAAAAAAAABTck4AAAAAAAAAAAAAAAAEADRpVAAAAAQAAAADI6tBrFibueH4w/WP8JSSeGvYgELxfNoUSI0+9erubAwAAAAFBUlMAAAAAAK6PQ/tPy+JSQczfbhuOoaozgC/BdU4p32VJfunH7VlaAAAAAHYEZwB//////////wAAAAEAAAABAAAAAAAAAAAAAAAAFEP9AAAAAAAAAAAA", + feeChangesXDR: "AAAAAgAAAAMADRpIAAAAAAAAAAD2xhx6xmy+xBSxvO3FgOnnnDPy2lcs6zvqWpBaY0KvjwAAABdIduNQAA0W9gAAAAwAAAADAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEADRpVAAAAAAAAAAD2xhx6xmy+xBSxvO3FgOnnnDPy2lcs6zvqWpBaY0KvjwAAABdIduLsAA0W9gAAAAwAAAADAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA==", + hash: "96415ac1d2f79621b26b1568f963fd8dd6c50c20a22c7428cefbfe9dee867588", + index: 0, + sequence: 20, + expected: []effect{ + { + address: "GDEOVUDLCYTO46D6GD6WH7BFESPBV5RACC6F6NUFCIRU7PL2XONQHVGJ", + details: map[string]interface{}{ + "amount": "1.0000000", + "asset_code": "ARS", + "asset_type": "credit_alphanum4", + "asset_issuer": "GCXI6Q73J7F6EUSBZTPW4G4OUGVDHABPYF2U4KO7MVEX52OH5VMVUCRF", + }, + effectType: history.EffectAccountCredited, + operationID: int64(85899350017), + order: uint32(1), + }, + { + address: "GD3MMHD2YZWL5RAUWG6O3RMA5HTZYM7S3JLSZ2Z35JNJAWTDIKXY737V", + details: map[string]interface{}{ + "amount": "0.0300000", + "asset_code": "BRL", + "asset_type": "credit_alphanum4", + "asset_issuer": "GCXI6Q73J7F6EUSBZTPW4G4OUGVDHABPYF2U4KO7MVEX52OH5VMVUCRF", + }, + effectType: history.EffectAccountDebited, + operationID: int64(85899350017), + order: uint32(2), + }, + { + address: "GD3MMHD2YZWL5RAUWG6O3RMA5HTZYM7S3JLSZ2Z35JNJAWTDIKXY737V", + details: map[string]interface{}{ + "seller": "GDEOVUDLCYTO46D6GD6WH7BFESPBV5RACC6F6NUFCIRU7PL2XONQHVGJ", + "offer_id": xdr.Int64(10072128), + "sold_amount": "0.0300000", + "bought_amount": "1.0000000", + "sold_asset_code": "BRL", + "sold_asset_type": "credit_alphanum4", + "bought_asset_code": "ARS", + "bought_asset_type": "credit_alphanum4", + "sold_asset_issuer": "GCXI6Q73J7F6EUSBZTPW4G4OUGVDHABPYF2U4KO7MVEX52OH5VMVUCRF", + "bought_asset_issuer": "GCXI6Q73J7F6EUSBZTPW4G4OUGVDHABPYF2U4KO7MVEX52OH5VMVUCRF", + }, + effectType: history.EffectTrade, + operationID: int64(85899350017), + order: uint32(3), + }, + { address: "GDEOVUDLCYTO46D6GD6WH7BFESPBV5RACC6F6NUFCIRU7PL2XONQHVGJ", details: map[string]interface{}{ "seller": "GD3MMHD2YZWL5RAUWG6O3RMA5HTZYM7S3JLSZ2Z35JNJAWTDIKXY737V", @@ -422,7 +550,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 56, expected: []effect{ - effect{ + { address: "GD5OGQTZZ2PYI2RSMOJA6BQ7CDCW2JXAXBKR6XZK6PPRFUZ3BUXNLFKP", details: map[string]interface{}{ "seller": "GAHEPWQ2B5ZOPI2NB647QCIXFPQR4H56FPYADQY54GNMFG4IYB5ZAJ5H", @@ -438,7 +566,7 @@ func TestOperationEffects(t *testing.T) { operationID: int64(240518172673), order: uint32(1), }, - effect{ + { address: "GAHEPWQ2B5ZOPI2NB647QCIXFPQR4H56FPYADQY54GNMFG4IYB5ZAJ5H", details: map[string]interface{}{ "seller": "GD5OGQTZZ2PYI2RSMOJA6BQ7CDCW2JXAXBKR6XZK6PPRFUZ3BUXNLFKP", @@ -466,7 +594,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 56, expected: []effect{ - effect{ + { address: "GBFC3KATHWQOZ3TWJEOLMBBFMPZ4OS2KYVZRKWVRMQKZ2LFNRLQEIRCV", details: map[string]interface{}{ "seller": "GCA3EPMNR26H3BO55PQPAMOGKBAIMARLQHWCRK7KTUPGR62SDVLIL7D6", @@ -482,7 +610,7 @@ func TestOperationEffects(t *testing.T) { operationID: int64(240518172673), order: uint32(1), }, - effect{ + { address: "GCA3EPMNR26H3BO55PQPAMOGKBAIMARLQHWCRK7KTUPGR62SDVLIL7D6", details: map[string]interface{}{ "seller": "GBFC3KATHWQOZ3TWJEOLMBBFMPZ4OS2KYVZRKWVRMQKZ2LFNRLQEIRCV", @@ -510,7 +638,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 56, expected: []effect{ - effect{ + { address: "GAA7AZYCJ65VJSMFAGQLBNCXA43QQ6ZEUR4GL4YSVB2FXUAHLLYUHIO5", details: map[string]interface{}{ "bought_amount": "100000.0000000", @@ -526,7 +654,7 @@ func TestOperationEffects(t *testing.T) { operationID: int64(240518172673), order: uint32(1), }, - effect{ + { address: "GAZAIOXF7GBHGPHOYJSTPIIC4K6AJM55S5Q44OCJHEHIF6YU2IHO6VHU", details: map[string]interface{}{ "bought_amount": "100.0000000", @@ -554,7 +682,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 56, expected: []effect{ - effect{ + { address: "GC4XF7RE3R4P77GY5XNGICM56IOKUURWAAANPXHFC7G5H6FCNQVVH3OH", details: map[string]interface{}{ "home_domain": "https://www.home.org/", @@ -563,7 +691,7 @@ func TestOperationEffects(t *testing.T) { operationID: int64(240518172673), order: uint32(1), }, - effect{ + { address: "GC4XF7RE3R4P77GY5XNGICM56IOKUURWAAANPXHFC7G5H6FCNQVVH3OH", details: map[string]interface{}{ "high_threshold": xdr.Uint32(3), @@ -574,7 +702,7 @@ func TestOperationEffects(t *testing.T) { operationID: int64(240518172673), order: uint32(2), }, - effect{ + { address: "GC4XF7RE3R4P77GY5XNGICM56IOKUURWAAANPXHFC7G5H6FCNQVVH3OH", details: map[string]interface{}{ "auth_required_flag": true, @@ -584,7 +712,7 @@ func TestOperationEffects(t *testing.T) { operationID: int64(240518172673), order: uint32(3), }, - effect{ + { address: "GC4XF7RE3R4P77GY5XNGICM56IOKUURWAAANPXHFC7G5H6FCNQVVH3OH", details: map[string]interface{}{ "inflation_destination": "GAQHWQYBBW272OOXNQMMLCA5WY2XAZPODGB7Q3S5OKKIXVESKO55ZQ7C", @@ -593,7 +721,7 @@ func TestOperationEffects(t *testing.T) { operationID: int64(240518172673), order: uint32(4), }, - effect{ + { address: "GC4XF7RE3R4P77GY5XNGICM56IOKUURWAAANPXHFC7G5H6FCNQVVH3OH", details: map[string]interface{}{ "public_key": "GC4XF7RE3R4P77GY5XNGICM56IOKUURWAAANPXHFC7G5H6FCNQVVH3OH", @@ -603,7 +731,7 @@ func TestOperationEffects(t *testing.T) { operationID: int64(240518172673), order: uint32(5), }, - effect{ + { address: "GC4XF7RE3R4P77GY5XNGICM56IOKUURWAAANPXHFC7G5H6FCNQVVH3OH", details: map[string]interface{}{ "public_key": "GAQHWQYBBW272OOXNQMMLCA5WY2XAZPODGB7Q3S5OKKIXVESKO55ZQ7C", @@ -625,7 +753,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 40, expected: []effect{ - effect{ + { address: "GCVW5LCRZFP7PENXTAGOVIQXADDNUXXZJCNKF4VQB2IK7W2LPJWF73UG", effectType: history.EffectTrustlineCreated, operationID: int64(171798695937), @@ -649,7 +777,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 40, expected: []effect{ - effect{ + { address: "GAOAGSP3JOOTKQA6SKKBTA6ZPUZGX2VSE3SZ4SFIGHKQIW4TEN5ZX3WW", effectType: history.EffectTrustlineRemoved, operationID: int64(171798695937), @@ -673,7 +801,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 40, expected: []effect{ - effect{ + { address: "GBY5WEQVMKTSM5UTQ3ZMQJTEJNIF3PUY7N62AQUG4YUPCL6RW7HVJARI", effectType: history.EffectTrustlineUpdated, operationID: int64(171798695937), @@ -697,7 +825,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 41, expected: []effect{ - effect{ + { address: "GD4SMOE3VPSF7ZR3CTEQ3P5UNTBMEJDA2GLXTHR7MMARANKKJDZ7RPGF", effectType: history.EffectTrustlineAuthorized, operationID: int64(176093663233), @@ -721,7 +849,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 44, expected: []effect{ - effect{ + { address: "GCHPXGVDKPF5KT4CNAT7X77OXYZ7YVE4JHKFDUHCGCVWCL4K4PQ67KKZ", effectType: history.EffectAccountDebited, operationID: int64(188978565121), @@ -731,7 +859,7 @@ func TestOperationEffects(t *testing.T) { "asset_type": "native", }, }, - effect{ + { address: "GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H", effectType: history.EffectAccountCredited, operationID: int64(188978565121), @@ -741,7 +869,7 @@ func TestOperationEffects(t *testing.T) { "asset_type": "native", }, }, - effect{ + { address: "GCHPXGVDKPF5KT4CNAT7X77OXYZ7YVE4JHKFDUHCGCVWCL4K4PQ67KKZ", effectType: history.EffectAccountRemoved, operationID: int64(188978565121), @@ -760,7 +888,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 47, expected: []effect{ - effect{ + { address: "GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H", effectType: history.EffectAccountCredited, operationID: int64(201863467009), @@ -770,7 +898,7 @@ func TestOperationEffects(t *testing.T) { "asset_type": "native", }, }, - effect{ + { address: "GDR53WAEIKOU3ZKN34CSHAWH7HV6K63CBJRUTWUDBFSMY7RRQK3SPKOS", effectType: history.EffectAccountCredited, operationID: int64(201863467009), @@ -792,7 +920,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 49, expected: []effect{ - effect{ + { address: "GAYSCMKQY6EYLXOPTT6JPPOXDMVNBWITPTSZIVWW4LWARVBOTH5RTLAD", effectType: history.EffectDataCreated, operationID: int64(210453401601), @@ -814,7 +942,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 49, expected: []effect{ - effect{ + { address: "GC4XF7RE3R4P77GY5XNGICM56IOKUURWAAANPXHFC7G5H6FCNQVVH3OH", effectType: history.EffectDataRemoved, operationID: int64(210453401601), @@ -835,7 +963,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 49, expected: []effect{ - effect{ + { address: "GA4O5DLUUTLCTMM2UOWOYPNIH2FTD4NLO6KDZOFQRUISQ3FYKABGJLPC", effectType: history.EffectDataUpdated, operationID: int64(210453401601), @@ -881,7 +1009,7 @@ func TestOperationEffects(t *testing.T) { index: 0, sequence: 58, expected: []effect{ - effect{ + { address: "GCQZP3IU7XU6EJ63JZXKCQOYT2RNXN3HB5CNHENNUEUHSMA4VUJJJSEN", effectType: history.EffectSequenceBumped, operationID: int64(249108107265), @@ -936,10 +1064,10 @@ func TestOperationEffectsSetOptionsSignersOrder(t *testing.T) { tt := assert.New(t) transaction := io.LedgerTransaction{ Meta: createTransactionMeta([]xdr.OperationMeta{ - xdr.OperationMeta{ + { Changes: []xdr.LedgerEntryChange{ // State - xdr.LedgerEntryChange{ + { Type: xdr.LedgerEntryChangeTypeLedgerEntryState, State: &xdr.LedgerEntry{ Data: xdr.LedgerEntryData{ @@ -947,11 +1075,11 @@ func TestOperationEffectsSetOptionsSignersOrder(t *testing.T) { Account: &xdr.AccountEntry{ AccountId: xdr.MustAddress("GC3C4AKRBQLHOJ45U4XG35ESVWRDECWO5XLDGYADO6DPR3L7KIDVUMML"), Signers: []xdr.Signer{ - xdr.Signer{ + { Key: xdr.MustSigner("GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV"), Weight: 10, }, - xdr.Signer{ + { Key: xdr.MustSigner("GCAHY6JSXQFKWKP6R7U5JPXDVNV4DJWOWRFLY3Y6YPBF64QRL4BPFDNS"), Weight: 10, }, @@ -961,7 +1089,7 @@ func TestOperationEffectsSetOptionsSignersOrder(t *testing.T) { }, }, // Updated - xdr.LedgerEntryChange{ + { Type: xdr.LedgerEntryChangeTypeLedgerEntryUpdated, Updated: &xdr.LedgerEntry{ Data: xdr.LedgerEntryData{ @@ -969,19 +1097,19 @@ func TestOperationEffectsSetOptionsSignersOrder(t *testing.T) { Account: &xdr.AccountEntry{ AccountId: xdr.MustAddress("GC3C4AKRBQLHOJ45U4XG35ESVWRDECWO5XLDGYADO6DPR3L7KIDVUMML"), Signers: []xdr.Signer{ - xdr.Signer{ + { Key: xdr.MustSigner("GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV"), Weight: 16, }, - xdr.Signer{ + { Key: xdr.MustSigner("GCAHY6JSXQFKWKP6R7U5JPXDVNV4DJWOWRFLY3Y6YPBF64QRL4BPFDNS"), Weight: 15, }, - xdr.Signer{ + { Key: xdr.MustSigner("GCR3TQ2TVH3QRI7GQMC3IJGUUBR32YQHWBIKIMTYRQ2YH4XUTDB75UKE"), Weight: 14, }, - xdr.Signer{ + { Key: xdr.MustSigner("GA4O5DLUUTLCTMM2UOWOYPNIH2FTD4NLO6KDZOFQRUISQ3FYKABGJLPC"), Weight: 17, }, @@ -998,7 +1126,7 @@ func TestOperationEffectsSetOptionsSignersOrder(t *testing.T) { transaction.Envelope.Type = xdr.EnvelopeTypeEnvelopeTypeTx transaction.Envelope.V1 = &xdr.TransactionV1Envelope{ Tx: xdr.Transaction{ - SourceAccount: xdr.MustAddress("GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV"), + SourceAccount: xdr.MustMuxedAccountAddress("GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV"), }, } @@ -1017,7 +1145,7 @@ func TestOperationEffectsSetOptionsSignersOrder(t *testing.T) { effects, err := operation.effects() tt.NoError(err) expected := []effect{ - effect{ + { address: "GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV", operationID: int64(197568499713), details: map[string]interface{}{ @@ -1027,7 +1155,7 @@ func TestOperationEffectsSetOptionsSignersOrder(t *testing.T) { effectType: history.EffectSignerUpdated, order: uint32(1), }, - effect{ + { address: "GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV", operationID: int64(197568499713), details: map[string]interface{}{ @@ -1037,7 +1165,7 @@ func TestOperationEffectsSetOptionsSignersOrder(t *testing.T) { effectType: history.EffectSignerUpdated, order: uint32(2), }, - effect{ + { address: "GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV", operationID: int64(197568499713), details: map[string]interface{}{ @@ -1047,7 +1175,7 @@ func TestOperationEffectsSetOptionsSignersOrder(t *testing.T) { effectType: history.EffectSignerCreated, order: uint32(3), }, - effect{ + { address: "GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV", operationID: int64(197568499713), details: map[string]interface{}{ @@ -1066,10 +1194,10 @@ func TestOperationEffectsSetOptionsSignersNoUpdated(t *testing.T) { tt := assert.New(t) transaction := io.LedgerTransaction{ Meta: createTransactionMeta([]xdr.OperationMeta{ - xdr.OperationMeta{ + { Changes: []xdr.LedgerEntryChange{ // State - xdr.LedgerEntryChange{ + { Type: xdr.LedgerEntryChangeTypeLedgerEntryState, State: &xdr.LedgerEntry{ Data: xdr.LedgerEntryData{ @@ -1077,15 +1205,15 @@ func TestOperationEffectsSetOptionsSignersNoUpdated(t *testing.T) { Account: &xdr.AccountEntry{ AccountId: xdr.MustAddress("GC3C4AKRBQLHOJ45U4XG35ESVWRDECWO5XLDGYADO6DPR3L7KIDVUMML"), Signers: []xdr.Signer{ - xdr.Signer{ + { Key: xdr.MustSigner("GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV"), Weight: 10, }, - xdr.Signer{ + { Key: xdr.MustSigner("GCAHY6JSXQFKWKP6R7U5JPXDVNV4DJWOWRFLY3Y6YPBF64QRL4BPFDNS"), Weight: 10, }, - xdr.Signer{ + { Key: xdr.MustSigner("GA4O5DLUUTLCTMM2UOWOYPNIH2FTD4NLO6KDZOFQRUISQ3FYKABGJLPC"), Weight: 17, }, @@ -1095,7 +1223,7 @@ func TestOperationEffectsSetOptionsSignersNoUpdated(t *testing.T) { }, }, // Updated - xdr.LedgerEntryChange{ + { Type: xdr.LedgerEntryChangeTypeLedgerEntryUpdated, Updated: &xdr.LedgerEntry{ Data: xdr.LedgerEntryData{ @@ -1103,15 +1231,15 @@ func TestOperationEffectsSetOptionsSignersNoUpdated(t *testing.T) { Account: &xdr.AccountEntry{ AccountId: xdr.MustAddress("GC3C4AKRBQLHOJ45U4XG35ESVWRDECWO5XLDGYADO6DPR3L7KIDVUMML"), Signers: []xdr.Signer{ - xdr.Signer{ + { Key: xdr.MustSigner("GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV"), Weight: 16, }, - xdr.Signer{ + { Key: xdr.MustSigner("GCAHY6JSXQFKWKP6R7U5JPXDVNV4DJWOWRFLY3Y6YPBF64QRL4BPFDNS"), Weight: 10, }, - xdr.Signer{ + { Key: xdr.MustSigner("GCR3TQ2TVH3QRI7GQMC3IJGUUBR32YQHWBIKIMTYRQ2YH4XUTDB75UKE"), Weight: 14, }, @@ -1128,7 +1256,7 @@ func TestOperationEffectsSetOptionsSignersNoUpdated(t *testing.T) { transaction.Envelope.Type = xdr.EnvelopeTypeEnvelopeTypeTx transaction.Envelope.V1 = &xdr.TransactionV1Envelope{ Tx: xdr.Transaction{ - SourceAccount: xdr.MustAddress("GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV"), + SourceAccount: xdr.MustMuxedAccountAddress("GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV"), }, } @@ -1147,7 +1275,7 @@ func TestOperationEffectsSetOptionsSignersNoUpdated(t *testing.T) { effects, err := operation.effects() tt.NoError(err) expected := []effect{ - effect{ + { address: "GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV", operationID: int64(197568499713), details: map[string]interface{}{ @@ -1156,7 +1284,7 @@ func TestOperationEffectsSetOptionsSignersNoUpdated(t *testing.T) { effectType: history.EffectSignerRemoved, order: uint32(1), }, - effect{ + { address: "GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV", operationID: int64(197568499713), details: map[string]interface{}{ @@ -1166,7 +1294,7 @@ func TestOperationEffectsSetOptionsSignersNoUpdated(t *testing.T) { effectType: history.EffectSignerUpdated, order: uint32(2), }, - effect{ + { address: "GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV", operationID: int64(197568499713), details: map[string]interface{}{ @@ -1191,7 +1319,7 @@ func TestOperationRegressionAccountTrustItself(t *testing.T) { transaction.Envelope.Type = xdr.EnvelopeTypeEnvelopeTypeTx transaction.Envelope.V1 = &xdr.TransactionV1Envelope{ Tx: xdr.Transaction{ - SourceAccount: xdr.MustAddress("GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV"), + SourceAccount: xdr.MustMuxedAccountAddress("GCBBDQLCTNASZJ3MTKAOYEOWRGSHDFAJVI7VPZUOP7KXNHYR3HP2BUKV"), }, } operation := transactionOperationWrapper{ @@ -1219,7 +1347,7 @@ func TestOperationEffectsAllowTrustAuthorizedToMaintainLiabilities(t *testing.T) asset := xdr.Asset{} allowTrustAsset, err := asset.ToAllowTrustOpAsset("COP") tt.NoError(err) - source := xdr.MustAddress("GDRW375MAYR46ODGF2WGANQC2RRZL7O246DYHHCGWTV2RE7IHE2QUQLD") + source := xdr.MustMuxedAccountAddress("GDRW375MAYR46ODGF2WGANQC2RRZL7O246DYHHCGWTV2RE7IHE2QUQLD") op := xdr.Operation{ SourceAccount: &source, Body: xdr.OperationBody{ @@ -1243,7 +1371,7 @@ func TestOperationEffectsAllowTrustAuthorizedToMaintainLiabilities(t *testing.T) tt.NoError(err) expected := []effect{ - effect{ + { address: "GDRW375MAYR46ODGF2WGANQC2RRZL7O246DYHHCGWTV2RE7IHE2QUQLD", operationID: 4294967297, details: map[string]interface{}{ diff --git a/services/horizon/internal/expingest/processors/ledgers_processor_test.go b/services/horizon/internal/expingest/processors/ledgers_processor_test.go index 96c1f14fe7..21981b2c79 100644 --- a/services/horizon/internal/expingest/processors/ledgers_processor_test.go +++ b/services/horizon/internal/expingest/processors/ledgers_processor_test.go @@ -56,7 +56,7 @@ func createTransaction(successful bool, numOps int) io.LedgerTransaction { Type: xdr.EnvelopeTypeEnvelopeTypeTx, V1: &xdr.TransactionV1Envelope{ Tx: xdr.Transaction{ - SourceAccount: xdr.MustAddress("GAUJETIZVEP2NRYLUESJ3LS66NVCEGMON4UDCBCSBEVPIID773P2W6AY"), + SourceAccount: xdr.MustMuxedAccountAddress("GAUJETIZVEP2NRYLUESJ3LS66NVCEGMON4UDCBCSBEVPIID773P2W6AY"), Operations: operations, }, }, diff --git a/services/horizon/internal/expingest/processors/operations_processor.go b/services/horizon/internal/expingest/processors/operations_processor.go index acae5463cd..6b194777fd 100644 --- a/services/horizon/internal/expingest/processors/operations_processor.go +++ b/services/horizon/internal/expingest/processors/operations_processor.go @@ -93,11 +93,13 @@ func (operation *transactionOperationWrapper) TransactionID() int64 { // SourceAccount returns the operation's source account. func (operation *transactionOperationWrapper) SourceAccount() *xdr.AccountId { sourceAccount := operation.operation.SourceAccount + var sa xdr.AccountId if sourceAccount != nil { - return sourceAccount + sa = sourceAccount.ToAccountId() + } else { + sa = operation.transaction.Envelope.SourceAccount().ToAccountId() } - sa := operation.transaction.Envelope.SourceAccount() return &sa } @@ -127,13 +129,15 @@ func (operation *transactionOperationWrapper) Details() map[string]interface{} { case xdr.OperationTypePayment: op := operation.operation.Body.MustPaymentOp() details["from"] = source.Address() - details["to"] = op.Destination.Address() + accid := op.Destination.ToAccountId() + details["to"] = accid.Address() details["amount"] = amount.String(op.Amount) assetDetails(details, op.Asset, "") case xdr.OperationTypePathPaymentStrictReceive: op := operation.operation.Body.MustPathPaymentStrictReceiveOp() details["from"] = source.Address() - details["to"] = op.Destination.Address() + accid := op.Destination.ToAccountId() + details["to"] = accid.Address() details["amount"] = amount.String(op.DestAmount) details["source_amount"] = amount.String(0) @@ -156,7 +160,8 @@ func (operation *transactionOperationWrapper) Details() map[string]interface{} { case xdr.OperationTypePathPaymentStrictSend: op := operation.operation.Body.MustPathPaymentStrictSendOp() details["from"] = source.Address() - details["to"] = op.Destination.Address() + accid := op.Destination.ToAccountId() + details["to"] = accid.Address() details["amount"] = amount.String(0) details["source_amount"] = amount.String(op.SendAmount) @@ -262,7 +267,7 @@ func (operation *transactionOperationWrapper) Details() map[string]interface{} { details["authorize_to_maintain_liabilities"] = xdr.TrustLineFlags(op.Authorize).IsAuthorizedToMaintainLiabilitiesFlag() } case xdr.OperationTypeAccountMerge: - aid := operation.operation.Body.MustDestination() + aid := operation.operation.Body.MustDestination().ToAccountId() details["account"] = source.Address() details["into"] = aid.Address() case xdr.OperationTypeInflation: @@ -345,11 +350,11 @@ func (operation *transactionOperationWrapper) Participants() ([]xdr.AccountId, e case xdr.OperationTypeCreateAccount: participants = append(participants, op.Body.MustCreateAccountOp().Destination) case xdr.OperationTypePayment: - participants = append(participants, op.Body.MustPaymentOp().Destination) + participants = append(participants, op.Body.MustPaymentOp().Destination.ToAccountId()) case xdr.OperationTypePathPaymentStrictReceive: - participants = append(participants, op.Body.MustPathPaymentStrictReceiveOp().Destination) + participants = append(participants, op.Body.MustPathPaymentStrictReceiveOp().Destination.ToAccountId()) case xdr.OperationTypePathPaymentStrictSend: - participants = append(participants, op.Body.MustPathPaymentStrictSendOp().Destination) + participants = append(participants, op.Body.MustPathPaymentStrictSendOp().Destination.ToAccountId()) case xdr.OperationTypeManageBuyOffer: // the only direct participant is the source_account case xdr.OperationTypeManageSellOffer: @@ -363,7 +368,7 @@ func (operation *transactionOperationWrapper) Participants() ([]xdr.AccountId, e case xdr.OperationTypeAllowTrust: participants = append(participants, op.Body.MustAllowTrustOp().Trustor) case xdr.OperationTypeAccountMerge: - participants = append(participants, op.Body.MustDestination()) + participants = append(participants, op.Body.MustDestination().ToAccountId()) case xdr.OperationTypeInflation: // the only direct participant is the source_account case xdr.OperationTypeManageData: diff --git a/services/horizon/internal/expingest/processors/operations_processor_test.go b/services/horizon/internal/expingest/processors/operations_processor_test.go index a4209be6aa..cf31d21452 100644 --- a/services/horizon/internal/expingest/processors/operations_processor_test.go +++ b/services/horizon/internal/expingest/processors/operations_processor_test.go @@ -4,9 +4,13 @@ import ( "encoding/json" "testing" + "github.com/stretchr/testify/assert" + "github.com/stellar/go/exp/ingest/io" "github.com/stellar/go/services/horizon/internal/db2/history" "github.com/stellar/go/support/errors" + "github.com/stellar/go/xdr" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) @@ -71,7 +75,25 @@ func (s *OperationsProcessorTestSuiteLedger) mockBatchInsertAdds(txs []io.Ledger } func (s *OperationsProcessorTestSuiteLedger) TestAddOperationSucceeds() { + unmuxed := xdr.MustAddress("GA5WBPYA5Y4WAEHXWR2UKO2UO4BUGHUQ74EUPKON2QHV4WRHOIRNKKH2") + muxed := xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xdeadbeefdeadbeef, + Ed25519: *unmuxed.Ed25519, + }, + } firstTx := createTransaction(true, 1) + firstTx.Index = 1 + firstTx.Envelope.Operations()[0].Body = xdr.OperationBody{ + Type: xdr.OperationTypePayment, + PaymentOp: &xdr.PaymentOp{ + Destination: muxed, + Asset: xdr.Asset{Type: xdr.AssetTypeAssetTypeNative}, + Amount: 100, + }, + } + firstTx.Envelope.V1.Tx.SourceAccount = muxed secondTx := createTransaction(false, 3) thirdTx := createTransaction(true, 4) @@ -119,3 +141,36 @@ func (s *OperationsProcessorTestSuiteLedger) TestExecFails() { s.Assert().Error(err) s.Assert().EqualError(err, "transient error") } + +func TestTransactionOperationWrapper_Details(t *testing.T) { + unmuxed := xdr.MustAddress("GA5WBPYA5Y4WAEHXWR2UKO2UO4BUGHUQ74EUPKON2QHV4WRHOIRNKKH2") + muxed := xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xdeadbeefdeadbeef, + Ed25519: *unmuxed.Ed25519, + }, + } + tx := createTransaction(true, 1) + tx.Index = 1 + tx.Envelope.Operations()[0].Body = xdr.OperationBody{ + Type: xdr.OperationTypePayment, + PaymentOp: &xdr.PaymentOp{ + Destination: muxed, + Asset: xdr.Asset{Type: xdr.AssetTypeAssetTypeNative}, + Amount: 100, + }, + } + wrapper := transactionOperationWrapper{ + index: 1, + transaction: tx, + operation: tx.Envelope.Operations()[0], + ledgerSequence: uint32(56), + } + assert.Equal(t, wrapper.Details(), map[string]interface{}{ + "amount": "0.0000100", + "asset_type": "native", + "from": "GAUJETIZVEP2NRYLUESJ3LS66NVCEGMON4UDCBCSBEVPIID773P2W6AY", + "to": "GA5WBPYA5Y4WAEHXWR2UKO2UO4BUGHUQ74EUPKON2QHV4WRHOIRNKKH2", + }) +} diff --git a/services/horizon/internal/expingest/processors/participants_processor.go b/services/horizon/internal/expingest/processors/participants_processor.go index bf95e4ce6a..42b541dbea 100644 --- a/services/horizon/internal/expingest/processors/participants_processor.go +++ b/services/horizon/internal/expingest/processors/participants_processor.go @@ -140,7 +140,7 @@ func participantsForTransaction( transaction io.LedgerTransaction, ) ([]xdr.AccountId, error) { participants := []xdr.AccountId{ - transaction.Envelope.SourceAccount(), + transaction.Envelope.SourceAccount().ToAccountId(), } if transaction.Envelope.IsFeeBump() { participants = append(participants, transaction.Envelope.FeeBumpAccount()) diff --git a/services/horizon/internal/expingest/processors/participants_processor_test.go b/services/horizon/internal/expingest/processors/participants_processor_test.go index 566b68f9c6..bd49e35cf7 100644 --- a/services/horizon/internal/expingest/processors/participants_processor_test.go +++ b/services/horizon/internal/expingest/processors/participants_processor_test.go @@ -3,13 +3,14 @@ package processors import ( "testing" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "github.com/stellar/go/exp/ingest/io" "github.com/stellar/go/services/horizon/internal/db2/history" "github.com/stellar/go/services/horizon/internal/toid" "github.com/stellar/go/support/errors" "github.com/stellar/go/xdr" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" ) type ParticipantsProcessorTestSuiteLedger struct { @@ -19,15 +20,17 @@ type ParticipantsProcessorTestSuiteLedger struct { mockBatchInsertBuilder *history.MockTransactionParticipantsBatchInsertBuilder mockOperationsBatchInsertBuilder *history.MockOperationParticipantBatchInsertBuilder - firstTx io.LedgerTransaction - secondTx io.LedgerTransaction - thirdTx io.LedgerTransaction - firstTxID int64 - secondTxID int64 - thirdTxID int64 - addresses []string - addressToID map[string]int64 - txs []io.LedgerTransaction + firstTx io.LedgerTransaction + secondTx io.LedgerTransaction + thirdTx io.LedgerTransaction + firstTxID int64 + secondTxID int64 + thirdTxID int64 + addresses []string + unmuxedAddresses []string + addressToID map[string]int64 + unmuxedAddressToID map[string]int64 + txs []io.LedgerTransaction } func TestParticipantsProcessorTestSuiteLedger(t *testing.T) { @@ -41,14 +44,28 @@ func (s *ParticipantsProcessorTestSuiteLedger) SetupTest() { sequence := uint32(20) s.addresses = []string{ - "GA5WBPYA5Y4WAEHXWR2UKO2UO4BUGHUQ74EUPKON2QHV4WRHOIRNKKH2", + "MDPK3PXP32W353Z3MC7QB3RZMAIPPNDVIU5VI5YDIMPJB7YJI6U43VAPLZNCO4RC2WIDK", "GAXI33UCLQTCKM2NMRBS7XYBR535LLEVAHL5YBN4FTCB4HZHT7ZA5CVK", "GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H", } + s.unmuxedAddresses = make([]string, len(s.addresses)) + for i := range s.addresses { + acid := xdr.MustMuxedAccountAddress(s.addresses[i]).ToAccountId() + s.unmuxedAddresses[i] = acid.Address() + } + s.firstTx = createTransaction(true, 1) s.firstTx.Index = 1 - s.firstTx.Envelope.V1.Tx.SourceAccount = xdr.MustAddress(s.addresses[0]) + s.firstTx.Envelope.Operations()[0].Body = xdr.OperationBody{ + Type: xdr.OperationTypePayment, + PaymentOp: &xdr.PaymentOp{ + Destination: xdr.MustMuxedAccountAddress(s.addresses[0]), + Asset: xdr.Asset{Type: xdr.AssetTypeAssetTypeNative}, + Amount: 100, + }, + } + s.firstTx.Envelope.V1.Tx.SourceAccount = xdr.MustMuxedAccountAddress(s.addresses[0]) s.firstTxID = toid.New(int32(sequence), 1, 0).ToInt64() s.secondTx = createTransaction(true, 1) @@ -59,12 +76,12 @@ func (s *ParticipantsProcessorTestSuiteLedger) SetupTest() { Destination: xdr.MustAddress(s.addresses[1]), }, } - s.secondTx.Envelope.V1.Tx.SourceAccount = xdr.MustAddress(s.addresses[2]) + s.secondTx.Envelope.V1.Tx.SourceAccount = xdr.MustMuxedAccountAddress(s.addresses[2]) s.secondTxID = toid.New(int32(sequence), 2, 0).ToInt64() s.thirdTx = createTransaction(true, 1) s.thirdTx.Index = 3 - s.thirdTx.Envelope.V1.Tx.SourceAccount = xdr.MustAddress(s.addresses[0]) + s.thirdTx.Envelope.V1.Tx.SourceAccount = xdr.MustMuxedAccountAddress(s.addresses[0]) s.thirdTxID = toid.New(int32(sequence), 3, 0).ToInt64() s.addressToID = map[string]int64{ @@ -73,6 +90,12 @@ func (s *ParticipantsProcessorTestSuiteLedger) SetupTest() { s.addresses[2]: 200, } + s.unmuxedAddressToID = map[string]int64{ + s.unmuxedAddresses[0]: 2, + s.unmuxedAddresses[1]: 20, + s.unmuxedAddresses[2]: 200, + } + s.processor = NewParticipantsProcessor( s.mockQ, sequence, @@ -130,7 +153,7 @@ func (s *ParticipantsProcessorTestSuiteLedger) TestEmptyParticipants() { func (s *ParticipantsProcessorTestSuiteLedger) TestFeeBumptransaction() { feeBumpTx := createTransaction(true, 0) feeBumpTx.Index = 1 - feeBumpTx.Envelope.V1.Tx.SourceAccount = xdr.MustAddress(s.addresses[0]) + feeBumpTx.Envelope.V1.Tx.SourceAccount = xdr.MustMuxedAccountAddress(s.addresses[0]) feeBumpTx.Envelope.FeeBump = &xdr.FeeBumpTransactionEnvelope{ Tx: xdr.FeeBumpTransaction{ FeeSource: xdr.MustAddress(s.addresses[1]), @@ -155,29 +178,29 @@ func (s *ParticipantsProcessorTestSuiteLedger) TestFeeBumptransaction() { feeBumpTx.Result.Result.Result.Results = nil feeBumpTxID := toid.New(20, 1, 0).ToInt64() - addresses := s.addresses[:2] - addressToID := map[string]int64{ - addresses[0]: s.addressToID[addresses[0]], - addresses[1]: s.addressToID[addresses[1]], + unmuxedAddresses := s.unmuxedAddresses[:2] + unmuxedAddressToID := map[string]int64{ + unmuxedAddresses[0]: s.unmuxedAddressToID[unmuxedAddresses[0]], + unmuxedAddresses[1]: s.unmuxedAddressToID[unmuxedAddresses[1]], } s.mockQ.On("CreateAccounts", mock.AnythingOfType("[]string"), maxBatchSize). Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - addresses, + unmuxedAddresses, arg, ) - }).Return(addressToID, nil).Once() + }).Return(unmuxedAddressToID, nil).Once() s.mockQ.On("NewTransactionParticipantsBatchInsertBuilder", maxBatchSize). Return(s.mockBatchInsertBuilder).Once() s.mockQ.On("NewOperationParticipantBatchInsertBuilder", maxBatchSize). Return(s.mockOperationsBatchInsertBuilder).Once() s.mockBatchInsertBuilder.On( - "Add", feeBumpTxID, addressToID[addresses[0]], + "Add", feeBumpTxID, unmuxedAddressToID[unmuxedAddresses[0]], ).Return(nil).Once() s.mockBatchInsertBuilder.On( - "Add", feeBumpTxID, addressToID[addresses[1]], + "Add", feeBumpTxID, unmuxedAddressToID[unmuxedAddresses[1]], ).Return(nil).Once() s.mockBatchInsertBuilder.On("Exec").Return(nil).Once() @@ -192,10 +215,10 @@ func (s *ParticipantsProcessorTestSuiteLedger) TestIngestParticipantsSucceeds() Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - s.addresses, + s.unmuxedAddresses, arg, ) - }).Return(s.addressToID, nil).Once() + }).Return(s.unmuxedAddressToID, nil).Once() s.mockQ.On("NewTransactionParticipantsBatchInsertBuilder", maxBatchSize). Return(s.mockBatchInsertBuilder).Once() s.mockQ.On("NewOperationParticipantBatchInsertBuilder", maxBatchSize). @@ -231,10 +254,10 @@ func (s *ParticipantsProcessorTestSuiteLedger) TestBatchAddFails() { Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - s.addresses, + s.unmuxedAddresses, arg, ) - }).Return(s.addressToID, nil).Once() + }).Return(s.unmuxedAddressToID, nil).Once() s.mockQ.On("NewTransactionParticipantsBatchInsertBuilder", maxBatchSize). Return(s.mockBatchInsertBuilder).Once() @@ -265,10 +288,10 @@ func (s *ParticipantsProcessorTestSuiteLedger) TestOperationParticipantsBatchAdd Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - s.addresses, + s.unmuxedAddresses, arg, ) - }).Return(s.addressToID, nil).Once() + }).Return(s.unmuxedAddressToID, nil).Once() s.mockQ.On("NewTransactionParticipantsBatchInsertBuilder", maxBatchSize). Return(s.mockBatchInsertBuilder).Once() s.mockQ.On("NewOperationParticipantBatchInsertBuilder", maxBatchSize). @@ -304,10 +327,10 @@ func (s *ParticipantsProcessorTestSuiteLedger) TestBatchAddExecFails() { Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - s.addresses, + s.unmuxedAddresses, arg, ) - }).Return(s.addressToID, nil).Once() + }).Return(s.unmuxedAddressToID, nil).Once() s.mockQ.On("NewTransactionParticipantsBatchInsertBuilder", maxBatchSize). Return(s.mockBatchInsertBuilder).Once() @@ -328,10 +351,10 @@ func (s *ParticipantsProcessorTestSuiteLedger) TestOpeartionBatchAddExecFails() Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - s.addresses, + s.unmuxedAddresses, arg, ) - }).Return(s.addressToID, nil).Once() + }).Return(s.unmuxedAddressToID, nil).Once() s.mockQ.On("NewTransactionParticipantsBatchInsertBuilder", maxBatchSize). Return(s.mockBatchInsertBuilder).Once() s.mockQ.On("NewOperationParticipantBatchInsertBuilder", maxBatchSize). diff --git a/services/horizon/internal/expingest/processors/trades_processor.go b/services/horizon/internal/expingest/processors/trades_processor.go index c1c7e558d7..63acaf5609 100644 --- a/services/horizon/internal/expingest/processors/trades_processor.go +++ b/services/horizon/internal/expingest/processors/trades_processor.go @@ -206,9 +206,10 @@ func (p *TradeProcessor) extractTrades( var buyerAddress string if buyer := op.SourceAccount; buyer != nil { - buyerAddress = buyer.Address() + accid := buyer.ToAccountId() + buyerAddress = accid.Address() } else { - sa := transaction.Envelope.SourceAccount() + sa := transaction.Envelope.SourceAccount().ToAccountId() buyerAddress = sa.Address() } buyerAccounts = append(buyerAccounts, buyerAddress) diff --git a/services/horizon/internal/expingest/processors/trades_processor_test.go b/services/horizon/internal/expingest/processors/trades_processor_test.go index 08f3b5c079..df7b7654e8 100644 --- a/services/horizon/internal/expingest/processors/trades_processor_test.go +++ b/services/horizon/internal/expingest/processors/trades_processor_test.go @@ -19,8 +19,10 @@ type TradeProcessorTestSuiteLedger struct { mockQ *history.MockQTrades mockBatchInsertBuilder *history.MockTradeBatchInsertBuilder - sourceAccount xdr.AccountId - opSourceAccount xdr.AccountId + unmuxedSourceAccount xdr.AccountId + unmuxedOpSourceAccount xdr.AccountId + sourceAccount xdr.MuxedAccount + opSourceAccount xdr.MuxedAccount strictReceiveTrade xdr.ClaimOfferAtom strictSendTrade xdr.ClaimOfferAtom buyOfferTrade xdr.ClaimOfferAtom @@ -32,8 +34,8 @@ type TradeProcessorTestSuiteLedger struct { assets []xdr.Asset - accountToID map[string]int64 - assetToID map[string]history.Asset + unmuxedAccountToID map[string]int64 + assetToID map[string]history.Asset txs []io.LedgerTransaction } @@ -46,60 +48,74 @@ func (s *TradeProcessorTestSuiteLedger) SetupTest() { s.mockQ = &history.MockQTrades{} s.mockBatchInsertBuilder = &history.MockTradeBatchInsertBuilder{} - s.sourceAccount = xdr.MustAddress("GAUJETIZVEP2NRYLUESJ3LS66NVCEGMON4UDCBCSBEVPIID773P2W6AY") - s.opSourceAccount = xdr.MustAddress("GC3C4AKRBQLHOJ45U4XG35ESVWRDECWO5XLDGYADO6DPR3L7KIDVUMML") + s.unmuxedSourceAccount = xdr.MustAddress("GAUJETIZVEP2NRYLUESJ3LS66NVCEGMON4UDCBCSBEVPIID773P2W6AY") + s.sourceAccount = xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xdeadbeef, + Ed25519: *s.unmuxedSourceAccount.Ed25519, + }, + } + s.unmuxedOpSourceAccount = xdr.MustAddress("GC3C4AKRBQLHOJ45U4XG35ESVWRDECWO5XLDGYADO6DPR3L7KIDVUMML") + s.opSourceAccount = xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xcafebabe, + Ed25519: *s.unmuxedOpSourceAccount.Ed25519, + }, + } s.strictReceiveTrade = xdr.ClaimOfferAtom{ SellerId: xdr.MustAddress("GA2YS6YBWIBUMUJCNYROC5TXYTTUA4TCZF7A4MJ2O4TTGT3LFNWIOMY4"), OfferId: 11, AssetSold: xdr.MustNewNativeAsset(), AmountSold: 111, AmountBought: 211, - AssetBought: xdr.MustNewCreditAsset("HUF", s.sourceAccount.Address()), + AssetBought: xdr.MustNewCreditAsset("HUF", s.unmuxedSourceAccount.Address()), } s.strictSendTrade = xdr.ClaimOfferAtom{ SellerId: xdr.MustAddress("GALOBQKDZUSAEUDE7F4OYUIQTUZBL62G6TRCXU2ED6SA7TL72MBUQSYJ"), OfferId: 12, - AssetSold: xdr.MustNewCreditAsset("USD", s.sourceAccount.Address()), + AssetSold: xdr.MustNewCreditAsset("USD", s.unmuxedSourceAccount.Address()), AmountSold: 112, AmountBought: 212, - AssetBought: xdr.MustNewCreditAsset("RUB", s.sourceAccount.Address()), + AssetBought: xdr.MustNewCreditAsset("RUB", s.unmuxedSourceAccount.Address()), } s.buyOfferTrade = xdr.ClaimOfferAtom{ SellerId: xdr.MustAddress("GCWRLPH5X5A3GABFDLDILZ4RLY6O76AYOIIR5H2PAI6TNZZZNLZWBXSH"), OfferId: 13, - AssetSold: xdr.MustNewCreditAsset("EUR", s.sourceAccount.Address()), + AssetSold: xdr.MustNewCreditAsset("EUR", s.unmuxedSourceAccount.Address()), AmountSold: 113, AmountBought: 213, - AssetBought: xdr.MustNewCreditAsset("NOK", s.sourceAccount.Address()), + AssetBought: xdr.MustNewCreditAsset("NOK", s.unmuxedSourceAccount.Address()), } s.sellOfferTrade = xdr.ClaimOfferAtom{ SellerId: xdr.MustAddress("GAVOLNFXVVUJOELN4T3YVSH2FFA3VSP2XN4NJRYF2ZWVCHS77C5KXLHZ"), OfferId: 14, - AssetSold: xdr.MustNewCreditAsset("PLN", s.sourceAccount.Address()), + AssetSold: xdr.MustNewCreditAsset("PLN", s.unmuxedSourceAccount.Address()), AmountSold: 114, AmountBought: 214, - AssetBought: xdr.MustNewCreditAsset("UAH", s.sourceAccount.Address()), + AssetBought: xdr.MustNewCreditAsset("UAH", s.unmuxedSourceAccount.Address()), } s.passiveSellOfferTrade = xdr.ClaimOfferAtom{ SellerId: xdr.MustAddress("GDQWI6FKB72DPOJE4CGYCFQZKRPQQIOYXRMZ5KEVGXMG6UUTGJMBCASH"), OfferId: 15, - AssetSold: xdr.MustNewCreditAsset("SEK", s.sourceAccount.Address()), + AssetSold: xdr.MustNewCreditAsset("SEK", s.unmuxedSourceAccount.Address()), AmountSold: 115, AmountBought: 215, - AssetBought: xdr.MustNewCreditAsset("GBP", s.sourceAccount.Address()), + AssetBought: xdr.MustNewCreditAsset("GBP", s.unmuxedSourceAccount.Address()), } s.otherPassiveSellOfferTrade = xdr.ClaimOfferAtom{ SellerId: xdr.MustAddress("GCPZFOJON3PSSYUBNT7MCGEDSGP47UTSJSB4XGCVEWEJO4XQ6U4XN3N2"), OfferId: 16, - AssetSold: xdr.MustNewCreditAsset("CHF", s.sourceAccount.Address()), + AssetSold: xdr.MustNewCreditAsset("CHF", s.unmuxedSourceAccount.Address()), AmountSold: 116, AmountBought: 216, - AssetBought: xdr.MustNewCreditAsset("JPY", s.sourceAccount.Address()), + AssetBought: xdr.MustNewCreditAsset("JPY", s.unmuxedSourceAccount.Address()), } - s.accountToID = map[string]int64{ - s.sourceAccount.Address(): 1000, - s.opSourceAccount.Address(): 1001, + s.unmuxedAccountToID = map[string]int64{ + s.unmuxedSourceAccount.Address(): 1000, + s.unmuxedOpSourceAccount.Address(): 1001, } s.assetToID = map[string]history.Asset{} s.allTrades = []xdr.ClaimOfferAtom{ @@ -114,7 +130,7 @@ func (s *TradeProcessorTestSuiteLedger) SetupTest() { s.assets = []xdr.Asset{} s.sellPrices = []xdr.Price{} for i, trade := range s.allTrades { - s.accountToID[trade.SellerId.Address()] = int64(1002 + i) + s.unmuxedAccountToID[trade.SellerId.Address()] = int64(1002 + i) s.assetToID[trade.AssetSold.String()] = history.Asset{ID: int64(10000 + i)} s.assetToID[trade.AssetBought.String()] = history.Asset{ID: int64(100 + i)} s.assets = append(s.assets, trade.AssetSold, trade.AssetBought) @@ -150,79 +166,79 @@ func (s *TradeProcessorTestSuiteLedger) mockReadTradeTransactions( ) []history.InsertTrade { closeTime := time.Unix(int64(ledger.Header.ScpValue.CloseTime), 0).UTC() inserts := []history.InsertTrade{ - history.InsertTrade{ + { HistoryOperationID: toid.New(int32(ledger.Header.LedgerSeq), 1, 2).ToInt64(), Order: 1, LedgerCloseTime: closeTime, BuyOfferExists: false, BuyOfferID: 0, - SellerAccountID: s.accountToID[s.strictReceiveTrade.SellerId.Address()], - BuyerAccountID: s.accountToID[s.opSourceAccount.Address()], + SellerAccountID: s.unmuxedAccountToID[s.strictReceiveTrade.SellerId.Address()], + BuyerAccountID: s.unmuxedAccountToID[s.unmuxedOpSourceAccount.Address()], Trade: s.strictReceiveTrade, SoldAssetID: s.assetToID[s.strictReceiveTrade.AssetSold.String()].ID, BoughtAssetID: s.assetToID[s.strictReceiveTrade.AssetBought.String()].ID, SellPrice: s.sellPrices[0], }, - history.InsertTrade{ + { HistoryOperationID: toid.New(int32(ledger.Header.LedgerSeq), 1, 3).ToInt64(), Order: 0, LedgerCloseTime: closeTime, BuyOfferExists: false, BuyOfferID: 0, - SellerAccountID: s.accountToID[s.strictSendTrade.SellerId.Address()], - BuyerAccountID: s.accountToID[s.opSourceAccount.Address()], + SellerAccountID: s.unmuxedAccountToID[s.strictSendTrade.SellerId.Address()], + BuyerAccountID: s.unmuxedAccountToID[s.unmuxedOpSourceAccount.Address()], Trade: s.strictSendTrade, SoldAssetID: s.assetToID[s.strictSendTrade.AssetSold.String()].ID, BoughtAssetID: s.assetToID[s.strictSendTrade.AssetBought.String()].ID, SellPrice: s.sellPrices[1], }, - history.InsertTrade{ + { HistoryOperationID: toid.New(int32(ledger.Header.LedgerSeq), 1, 4).ToInt64(), Order: 1, LedgerCloseTime: closeTime, BuyOfferExists: true, BuyOfferID: 879136, - SellerAccountID: s.accountToID[s.buyOfferTrade.SellerId.Address()], - BuyerAccountID: s.accountToID[s.opSourceAccount.Address()], + SellerAccountID: s.unmuxedAccountToID[s.buyOfferTrade.SellerId.Address()], + BuyerAccountID: s.unmuxedAccountToID[s.unmuxedOpSourceAccount.Address()], Trade: s.buyOfferTrade, SoldAssetID: s.assetToID[s.buyOfferTrade.AssetSold.String()].ID, BoughtAssetID: s.assetToID[s.buyOfferTrade.AssetBought.String()].ID, SellPrice: s.sellPrices[2], }, - history.InsertTrade{ + { HistoryOperationID: toid.New(int32(ledger.Header.LedgerSeq), 1, 5).ToInt64(), Order: 2, LedgerCloseTime: closeTime, BuyOfferExists: false, BuyOfferID: 0, - SellerAccountID: s.accountToID[s.sellOfferTrade.SellerId.Address()], - BuyerAccountID: s.accountToID[s.opSourceAccount.Address()], + SellerAccountID: s.unmuxedAccountToID[s.sellOfferTrade.SellerId.Address()], + BuyerAccountID: s.unmuxedAccountToID[s.unmuxedOpSourceAccount.Address()], Trade: s.sellOfferTrade, SoldAssetID: s.assetToID[s.sellOfferTrade.AssetSold.String()].ID, BoughtAssetID: s.assetToID[s.sellOfferTrade.AssetBought.String()].ID, SellPrice: s.sellPrices[3], }, - history.InsertTrade{ + { HistoryOperationID: toid.New(int32(ledger.Header.LedgerSeq), 1, 6).ToInt64(), Order: 0, LedgerCloseTime: closeTime, BuyOfferExists: false, BuyOfferID: 0, - SellerAccountID: s.accountToID[s.passiveSellOfferTrade.SellerId.Address()], - BuyerAccountID: s.accountToID[s.sourceAccount.Address()], + SellerAccountID: s.unmuxedAccountToID[s.passiveSellOfferTrade.SellerId.Address()], + BuyerAccountID: s.unmuxedAccountToID[s.unmuxedSourceAccount.Address()], Trade: s.passiveSellOfferTrade, SoldAssetID: s.assetToID[s.passiveSellOfferTrade.AssetSold.String()].ID, BoughtAssetID: s.assetToID[s.passiveSellOfferTrade.AssetBought.String()].ID, SellPrice: s.sellPrices[4], }, - history.InsertTrade{ + { HistoryOperationID: toid.New(int32(ledger.Header.LedgerSeq), 1, 7).ToInt64(), Order: 0, LedgerCloseTime: closeTime, BuyOfferExists: false, BuyOfferID: 0, - SellerAccountID: s.accountToID[s.otherPassiveSellOfferTrade.SellerId.Address()], - BuyerAccountID: s.accountToID[s.opSourceAccount.Address()], + SellerAccountID: s.unmuxedAccountToID[s.otherPassiveSellOfferTrade.SellerId.Address()], + BuyerAccountID: s.unmuxedAccountToID[s.unmuxedOpSourceAccount.Address()], Trade: s.otherPassiveSellOfferTrade, SoldAssetID: s.assetToID[s.otherPassiveSellOfferTrade.AssetSold.String()].ID, BoughtAssetID: s.assetToID[s.otherPassiveSellOfferTrade.AssetBought.String()].ID, @@ -231,16 +247,16 @@ func (s *TradeProcessorTestSuiteLedger) mockReadTradeTransactions( } emptyTrade := xdr.ClaimOfferAtom{ - SellerId: s.sourceAccount, + SellerId: s.sourceAccount.ToAccountId(), OfferId: 123, AssetSold: xdr.MustNewNativeAsset(), AmountSold: 0, - AssetBought: xdr.MustNewCreditAsset("EUR", s.sourceAccount.Address()), + AssetBought: xdr.MustNewCreditAsset("EUR", s.unmuxedSourceAccount.Address()), AmountBought: 0, } operationResults := []xdr.OperationResult{ - xdr.OperationResult{ + { Tr: &xdr.OperationResultTr{ Type: xdr.OperationTypeBumpSequence, BumpSeqResult: &xdr.BumpSequenceResult{ @@ -248,7 +264,7 @@ func (s *TradeProcessorTestSuiteLedger) mockReadTradeTransactions( }, }, }, - xdr.OperationResult{ + { Tr: &xdr.OperationResultTr{ Type: xdr.OperationTypePathPaymentStrictReceive, PathPaymentStrictReceiveResult: &xdr.PathPaymentStrictReceiveResult{ @@ -262,7 +278,7 @@ func (s *TradeProcessorTestSuiteLedger) mockReadTradeTransactions( }, }, }, - xdr.OperationResult{ + { Tr: &xdr.OperationResultTr{ Type: xdr.OperationTypePathPaymentStrictSend, PathPaymentStrictSendResult: &xdr.PathPaymentStrictSendResult{ @@ -276,7 +292,7 @@ func (s *TradeProcessorTestSuiteLedger) mockReadTradeTransactions( }, }, }, - xdr.OperationResult{ + { Tr: &xdr.OperationResultTr{ Type: xdr.OperationTypeManageBuyOffer, ManageBuyOfferResult: &xdr.ManageBuyOfferResult{ @@ -296,7 +312,7 @@ func (s *TradeProcessorTestSuiteLedger) mockReadTradeTransactions( }, }, }, - xdr.OperationResult{ + { Tr: &xdr.OperationResultTr{ Type: xdr.OperationTypeManageSellOffer, ManageSellOfferResult: &xdr.ManageSellOfferResult{ @@ -314,7 +330,7 @@ func (s *TradeProcessorTestSuiteLedger) mockReadTradeTransactions( }, }, }, - xdr.OperationResult{ + { Tr: &xdr.OperationResultTr{ Type: xdr.OperationTypeManageSellOffer, ManageSellOfferResult: &xdr.ManageSellOfferResult{ @@ -332,7 +348,7 @@ func (s *TradeProcessorTestSuiteLedger) mockReadTradeTransactions( }, }, }, - xdr.OperationResult{ + { Tr: &xdr.OperationResultTr{ Type: xdr.OperationTypeCreatePassiveSellOffer, CreatePassiveSellOfferResult: &xdr.ManageSellOfferResult{ @@ -351,47 +367,47 @@ func (s *TradeProcessorTestSuiteLedger) mockReadTradeTransactions( } operations := []xdr.Operation{ - xdr.Operation{ + { Body: xdr.OperationBody{ Type: xdr.OperationTypeBumpSequence, BumpSequenceOp: &xdr.BumpSequenceOp{BumpTo: 30000}, }, }, - xdr.Operation{ + { Body: xdr.OperationBody{ Type: xdr.OperationTypePathPaymentStrictReceive, PathPaymentStrictReceiveOp: &xdr.PathPaymentStrictReceiveOp{}, }, SourceAccount: &s.opSourceAccount, }, - xdr.Operation{ + { Body: xdr.OperationBody{ Type: xdr.OperationTypePathPaymentStrictSend, PathPaymentStrictSendOp: &xdr.PathPaymentStrictSendOp{}, }, SourceAccount: &s.opSourceAccount, }, - xdr.Operation{ + { Body: xdr.OperationBody{ Type: xdr.OperationTypeManageBuyOffer, ManageBuyOfferOp: &xdr.ManageBuyOfferOp{}, }, SourceAccount: &s.opSourceAccount, }, - xdr.Operation{ + { Body: xdr.OperationBody{ Type: xdr.OperationTypeManageSellOffer, ManageSellOfferOp: &xdr.ManageSellOfferOp{}, }, SourceAccount: &s.opSourceAccount, }, - xdr.Operation{ + { Body: xdr.OperationBody{ Type: xdr.OperationTypeCreatePassiveSellOffer, CreatePassiveSellOfferOp: &xdr.CreatePassiveSellOfferOp{}, }, }, - xdr.Operation{ + { Body: xdr.OperationBody{ Type: xdr.OperationTypeCreatePassiveSellOffer, CreatePassiveSellOfferOp: &xdr.CreatePassiveSellOfferOp{}, @@ -424,7 +440,7 @@ func (s *TradeProcessorTestSuiteLedger) mockReadTradeTransactions( V: 2, V2: &xdr.TransactionMetaV2{ Operations: []xdr.OperationMeta{ - xdr.OperationMeta{ + { Changes: xdr.LedgerEntryChanges{}, }, }, @@ -479,10 +495,10 @@ func (s *TradeProcessorTestSuiteLedger) TestIngestTradesSucceeds() { Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - mapKeysToList(s.accountToID), + mapKeysToList(s.unmuxedAccountToID), arg, ) - }).Return(s.accountToID, nil).Once() + }).Return(s.unmuxedAccountToID, nil).Once() s.mockQ.On("CreateAssets", mock.AnythingOfType("[]xdr.Asset"), maxBatchSize). Run(func(args mock.Arguments) { @@ -517,7 +533,7 @@ func (s *TradeProcessorTestSuiteLedger) TestCreateAccountsError() { Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - mapKeysToList(s.accountToID), + mapKeysToList(s.unmuxedAccountToID), arg, ) }).Return(map[string]int64{}, fmt.Errorf("create accounts error")).Once() @@ -539,10 +555,10 @@ func (s *TradeProcessorTestSuiteLedger) TestCreateAssetsError() { Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - mapKeysToList(s.accountToID), + mapKeysToList(s.unmuxedAccountToID), arg, ) - }).Return(s.accountToID, nil).Once() + }).Return(s.unmuxedAccountToID, nil).Once() s.mockQ.On("CreateAssets", mock.AnythingOfType("[]xdr.Asset"), maxBatchSize). Run(func(args mock.Arguments) { @@ -569,10 +585,10 @@ func (s *TradeProcessorTestSuiteLedger) TestBatchAddError() { Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - mapKeysToList(s.accountToID), + mapKeysToList(s.unmuxedAccountToID), arg, ) - }).Return(s.accountToID, nil).Once() + }).Return(s.unmuxedAccountToID, nil).Once() s.mockQ.On("CreateAssets", mock.AnythingOfType("[]xdr.Asset"), maxBatchSize). Run(func(args mock.Arguments) { @@ -602,10 +618,10 @@ func (s *TradeProcessorTestSuiteLedger) TestBatchExecError() { Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - mapKeysToList(s.accountToID), + mapKeysToList(s.unmuxedAccountToID), arg, ) - }).Return(s.accountToID, nil).Once() + }).Return(s.unmuxedAccountToID, nil).Once() s.mockQ.On("CreateAssets", mock.AnythingOfType("[]xdr.Asset"), maxBatchSize). Run(func(args mock.Arguments) { @@ -635,10 +651,10 @@ func (s *TradeProcessorTestSuiteLedger) TestIgnoreCheckIfSmallLedger() { Run(func(args mock.Arguments) { arg := args.Get(0).([]string) s.Assert().ElementsMatch( - mapKeysToList(s.accountToID), + mapKeysToList(s.unmuxedAccountToID), arg, ) - }).Return(s.accountToID, nil).Once() + }).Return(s.unmuxedAccountToID, nil).Once() s.mockQ.On("CreateAssets", mock.AnythingOfType("[]xdr.Asset"), maxBatchSize). Run(func(args mock.Arguments) { @@ -661,3 +677,24 @@ func (s *TradeProcessorTestSuiteLedger) TestIgnoreCheckIfSmallLedger() { err := s.processor.Commit() s.Assert().NoError(err) } + +func TestTradeProcessor_ProcessTransaction_MuxedAccount(t *testing.T) { + unmuxed := xdr.MustAddress("GA5WBPYA5Y4WAEHXWR2UKO2UO4BUGHUQ74EUPKON2QHV4WRHOIRNKKH2") + muxed := xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xdeadbeefdeadbeef, + Ed25519: *unmuxed.Ed25519, + }, + } + tx := createTransaction(true, 1) + tx.Index = 1 + tx.Envelope.Operations()[0].Body = xdr.OperationBody{ + Type: xdr.OperationTypePayment, + PaymentOp: &xdr.PaymentOp{ + Destination: muxed, + Asset: xdr.Asset{Type: xdr.AssetTypeAssetTypeNative}, + Amount: 100, + }, + } +} diff --git a/services/horizon/internal/expingest/processors/transaction_operation_wrapper_test.go b/services/horizon/internal/expingest/processors/transaction_operation_wrapper_test.go index 1b0ce4ce76..f77951fc36 100644 --- a/services/horizon/internal/expingest/processors/transaction_operation_wrapper_test.go +++ b/services/horizon/internal/expingest/processors/transaction_operation_wrapper_test.go @@ -119,7 +119,7 @@ func TestOperationTransactionSourceAccount(t *testing.T) { ) op := transaction.Envelope.Operations()[0] if len(tc.sourceAccount) > 0 { - sourceAccount := xdr.MustAddress(tc.sourceAccount) + sourceAccount := xdr.MustMuxedAccountAddress(tc.sourceAccount) op.SourceAccount = &sourceAccount } @@ -536,7 +536,7 @@ func TestTransactionOperationDetails(t *testing.T) { "asset_type": "native", "destination_min": "10.0000000", "from": "GDVQFXKLSASXPCK2L3UNSD2WP5SG6ZRB4IOKPUTO2DG3C6JH3TJSEA7R", - "path": []map[string]interface{}{map[string]interface{}{"asset_type": "native"}}, + "path": []map[string]interface{}{{"asset_type": "native"}}, "source_amount": "10.0000000", "source_asset_type": "native", "to": "GDQVLGCJPJ57SALDJPH3YWZVGYUJJTRUUIEJGYKHLTSFY3ZOBKU3QIO3", @@ -864,7 +864,7 @@ func TestTransactionOperationAllowTrustDetails(t *testing.T) { allowTrustAsset, err := asset.ToAllowTrustOpAsset("COP") tt.NoError(err) - source := xdr.MustAddress("GDRW375MAYR46ODGF2WGANQC2RRZL7O246DYHHCGWTV2RE7IHE2QUQLD") + source := xdr.MustMuxedAccountAddress("GDRW375MAYR46ODGF2WGANQC2RRZL7O246DYHHCGWTV2RE7IHE2QUQLD") testCases := []struct { desc string diff --git a/services/horizon/internal/txsub/system.go b/services/horizon/internal/txsub/system.go index 49e3caf3fd..36bdeda2ee 100644 --- a/services/horizon/internal/txsub/system.go +++ b/services/horizon/internal/txsub/system.go @@ -86,7 +86,10 @@ func (sys *System) Submit( // From now: r.Err == ErrNoResults sourceAccount := envelope.SourceAccount() - sourceAddress := sourceAccount.Address() + // The database doesn't (yet) store muxed accounts, so we query + // the corresponding AccountId + accid := sourceAccount.ToAccountId() + sourceAddress := accid.Address() curSeq, err := sys.Sequences.Get([]string{sourceAddress}) if err != nil { sys.finish(ctx, hash, response, Result{Err: err}) diff --git a/services/horizon/internal/txsub/system_test.go b/services/horizon/internal/txsub/system_test.go index e29af38170..3b502ec6eb 100644 --- a/services/horizon/internal/txsub/system_test.go +++ b/services/horizon/internal/txsub/system_test.go @@ -4,29 +4,32 @@ import ( "context" "errors" "fmt" - "github.com/guregu/null" "testing" "time" + "github.com/guregu/null" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "github.com/stellar/go/services/horizon/internal/db2/history" "github.com/stellar/go/services/horizon/internal/test" "github.com/stellar/go/services/horizon/internal/txsub/sequence" "github.com/stellar/go/xdr" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" ) type SystemTestSuite struct { suite.Suite - ctx context.Context - submitter *MockSubmitter - results *MockResultProvider - sequences *MockSequenceProvider - system *System - noResults Result - successTx Result - successXDR xdr.TransactionEnvelope - badSeq SubmissionResult + ctx context.Context + submitter *MockSubmitter + results *MockResultProvider + sequences *MockSequenceProvider + system *System + noResults Result + successTx Result + successXDR xdr.TransactionEnvelope + badSeq SubmissionResult + unmuxedSource xdr.AccountId } func (suite *SystemTestSuite) SetupTest() { @@ -46,12 +49,49 @@ func (suite *SystemTestSuite) SetupTest() { SubmissionQueue: sequence.NewManager(), } + suite.unmuxedSource = xdr.MustAddress("GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H") + source := xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xcafebabe, + Ed25519: *suite.unmuxedSource.Ed25519, + }, + } + + tx := xdr.TransactionEnvelope{ + Type: xdr.EnvelopeTypeEnvelopeTypeTx, + V1: &xdr.TransactionV1Envelope{ + Tx: xdr.Transaction{ + SourceAccount: source, + Fee: 100, + SeqNum: 1, + Operations: []xdr.Operation{ + { + Body: xdr.OperationBody{ + CreateAccountOp: &xdr.CreateAccountOp{ + Destination: xdr.MustAddress("GCXKG6RN4ONIEPCMNFB732A436Z5PNDSRLGWK7GBLCMQLIFO4S7EYWVU"), + StartingBalance: 1000000000, + }, + }, + }, + }, + }, + Signatures: []xdr.DecoratedSignature{ + { + Hint: xdr.SignatureHint{86, 252, 5, 247}, + Signature: xdr.Signature{131, 206, 171, 228, 64, 20, 40, 52, 2, 98, 124, 244, 87, 14, 130, 225, 190, 220, 156, 79, 121, 69, 60, 36, 57, 214, 9, 29, 176, 81, 218, 4, 213, 176, 211, 148, 191, 86, 21, 180, 94, 9, 43, 208, 32, 79, 19, 131, 90, 21, 93, 138, 153, 203, 55, 103, 2, 230, 137, 190, 19, 70, 179, 11}, + }, + }, + }, + } + suite.noResults = Result{Err: ErrNoResults} + envelopeBase64, _ := xdr.MarshalBase64(tx) suite.successTx = Result{ Transaction: history.Transaction{ TransactionHash: "2374e99349b9ef7dba9a5db3339b78fda8f34777b1af33ba468ad5c0df946d4d", LedgerSequence: 2, - TxEnvelope: "AAAAAGL8HQvQkbK2HA3WVjRrKmjX00fG8sLI7m0ERwJW/AX3AAAAZAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAArqN6LeOagjxMaUP96Bzfs9e0corNZXzBWJkFoK7kvkwAAAAAO5rKAAAAAAAAAAABVvwF9wAAAECDzqvkQBQoNAJifPRXDoLhvtycT3lFPCQ51gkdsFHaBNWw05S/VhW0Xgkr0CBPE4NaFV2Kmcs3ZwLmib4TRrML", + TxEnvelope: envelopeBase64, TxResult: "I3Tpk0m57326ml2zM5t4/ajzR3exrzO6RorVwN+UbU0AAAAAAAAAZAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA==", }, } @@ -61,8 +101,8 @@ func (suite *SystemTestSuite) SetupTest() { Err: ErrBadSequence, } - suite.sequences.On("Get", []string{"GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H"}). - Return(map[string]uint64{"GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H": 0}, nil). + suite.sequences.On("Get", []string{suite.unmuxedSource.Address()}). + Return(map[string]uint64{suite.unmuxedSource.Address(): 0}, nil). Once() } diff --git a/strkey/decode_test.go b/strkey/decode_test.go index 953e846120..790f0fe2fc 100644 --- a/strkey/decode_test.go +++ b/strkey/decode_test.go @@ -24,6 +24,18 @@ func TestDecode(t *testing.T) { 0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03, }, }, + { + Name: "MuxedAccount", + Address: "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + ExpectedVersionByte: VersionByteMuxedAccount, + ExpectedPayload: []byte{ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x0c, 0x34, 0xbf, 0x93, 0xad, 0x0d, 0x99, + 0x71, 0xd0, 0x4c, 0xcc, 0x90, 0xf7, 0x05, 0x51, + 0x1c, 0x83, 0x8a, 0xad, 0x97, 0x34, 0xa4, 0xa2, + 0xfb, 0x0d, 0x7a, 0x03, 0xfc, 0x7f, 0xe8, 0x9a, + }, + }, { Name: "Seed", Address: "SBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWHOKR", @@ -85,4 +97,66 @@ func TestDecode(t *testing.T) { // corrupted payload _, err = Decode(VersionByteAccountID, "GA3D5KRYM6CB7OWOOOORR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5") assert.Error(t, err) + + // non-canonical representation due to extra character + _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOGA") + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "unused leftover character") + } + + // non-canonical representation due to leftover bits set to 1 (some of the test strkeys are too short for a muxed account + // but they comply with the test's purpose all the same) + + // 1 unused bit (length 69) + _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOH") + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "unused bits should be set to 0") + } + + // 4 unused bits (length 68) + + // 'B' is equivalent to 0b00001 + _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNB") + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "unused bits should be set to 0") + } + // 'C' is equivalent to 0b00010 + _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNC") + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "unused bits should be set to 0") + } + // 'E' is equivalent to 0b00100 + _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNE") + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "unused bits should be set to 0") + } + // 'I' is equivalent to 0b01000 + _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNI") + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "unused bits should be set to 0") + } + // '7' is equivalent to 0b11111 + _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKN7") + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "unused bits should be set to 0") + } + // '6' is equivalent to 0b11110 + _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKN6") + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "unused bits should be set to 0") + } + // '4' is equivalent to 0b11100 + _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKN4") + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "unused bits should be set to 0") + } + // 'Y' is equivalent to 0b11000 + _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNY") + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "unused bits should be set to 0") + } + + _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNQ") + // 'Q' is equivalent to 0b10000, so there should be no error + assert.NotContains(t, err.Error(), "unused bits should be set to 0") } diff --git a/strkey/encode_test.go b/strkey/encode_test.go index b020074406..4111fd0f12 100644 --- a/strkey/encode_test.go +++ b/strkey/encode_test.go @@ -24,6 +24,18 @@ func TestEncode(t *testing.T) { }, Expected: "GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5", }, + { + Name: "MuxedAccount", + VersionByte: VersionByteMuxedAccount, + Payload: []byte{ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x0c, 0x34, 0xbf, 0x93, 0xad, 0x0d, 0x99, + 0x71, 0xd0, 0x4c, 0xcc, 0x90, 0xf7, 0x05, 0x51, + 0x1c, 0x83, 0x8a, 0xad, 0x97, 0x34, 0xa4, 0xa2, + 0xfb, 0x0d, 0x7a, 0x03, 0xfc, 0x7f, 0xe8, 0x9a, + }, + Expected: "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + }, { Name: "Seed", VersionByte: VersionByteSeed, diff --git a/strkey/main.go b/strkey/main.go index ae28b35a04..2b8ed7686b 100644 --- a/strkey/main.go +++ b/strkey/main.go @@ -21,6 +21,9 @@ const ( //VersionByteAccountID is the version byte used for encoded stellar addresses VersionByteAccountID VersionByte = 6 << 3 // Base32-encodes to 'G...' + //VersionByteAccountID is the version byte used for encoded stellar multiplexed addresses + VersionByteMuxedAccount = 12 << 3 // Base32-encodes to 'M...' + //VersionByteSeed is the version byte used for encoded stellar seed VersionByteSeed = 18 << 3 // Base32-encodes to 'S...' @@ -128,7 +131,7 @@ func Encode(version VersionByte, src []byte) (string, error) { return "", err } - result := base32.StdEncoding.EncodeToString(raw.Bytes()) + result := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(raw.Bytes()) return result, nil } @@ -159,6 +162,10 @@ func checkValidVersionByte(version VersionByte) error { return nil } + if version == VersionByteMuxedAccount { + return nil + } + if version == VersionByteSeed { return nil } @@ -174,20 +181,55 @@ func checkValidVersionByte(version VersionByte) error { return ErrInvalidVersionByte } +var decodingTable = initDecodingTable() + +func initDecodingTable() [256]byte { + var localDecodingTable [256]byte + for i := range localDecodingTable { + localDecodingTable[i] = 0xff + } + for i, ch := range []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") { + localDecodingTable[ch] = byte(i) + } + return localDecodingTable +} + // decodeString decodes a base32 string into the raw bytes, and ensures it could // potentially be strkey encoded (i.e. it has both a version byte and a // checksum, neither of which are explicitly checked by this func) func decodeString(src string) ([]byte, error) { - raw, err := base32.StdEncoding.DecodeString(src) + // operations on strings are expensive since it involves unicode parsing + // so, we use bytes from the beginning + srcBytes := []byte(src) + // The minimal binary decoded length is 3 bytes (version byte and 2-byte CRC) which, + // in unpadded base32 (since each character provides 5 bits) corresponds to ceiling(8*3/5) = 5 + if len(srcBytes) < 5 { + return nil, errors.Errorf("strkey is %d bytes long; minimum valid length is 5", len(srcBytes)) + } + // SEP23 enforces strkeys to be in canonical base32 representation. + // Go's decoder doesn't help us there, so we need to do it ourselves. + // 1. Make sure there is no full unused leftover byte at the end + // (i.e. there shouldn't be 5 or more leftover bits) + leftoverBits := (len(srcBytes) * 5) % 8 + if leftoverBits >= 5 { + return nil, errors.New("non-canonical strkey; unused leftover character") + } + // 2. In the last byte of the strkey there may be leftover bits (4 at most, otherwise it would be a full byte, + // which we have for checked above). If there are any leftover bits, they should be set to 0 + if leftoverBits > 0 { + lastChar := srcBytes[len(srcBytes)-1] + decodedLastChar := decodingTable[lastChar] + leftoverBitsMask := byte(0x0f) >> (4 - leftoverBits) + if decodedLastChar&leftoverBitsMask != 0 { + return nil, errors.New("non-canonical strkey; unused bits should be set to 0") + } + } + n, err := base32.StdEncoding.WithPadding(base32.NoPadding).Decode(srcBytes, srcBytes) if err != nil { return nil, errors.Wrap(err, "base32 decode failed") } - if len(raw) < 3 { - return nil, errors.Errorf("encoded value is %d bytes; minimum valid length is 3", len(raw)) - } - - return raw, nil + return srcBytes[:n], nil } // IsValidEd25519PublicKey validates a stellar public key diff --git a/txnbuild/account_merge.go b/txnbuild/account_merge.go index 6c7903f017..197c6f8110 100644 --- a/txnbuild/account_merge.go +++ b/txnbuild/account_merge.go @@ -14,7 +14,7 @@ type AccountMerge struct { // BuildXDR for AccountMerge returns a fully configured XDR Operation. func (am *AccountMerge) BuildXDR() (xdr.Operation, error) { - var xdrOp xdr.AccountId + var xdrOp xdr.MuxedAccount err := xdrOp.SetAddress(am.Destination) if err != nil { diff --git a/txnbuild/asset_test.go b/txnbuild/asset_test.go index c2895db3e2..fc0926d99f 100644 --- a/txnbuild/asset_test.go +++ b/txnbuild/asset_test.go @@ -3,10 +3,10 @@ package txnbuild import ( "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stellar/go/xdr" - "github.com/stretchr/testify/assert" ) func TestNativeAssetToXDR(t *testing.T) { @@ -99,6 +99,6 @@ func TestBadIssuer(t *testing.T) { var xdrAssetCode [4]byte copy(xdrAssetCode[:], asset.Code) var xdrIssuer xdr.AccountId - expectedErrMsg := "base32 decode failed: illegal base32 data at input byte 16" + expectedErrMsg := "non-canonical strkey; unused bits should be set to 0" require.EqualError(t, xdrIssuer.SetAddress(asset.Issuer), expectedErrMsg, "Issuer address should be validated") } diff --git a/txnbuild/operation.go b/txnbuild/operation.go index 9c1e94a03e..bd0139a203 100644 --- a/txnbuild/operation.go +++ b/txnbuild/operation.go @@ -17,7 +17,7 @@ func SetOpSourceAccount(op *xdr.Operation, sourceAccount Account) { if sourceAccount == nil { return } - var opSourceAccountID xdr.AccountId + var opSourceAccountID xdr.MuxedAccount opSourceAccountID.SetAddress(sourceAccount.GetAccountID()) op.SourceAccount = &opSourceAccountID } @@ -61,7 +61,7 @@ func operationFromXDR(xdrOp xdr.Operation) (Operation, error) { } // accountFromXDR returns a txnbuild Account from a XDR Account. -func accountFromXDR(account *xdr.AccountId) Account { +func accountFromXDR(account *xdr.MuxedAccount) Account { if account != nil { return &SimpleAccount{AccountID: account.Address()} } diff --git a/txnbuild/operation_test.go b/txnbuild/operation_test.go index 483ff03f70..8635587f14 100644 --- a/txnbuild/operation_test.go +++ b/txnbuild/operation_test.go @@ -215,8 +215,9 @@ func TestSetOptionsFromXDR(t *testing.T) { Signer: &signer, } + muxSource := xdr.MustMuxedAccountAddress(opSource.Address()) xdrOp := xdr.Operation{ - SourceAccount: &opSource, + SourceAccount: &muxSource, Body: xdr.OperationBody{ Type: xdr.OperationTypeSetOptions, SetOptionsOp: &xdrSetOptions, @@ -254,7 +255,7 @@ func TestChangeTrustFromXDR(t *testing.T) { Limit: xdrLimit, } - var opSource xdr.AccountId + var opSource xdr.MuxedAccount err = opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) xdrOp := xdr.Operation{ @@ -284,7 +285,7 @@ func TestAllowTrustFromXDR(t *testing.T) { allowTrustAsset, err := xdrAsset.ToAllowTrustOpAsset("ABCXYZ") assert.NoError(t, err) - var opSource xdr.AccountId + var opSource xdr.MuxedAccount err = opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) @@ -321,11 +322,11 @@ func TestAllowTrustFromXDR(t *testing.T) { } func TestAccountMergeFromXDR(t *testing.T) { - var opSource xdr.AccountId + var opSource xdr.MuxedAccount err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) - var destination xdr.AccountId + var destination xdr.MuxedAccount err = destination.SetAddress("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") assert.NoError(t, err) @@ -346,7 +347,7 @@ func TestAccountMergeFromXDR(t *testing.T) { } func TestInflationFromXDR(t *testing.T) { - var opSource xdr.AccountId + var opSource xdr.MuxedAccount err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) @@ -363,7 +364,7 @@ func TestInflationFromXDR(t *testing.T) { } func TestManageDataFromXDR(t *testing.T) { - var opSource xdr.AccountId + var opSource xdr.MuxedAccount err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) @@ -392,7 +393,7 @@ func TestManageDataFromXDR(t *testing.T) { } func TestBumpSequenceFromXDR(t *testing.T) { - var opSource xdr.AccountId + var opSource xdr.MuxedAccount err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) diff --git a/txnbuild/path_payment.go b/txnbuild/path_payment.go index caa1e030dd..462a754a6b 100644 --- a/txnbuild/path_payment.go +++ b/txnbuild/path_payment.go @@ -42,7 +42,7 @@ func (pp *PathPaymentStrictReceive) BuildXDR() (xdr.Operation, error) { } // Set XDR destination - var xdrDestination xdr.AccountId + var xdrDestination xdr.MuxedAccount err = xdrDestination.SetAddress(pp.Destination) if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") diff --git a/txnbuild/path_payment_strict_send.go b/txnbuild/path_payment_strict_send.go index 621f43a90a..71329a8575 100644 --- a/txnbuild/path_payment_strict_send.go +++ b/txnbuild/path_payment_strict_send.go @@ -36,7 +36,7 @@ func (pp *PathPaymentStrictSend) BuildXDR() (xdr.Operation, error) { } // Set XDR destination - var xdrDestination xdr.AccountId + var xdrDestination xdr.MuxedAccount err = xdrDestination.SetAddress(pp.Destination) if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") diff --git a/txnbuild/payment.go b/txnbuild/payment.go index a16f06fe77..15036c81f0 100644 --- a/txnbuild/payment.go +++ b/txnbuild/payment.go @@ -17,9 +17,9 @@ type Payment struct { // BuildXDR for Payment returns a fully configured XDR Operation. func (p *Payment) BuildXDR() (xdr.Operation, error) { - var destAccountID xdr.AccountId + var destMuxedAccount xdr.MuxedAccount - err := destAccountID.SetAddress(p.Destination) + err := destMuxedAccount.SetAddress(p.Destination) if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") } @@ -39,7 +39,7 @@ func (p *Payment) BuildXDR() (xdr.Operation, error) { opType := xdr.OperationTypePayment xdrOp := xdr.PaymentOp{ - Destination: destAccountID, + Destination: destMuxedAccount, Amount: xdrAmount, Asset: xdrAsset, } diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index cac74ffa18..e57eaa4a90 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -449,6 +449,18 @@ func ReadChallengeTx(challengeTx, serverAccountID, network string) (tx Transacti if err != nil { return tx, clientAccountID, err } + + // Enforce no muxed accounts (at least until we understand their impact) + muxedAccountErr := errors.New("muxed accounts are not allowed in challenge transactions") + if tx.xdrTransaction.SourceAccount.Type != xdr.CryptoKeyTypeKeyTypeEd25519 { + return tx, clientAccountID, muxedAccountErr + } + for _, op := range tx.xdrTransaction.Operations { + if op.SourceAccount != nil && op.SourceAccount.Type != xdr.CryptoKeyTypeKeyTypeEd25519 { + return tx, clientAccountID, muxedAccountErr + } + } + tx.Network = network // verify transaction source diff --git a/xdr/Stellar-transaction.x b/xdr/Stellar-transaction.x index 2c518b0035..a396042e69 100644 --- a/xdr/Stellar-transaction.x +++ b/xdr/Stellar-transaction.x @@ -7,6 +7,17 @@ namespace stellar { +// Source or destination of a payment operation +union MuxedAccount switch (CryptoKeyType type) { + case KEY_TYPE_ED25519: + uint256 ed25519; + case KEY_TYPE_MUXED_ED25519: + struct { + uint64 id; + uint256 ed25519; + } med25519; +}; + struct DecoratedSignature { SignatureHint hint; // last 4 bytes of the public key, used as a hint @@ -55,9 +66,9 @@ struct CreateAccountOp */ struct PaymentOp { - AccountID destination; // recipient of the payment - Asset asset; // what they end up with - int64 amount; // amount they end up with + MuxedAccount destination; // recipient of the payment + Asset asset; // what they end up with + int64 amount; // amount they end up with }; /* PathPaymentStrictReceive @@ -78,9 +89,9 @@ struct PathPaymentStrictReceiveOp // send (excluding fees). // The operation will fail if can't be met - AccountID destination; // recipient of the payment - Asset destAsset; // what they end up with - int64 destAmount; // amount they end up with + MuxedAccount destination; // recipient of the payment + Asset destAsset; // what they end up with + int64 destAmount; // amount they end up with Asset path<5>; // additional hops it must go through to get there }; @@ -101,11 +112,11 @@ struct PathPaymentStrictSendOp Asset sendAsset; // asset we pay with int64 sendAmount; // amount of sendAsset to send (excluding fees) - AccountID destination; // recipient of the payment - Asset destAsset; // what they end up with - int64 destMin; // the minimum amount of dest asset to - // be received - // The operation will fail if it can't be met + MuxedAccount destination; // recipient of the payment + Asset destAsset; // what they end up with + int64 destMin; // the minimum amount of dest asset to + // be received + // The operation will fail if it can't be met Asset path<5>; // additional hops it must go through to get there }; @@ -286,7 +297,7 @@ struct Operation // sourceAccount is the account used to run the operation // if not set, the runtime defaults to "sourceAccount" specified at // the transaction level - AccountID* sourceAccount; + MuxedAccount* sourceAccount; union switch (OperationType type) { @@ -307,7 +318,7 @@ struct Operation case ALLOW_TRUST: AllowTrustOp allowTrustOp; case ACCOUNT_MERGE: - AccountID destination; + MuxedAccount destination; case INFLATION: void; case MANAGE_DATA: @@ -392,7 +403,7 @@ struct TransactionV0Envelope struct Transaction { // account used to run the transaction - AccountID sourceAccount; + MuxedAccount sourceAccount; // the fee the sourceAccount will pay uint32 fee; @@ -563,7 +574,7 @@ enum PathPaymentStrictReceiveResultCode struct SimplePaymentResult { - AccountID destination; + MuxedAccount destination; Asset asset; int64 amount; }; @@ -1012,4 +1023,4 @@ struct TransactionResult } ext; }; -} \ No newline at end of file +} diff --git a/xdr/Stellar-types.x b/xdr/Stellar-types.x index b7a616aef6..f4b6810843 100644 --- a/xdr/Stellar-types.x +++ b/xdr/Stellar-types.x @@ -17,6 +17,7 @@ typedef hyper int64; enum CryptoKeyType { KEY_TYPE_ED25519 = 0, + KEY_TYPE_MUXED_ED25519 = 256, KEY_TYPE_PRE_AUTH_TX = 1, KEY_TYPE_HASH_X = 2 }; diff --git a/xdr/account_id_test.go b/xdr/account_id_test.go index ad3a7a011b..7d089a67e3 100644 --- a/xdr/account_id_test.go +++ b/xdr/account_id_test.go @@ -15,7 +15,8 @@ var _ = Describe("xdr.AccountId#Address()", func() { It("returns a strkey string when account id is valid", func() { var aid AccountId - aid.SetAddress("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH") + err := aid.SetAddress("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH") + Expect(err).ShouldNot(HaveOccurred()) addy := aid.Address() Expect(addy).To(Equal("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH")) }) @@ -24,15 +25,19 @@ var _ = Describe("xdr.AccountId#Address()", func() { var _ = Describe("xdr.AccountId#Equals()", func() { It("returns true when the account ids have equivalent values", func() { var l, r AccountId - l.SetAddress("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH") - r.SetAddress("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH") + err := l.SetAddress("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH") + Expect(err).ShouldNot(HaveOccurred()) + err = r.SetAddress("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH") + Expect(err).ShouldNot(HaveOccurred()) Expect(l.Equals(r)).To(BeTrue()) }) It("returns false when the account ids have different values", func() { var l, r AccountId - l.SetAddress("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH") - r.SetAddress("GBTBXQEVDNVUEESCTPUT3CHJDVNG44EMPMBELH5F7H3YPHXPZXOTEWB4") + err := l.SetAddress("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH") + Expect(err).ShouldNot(HaveOccurred()) + err = r.SetAddress("GBTBXQEVDNVUEESCTPUT3CHJDVNG44EMPMBELH5F7H3YPHXPZXOTEWB4") + Expect(err).ShouldNot(HaveOccurred()) Expect(l.Equals(r)).To(BeFalse()) }) }) @@ -40,7 +45,8 @@ var _ = Describe("xdr.AccountId#Equals()", func() { var _ = Describe("xdr.AccountId#LedgerKey()", func() { It("works", func() { var aid AccountId - aid.SetAddress("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH") + err := aid.SetAddress("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH") + Expect(err).ShouldNot(HaveOccurred()) key := aid.LedgerKey() packed := key.MustAccount().AccountId diff --git a/xdr/asset.go b/xdr/asset.go index a8d1e5b952..ffd7e5d334 100644 --- a/xdr/asset.go +++ b/xdr/asset.go @@ -39,8 +39,11 @@ func MustNewNativeAsset() Asset { func MustNewCreditAsset(code string, issuer string) Asset { a := Asset{} accountID := AccountId{} - accountID.SetAddress(issuer) - err := a.SetCredit(code, accountID) + err := accountID.SetAddress(issuer) + if err != nil { + panic(err) + } + err = a.SetCredit(code, accountID) if err != nil { panic(err) } diff --git a/xdr/muxed_account.go b/xdr/muxed_account.go new file mode 100644 index 0000000000..e8df81d889 --- /dev/null +++ b/xdr/muxed_account.go @@ -0,0 +1,148 @@ +package xdr + +import ( + "errors" + "fmt" + + "github.com/stellar/go/strkey" +) + +// Address returns the strkey encoded form of this MuxedAccount. This method will +// panic if the MuxedAccount is backed by a public key of an unknown type. +func (m *MuxedAccount) Address() string { + address, err := m.GetAddress() + if err != nil { + panic(err) + } + return address +} + +// GetAddress returns the strkey encoded form of this MuxedAccount, and an error +// if the MuxedAccount is backed by a public key of an unknown type. +func (m *MuxedAccount) GetAddress() (string, error) { + if m == nil { + return "", nil + } + + raw := make([]byte, 0, 40) + switch m.Type { + case CryptoKeyTypeKeyTypeEd25519: + ed, ok := m.GetEd25519() + if !ok { + return "", fmt.Errorf("Could not get Ed25519") + } + raw = append(raw, ed[:]...) + return strkey.Encode(strkey.VersionByteAccountID, raw) + case CryptoKeyTypeKeyTypeMuxedEd25519: + ed, ok := m.GetMed25519() + if !ok { + return "", fmt.Errorf("Could not get Med25519") + } + idBytes, err := ed.Id.MarshalBinary() + if err != nil { + return "", fmt.Errorf("Could not marshal ID") + } + raw = append(raw, idBytes...) + raw = append(raw, ed.Ed25519[:]...) + return strkey.Encode(strkey.VersionByteMuxedAccount, raw) + default: + return "", fmt.Errorf("Unknown muxed account type: %v", m.Type) + } + +} + +// Equals returns true if `other` is equivalent to `m` +func (m *MuxedAccount) Equals(other MuxedAccount) bool { + if m.Type != other.Type { + return false + } + + switch m.Type { + case CryptoKeyTypeKeyTypeEd25519: + l := m.MustEd25519() + r := other.MustEd25519() + return l == r + case CryptoKeyTypeKeyTypeMuxedEd25519: + l := m.MustMed25519() + r := other.MustMed25519() + return l == r + default: + panic(fmt.Errorf("Unknown muxed account type: %v", m.Type)) + } +} + +func MustMuxedAccountAddress(address string) MuxedAccount { + m := MuxedAccount{} + err := m.SetAddress(address) + if err != nil { + panic(err) + } + return m +} + +// AddressToMuxedAccount returns an MuxedAccount for a given address string. +// If the address is not valid the error returned will not be nil +func AddressToMuxedAccount(address string) (MuxedAccount, error) { + result := MuxedAccount{} + err := result.SetAddress(address) + + return result, err +} + +// SetAddress modifies the receiver, setting it's value to the MuxedAccount form +// of the provided address. +func (m *MuxedAccount) SetAddress(address string) error { + if m == nil { + return nil + } + + switch len(address) { + case 56: + raw, err := strkey.Decode(strkey.VersionByteAccountID, address) + if err != nil { + return err + } + if len(raw) != 32 { + return errors.New("invalid address") + } + var ui Uint256 + copy(ui[:], raw) + *m, err = NewMuxedAccount(CryptoKeyTypeKeyTypeEd25519, ui) + return err + case 69: + raw, err := strkey.Decode(strkey.VersionByteMuxedAccount, address) + if err != nil { + return err + } + if len(raw) != 40 { + return errors.New("invalid muxed address") + } + var muxed MuxedAccountMed25519 + if err = muxed.Id.UnmarshalBinary(raw[:8]); err != nil { + return err + } + copy(muxed.Ed25519[:], raw[8:]) + *m, err = NewMuxedAccount(CryptoKeyTypeKeyTypeMuxedEd25519, muxed) + return err + default: + return errors.New("invalid address") + } + +} + +// ToAccountId transforms a MuxedAccount to an AccountId, dropping the +// memo Id if necessary +func (m MuxedAccount) ToAccountId() AccountId { + result := AccountId{Type: PublicKeyTypePublicKeyTypeEd25519} + switch m.Type { + case CryptoKeyTypeKeyTypeEd25519: + ed := m.MustEd25519() + result.Ed25519 = &ed + case CryptoKeyTypeKeyTypeMuxedEd25519: + ed := m.MustMed25519().Ed25519 + result.Ed25519 = &ed + default: + panic(fmt.Errorf("Unknown muxed account type: %v", m.Type)) + } + return result +} diff --git a/xdr/muxed_account_test.go b/xdr/muxed_account_test.go new file mode 100644 index 0000000000..22257e3006 --- /dev/null +++ b/xdr/muxed_account_test.go @@ -0,0 +1,154 @@ +package xdr_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + . "github.com/stellar/go/xdr" +) + +var _ = Describe("xdr.MuxedAccount#Get/SetAddress()", func() { + It("returns an empty string when muxed account is nil", func() { + addy := (*MuxedAccount)(nil).Address() + Expect(addy).To(Equal("")) + }) + + It("returns a strkey string when muxed account is valid", func() { + var unmuxed MuxedAccount + err := unmuxed.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ") + Expect(err).ShouldNot(HaveOccurred()) + Expect(unmuxed.Type).To(Equal(CryptoKeyTypeKeyTypeEd25519)) + Expect(*unmuxed.Ed25519).To(Equal(Uint256{63, 12, 52, 191, 147, 173, 13, 153, 113, 208, 76, 204, 144, 247, 5, 81, 28, 131, 138, 173, 151, 52, 164, 162, 251, 13, 122, 3, 252, 127, 232, 154})) + muxedy := unmuxed.Address() + Expect(muxedy).To(Equal("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ")) + + var muxed MuxedAccount + err = muxed.SetAddress("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG") + Expect(err).ShouldNot(HaveOccurred()) + Expect(muxed.Type).To(Equal(CryptoKeyTypeKeyTypeMuxedEd25519)) + Expect(muxed.Med25519.Id).To(Equal(Uint64(9223372036854775808))) + Expect(muxed.Med25519.Ed25519).To(Equal(*unmuxed.Ed25519)) + muxedy = muxed.Address() + Expect(muxedy).To(Equal("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG")) + + err = muxed.SetAddress("MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6") + Expect(err).ShouldNot(HaveOccurred()) + Expect(muxed.Type).To(Equal(CryptoKeyTypeKeyTypeMuxedEd25519)) + Expect(muxed.Med25519.Id).To(Equal(Uint64(0))) + Expect(muxed.Med25519.Ed25519).To(Equal(*unmuxed.Ed25519)) + muxedy = muxed.Address() + Expect(muxedy).To(Equal("MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6")) + }) + + It("returns an error when the strkey is invalid", func() { + var muxed MuxedAccount + + // Test cases from SEP23 + + err := muxed.SetAddress("GAAAAAAAACGC6") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddress("MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL7") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZA") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUACUSI") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddress("G47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVP2I") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddress("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOGA") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddress("MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITIADJPA") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddress("M4AAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITIU2K") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddress("MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6===") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddress("MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL4") + Expect(err).Should(HaveOccurred()) + }) +}) + +var _ = Describe("xdr.MuxedAccountd#Equals()", func() { + It("returns true when the account ids have equivalent values", func() { + var l, r MuxedAccount + err := l.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ") + Expect(err).ShouldNot(HaveOccurred()) + err = r.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ") + Expect(err).ShouldNot(HaveOccurred()) + + Expect(l.Equals(r)).To(BeTrue()) + err = l.SetAddress("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG") + Expect(err).ShouldNot(HaveOccurred()) + err = r.SetAddress("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG") + Expect(err).ShouldNot(HaveOccurred()) + Expect(l.Equals(r)).To(BeTrue()) + + err = l.SetAddress("MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6") + Expect(err).ShouldNot(HaveOccurred()) + err = r.SetAddress("MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6") + Expect(err).ShouldNot(HaveOccurred()) + Expect(l.Equals(r)).To(BeTrue()) + + }) + + It("returns false when the account ids have different values", func() { + var l, r MuxedAccount + err := l.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ") + Expect(err).ShouldNot(HaveOccurred()) + err = r.SetAddress("GCR22L3WS7TP72S4Z27YTO6JIQYDJK2KLS2TQNHK6Y7XYPA3AGT3X4FH") + Expect(err).ShouldNot(HaveOccurred()) + Expect(l.Equals(r)).To(BeFalse()) + + // Same key but different memo ids + err = l.SetAddress("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG") + Expect(err).ShouldNot(HaveOccurred()) + err = r.SetAddress("MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6") + Expect(err).ShouldNot(HaveOccurred()) + Expect(l.Equals(r)).To(BeFalse()) + + err = l.SetAddress("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG") + Expect(err).ShouldNot(HaveOccurred()) + err = r.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ") + Expect(err).ShouldNot(HaveOccurred()) + Expect(l.Equals(r)).To(BeFalse()) + }) +}) + +var _ = Describe("xdr.AddressToMuxedAccount()", func() { + It("works", func() { + address := "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ" + muxedAccount, err := AddressToMuxedAccount(address) + + Expect(muxedAccount.Address()).To(Equal("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ")) + Expect(err).ShouldNot(HaveOccurred()) + + _, err = AddressToAccountId("GCR22L3") + + Expect(err).Should(HaveOccurred()) + }) +}) + +var _ = Describe("xdr.MuxedAccount.ToAccountId()", func() { + It("works", func() { + var muxed MuxedAccount + + err := muxed.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ") + Expect(err).ShouldNot(HaveOccurred()) + aid := muxed.ToAccountId() + Expect(aid.Address()).To(Equal("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ")) + + err = muxed.SetAddress("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG") + Expect(err).ShouldNot(HaveOccurred()) + aid = muxed.ToAccountId() + Expect(aid.Address()).To(Equal("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ")) + }) +}) diff --git a/xdr/transaction_envelope.go b/xdr/transaction_envelope.go index 0122c2e600..6341f0643f 100644 --- a/xdr/transaction_envelope.go +++ b/xdr/transaction_envelope.go @@ -23,15 +23,15 @@ func (e TransactionEnvelope) FeeBumpSignatures() []DecoratedSignature { // SourceAccount returns the source account for the transaction // If the transaction envelope is for a fee bump transaction, SourceAccount() // returns the source account of the inner transaction -func (e TransactionEnvelope) SourceAccount() AccountId { +func (e TransactionEnvelope) SourceAccount() MuxedAccount { switch e.Type { case EnvelopeTypeEnvelopeTypeTxFeeBump: return e.FeeBump.Tx.InnerTx.V1.Tx.SourceAccount case EnvelopeTypeEnvelopeTypeTx: return e.V1.Tx.SourceAccount case EnvelopeTypeEnvelopeTypeTxV0: - return AccountId{ - Type: PublicKeyTypePublicKeyTypeEd25519, + return MuxedAccount{ + Type: CryptoKeyTypeKeyTypeEd25519, Ed25519: &e.V0.Tx.SourceAccountEd25519, } default: diff --git a/xdr/transaction_envelope_test.go b/xdr/transaction_envelope_test.go index 91abbd2356..26cda744b8 100644 --- a/xdr/transaction_envelope_test.go +++ b/xdr/transaction_envelope_test.go @@ -22,7 +22,7 @@ func createLegacyTx() TransactionEnvelope { MaxTime: 2, }, Operations: []Operation{ - Operation{ + { Body: OperationBody{ BumpSequenceOp: &BumpSequenceOp{ BumpTo: 34, @@ -32,7 +32,7 @@ func createLegacyTx() TransactionEnvelope { }, }, Signatures: []DecoratedSignature{ - DecoratedSignature{ + { Hint: SignatureHint{1, 1, 1, 1}, Signature: Signature{10, 10, 10}, }, @@ -46,8 +46,8 @@ func createTx() TransactionEnvelope { Type: EnvelopeTypeEnvelopeTypeTx, V1: &TransactionV1Envelope{ Tx: Transaction{ - SourceAccount: AccountId{ - Type: PublicKeyTypePublicKeyTypeEd25519, + SourceAccount: MuxedAccount{ + Type: CryptoKeyTypeKeyTypeEd25519, Ed25519: &Uint256{ 3, 3, 3, }, @@ -63,7 +63,7 @@ func createTx() TransactionEnvelope { MaxTime: 4, }, Operations: []Operation{ - Operation{ + { Body: OperationBody{ BumpSequenceOp: &BumpSequenceOp{ BumpTo: 98, @@ -73,7 +73,7 @@ func createTx() TransactionEnvelope { }, }, Signatures: []DecoratedSignature{ - DecoratedSignature{ + { Hint: SignatureHint{2, 2, 2, 2}, Signature: Signature{20, 20, 20}, }, @@ -98,7 +98,7 @@ func createFeeBumpTx() TransactionEnvelope { }, }, Signatures: []DecoratedSignature{ - DecoratedSignature{ + { Hint: SignatureHint{3, 3, 3, 3}, Signature: Signature{30, 30, 30}, }, @@ -173,7 +173,7 @@ func TestSourceAccount(t *testing.T) { assert.Equal( t, - PublicKeyTypePublicKeyTypeEd25519, + CryptoKeyTypeKeyTypeEd25519, legacyTx.SourceAccount().Type, ) assert.Equal( diff --git a/xdr/xdr_generated.go b/xdr/xdr_generated.go index cd9eeb6823..747f463ab3 100644 --- a/xdr/xdr_generated.go +++ b/xdr/xdr_generated.go @@ -6905,6 +6905,162 @@ var ( _ encoding.BinaryUnmarshaler = (*AuthenticatedMessage)(nil) ) +// MuxedAccountMed25519 is an XDR NestedStruct defines as: +// +// struct { +// uint64 id; +// uint256 ed25519; +// } +// +type MuxedAccountMed25519 struct { + Id Uint64 + Ed25519 Uint256 +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s MuxedAccountMed25519) MarshalBinary() ([]byte, error) { + b := new(bytes.Buffer) + _, err := Marshal(b, s) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *MuxedAccountMed25519) UnmarshalBinary(inp []byte) error { + _, err := Unmarshal(bytes.NewReader(inp), s) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*MuxedAccountMed25519)(nil) + _ encoding.BinaryUnmarshaler = (*MuxedAccountMed25519)(nil) +) + +// MuxedAccount is an XDR Union defines as: +// +// union MuxedAccount switch (CryptoKeyType type) { +// case KEY_TYPE_ED25519: +// uint256 ed25519; +// case KEY_TYPE_MUXED_ED25519: +// struct { +// uint64 id; +// uint256 ed25519; +// } med25519; +// }; +// +type MuxedAccount struct { + Type CryptoKeyType + Ed25519 *Uint256 + Med25519 *MuxedAccountMed25519 +} + +// SwitchFieldName returns the field name in which this union's +// discriminant is stored +func (u MuxedAccount) SwitchFieldName() string { + return "Type" +} + +// ArmForSwitch returns which field name should be used for storing +// the value for an instance of MuxedAccount +func (u MuxedAccount) ArmForSwitch(sw int32) (string, bool) { + switch CryptoKeyType(sw) { + case CryptoKeyTypeKeyTypeEd25519: + return "Ed25519", true + case CryptoKeyTypeKeyTypeMuxedEd25519: + return "Med25519", true + } + return "-", false +} + +// NewMuxedAccount creates a new MuxedAccount. +func NewMuxedAccount(aType CryptoKeyType, value interface{}) (result MuxedAccount, err error) { + result.Type = aType + switch CryptoKeyType(aType) { + case CryptoKeyTypeKeyTypeEd25519: + tv, ok := value.(Uint256) + if !ok { + err = fmt.Errorf("invalid value, must be Uint256") + return + } + result.Ed25519 = &tv + case CryptoKeyTypeKeyTypeMuxedEd25519: + tv, ok := value.(MuxedAccountMed25519) + if !ok { + err = fmt.Errorf("invalid value, must be MuxedAccountMed25519") + return + } + result.Med25519 = &tv + } + return +} + +// MustEd25519 retrieves the Ed25519 value from the union, +// panicing if the value is not set. +func (u MuxedAccount) MustEd25519() Uint256 { + val, ok := u.GetEd25519() + + if !ok { + panic("arm Ed25519 is not set") + } + + return val +} + +// GetEd25519 retrieves the Ed25519 value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u MuxedAccount) GetEd25519() (result Uint256, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "Ed25519" { + result = *u.Ed25519 + ok = true + } + + return +} + +// MustMed25519 retrieves the Med25519 value from the union, +// panicing if the value is not set. +func (u MuxedAccount) MustMed25519() MuxedAccountMed25519 { + val, ok := u.GetMed25519() + + if !ok { + panic("arm Med25519 is not set") + } + + return val +} + +// GetMed25519 retrieves the Med25519 value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u MuxedAccount) GetMed25519() (result MuxedAccountMed25519, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "Med25519" { + result = *u.Med25519 + ok = true + } + + return +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s MuxedAccount) MarshalBinary() ([]byte, error) { + b := new(bytes.Buffer) + _, err := Marshal(b, s) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *MuxedAccount) UnmarshalBinary(inp []byte) error { + _, err := Unmarshal(bytes.NewReader(inp), s) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*MuxedAccount)(nil) + _ encoding.BinaryUnmarshaler = (*MuxedAccount)(nil) +) + // DecoratedSignature is an XDR Struct defines as: // // struct DecoratedSignature @@ -7058,13 +7214,13 @@ var ( // // struct PaymentOp // { -// AccountID destination; // recipient of the payment -// Asset asset; // what they end up with -// int64 amount; // amount they end up with +// MuxedAccount destination; // recipient of the payment +// Asset asset; // what they end up with +// int64 amount; // amount they end up with // }; // type PaymentOp struct { - Destination AccountId + Destination MuxedAccount Asset Asset Amount Int64 } @@ -7096,9 +7252,9 @@ var ( // // send (excluding fees). // // The operation will fail if can't be met // -// AccountID destination; // recipient of the payment -// Asset destAsset; // what they end up with -// int64 destAmount; // amount they end up with +// MuxedAccount destination; // recipient of the payment +// Asset destAsset; // what they end up with +// int64 destAmount; // amount they end up with // // Asset path<5>; // additional hops it must go through to get there // }; @@ -7106,7 +7262,7 @@ var ( type PathPaymentStrictReceiveOp struct { SendAsset Asset SendMax Int64 - Destination AccountId + Destination MuxedAccount DestAsset Asset DestAmount Int64 Path []Asset `xdrmaxsize:"5"` @@ -7137,11 +7293,11 @@ var ( // Asset sendAsset; // asset we pay with // int64 sendAmount; // amount of sendAsset to send (excluding fees) // -// AccountID destination; // recipient of the payment -// Asset destAsset; // what they end up with -// int64 destMin; // the minimum amount of dest asset to -// // be received -// // The operation will fail if it can't be met +// MuxedAccount destination; // recipient of the payment +// Asset destAsset; // what they end up with +// int64 destMin; // the minimum amount of dest asset to +// // be received +// // The operation will fail if it can't be met // // Asset path<5>; // additional hops it must go through to get there // }; @@ -7149,7 +7305,7 @@ var ( type PathPaymentStrictSendOp struct { SendAsset Asset SendAmount Int64 - Destination AccountId + Destination MuxedAccount DestAsset Asset DestMin Int64 Path []Asset `xdrmaxsize:"5"` @@ -7627,7 +7783,7 @@ var ( // case ALLOW_TRUST: // AllowTrustOp allowTrustOp; // case ACCOUNT_MERGE: -// AccountID destination; +// MuxedAccount destination; // case INFLATION: // void; // case MANAGE_DATA: @@ -7650,7 +7806,7 @@ type OperationBody struct { SetOptionsOp *SetOptionsOp ChangeTrustOp *ChangeTrustOp AllowTrustOp *AllowTrustOp - Destination *AccountId + Destination *MuxedAccount ManageDataOp *ManageDataOp BumpSequenceOp *BumpSequenceOp ManageBuyOfferOp *ManageBuyOfferOp @@ -7760,9 +7916,9 @@ func NewOperationBody(aType OperationType, value interface{}) (result OperationB } result.AllowTrustOp = &tv case OperationTypeAccountMerge: - tv, ok := value.(AccountId) + tv, ok := value.(MuxedAccount) if !ok { - err = fmt.Errorf("invalid value, must be AccountId") + err = fmt.Errorf("invalid value, must be MuxedAccount") return } result.Destination = &tv @@ -8002,7 +8158,7 @@ func (u OperationBody) GetAllowTrustOp() (result AllowTrustOp, ok bool) { // MustDestination retrieves the Destination value from the union, // panicing if the value is not set. -func (u OperationBody) MustDestination() AccountId { +func (u OperationBody) MustDestination() MuxedAccount { val, ok := u.GetDestination() if !ok { @@ -8014,7 +8170,7 @@ func (u OperationBody) MustDestination() AccountId { // GetDestination retrieves the Destination value from the union, // returning ok if the union's switch indicated the value is valid. -func (u OperationBody) GetDestination() (result AccountId, ok bool) { +func (u OperationBody) GetDestination() (result MuxedAccount, ok bool) { armName, _ := u.ArmForSwitch(int32(u.Type)) if armName == "Destination" { @@ -8150,7 +8306,7 @@ var ( // // sourceAccount is the account used to run the operation // // if not set, the runtime defaults to "sourceAccount" specified at // // the transaction level -// AccountID* sourceAccount; +// MuxedAccount* sourceAccount; // // union switch (OperationType type) // { @@ -8171,7 +8327,7 @@ var ( // case ALLOW_TRUST: // AllowTrustOp allowTrustOp; // case ACCOUNT_MERGE: -// AccountID destination; +// MuxedAccount destination; // case INFLATION: // void; // case MANAGE_DATA: @@ -8187,7 +8343,7 @@ var ( // }; // type Operation struct { - SourceAccount *AccountId + SourceAccount *MuxedAccount Body OperationBody } @@ -8703,7 +8859,7 @@ var ( // struct Transaction // { // // account used to run the transaction -// AccountID sourceAccount; +// MuxedAccount sourceAccount; // // // the fee the sourceAccount will pay // uint32 fee; @@ -8728,7 +8884,7 @@ var ( // }; // type Transaction struct { - SourceAccount AccountId + SourceAccount MuxedAccount Fee Uint32 SeqNum SequenceNumber TimeBounds *TimeBounds @@ -9728,13 +9884,13 @@ var ( // // struct SimplePaymentResult // { -// AccountID destination; +// MuxedAccount destination; // Asset asset; // int64 amount; // }; // type SimplePaymentResult struct { - Destination AccountId + Destination MuxedAccount Asset Asset Amount Int64 } @@ -12766,6 +12922,7 @@ var ( // // struct InnerTransactionResult // { +// // Always 0. Here for binary compatibility. // int64 feeCharged; // // union switch (TransactionResultCode code) @@ -13270,6 +13427,7 @@ var ( // enum CryptoKeyType // { // KEY_TYPE_ED25519 = 0, +// KEY_TYPE_MUXED_ED25519 = 256, // KEY_TYPE_PRE_AUTH_TX = 1, // KEY_TYPE_HASH_X = 2 // }; @@ -13277,15 +13435,17 @@ var ( type CryptoKeyType int32 const ( - CryptoKeyTypeKeyTypeEd25519 CryptoKeyType = 0 - CryptoKeyTypeKeyTypePreAuthTx CryptoKeyType = 1 - CryptoKeyTypeKeyTypeHashX CryptoKeyType = 2 + CryptoKeyTypeKeyTypeEd25519 CryptoKeyType = 0 + CryptoKeyTypeKeyTypeMuxedEd25519 CryptoKeyType = 256 + CryptoKeyTypeKeyTypePreAuthTx CryptoKeyType = 1 + CryptoKeyTypeKeyTypeHashX CryptoKeyType = 2 ) var cryptoKeyTypeMap = map[int32]string{ - 0: "CryptoKeyTypeKeyTypeEd25519", - 1: "CryptoKeyTypeKeyTypePreAuthTx", - 2: "CryptoKeyTypeKeyTypeHashX", + 0: "CryptoKeyTypeKeyTypeEd25519", + 256: "CryptoKeyTypeKeyTypeMuxedEd25519", + 1: "CryptoKeyTypeKeyTypePreAuthTx", + 2: "CryptoKeyTypeKeyTypeHashX", } // ValidEnum validates a proposed value for this enum. Implements