From efb9c3bec070c2fee2dd6fc8b0633e2baef15f60 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Mon, 5 Apr 2021 17:12:09 +0200 Subject: [PATCH 01/22] Re-enable M-key parsing in strkey package --- strkey/decode_test.go | 75 +++++++++++++++++++++++++++++++++++++++++++ strkey/encode_test.go | 12 +++++++ strkey/main.go | 5 ++- 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/strkey/decode_test.go b/strkey/decode_test.go index 5b8bc7b24d..932b096388 100644 --- a/strkey/decode_test.go +++ b/strkey/decode_test.go @@ -24,6 +24,19 @@ 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,6 +98,68 @@ 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") } func TestMalformed(t *testing.T) { 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 167efaa4ee..76582e51af 100644 --- a/strkey/main.go +++ b/strkey/main.go @@ -24,6 +24,9 @@ const ( //VersionByteSeed is the version byte used for encoded stellar seed VersionByteSeed = 18 << 3 // Base32-encodes to 'S...' + //VersionByteMuxedAccounts is the version byte used for encoded stellar multiplexed addresses + VersionByteMuxedAccount = 12 << 3 // Base32-encodes to 'M...' + //VersionByteHashTx is the version byte used for encoded stellar hashTx //signer keys. VersionByteHashTx = 19 << 3 // Base32-encodes to 'T...' @@ -163,7 +166,7 @@ func checkValidVersionByte(version VersionByte) error { switch version { // intentionally disallow M-strkeys (versionByteMuxedAccount) // until SEP23 leaves the Draft status. - case VersionByteAccountID, VersionByteSeed, VersionByteHashTx, VersionByteHashX: + case VersionByteAccountID, VersionByteMuxedAccount, VersionByteSeed, VersionByteHashTx, VersionByteHashX: return nil default: return ErrInvalidVersionByte From a0bf2864d243693d79b8a7f1633e54048e8c1fda Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 6 Apr 2021 15:39:27 +0200 Subject: [PATCH 02/22] Enable SEP23 parsing for MuxedAccounts (with separated functions) --- xdr/muxed_account.go | 91 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/xdr/muxed_account.go b/xdr/muxed_account.go index 3a9e7c31fa..2b493f4599 100644 --- a/xdr/muxed_account.go +++ b/xdr/muxed_account.go @@ -22,7 +22,7 @@ func MustMuxedAddressPtr(address string) *MuxedAccount { } // SetAddress modifies the receiver, setting it's value to the MuxedAccount form -// of the provided address. +// of the provided G-address. func (m *MuxedAccount) SetAddress(address string) error { if m == nil { return nil @@ -47,6 +47,95 @@ func (m *MuxedAccount) SetAddress(address string) error { } +// SetAddressWithSEP23 modifies the receiver, setting it's value to the MuxedAccount form +// of the provided strkey G-address or M-address, as described in SEP23. +func (m *MuxedAccount) SetAddressWithSEP23(address string) error { + if m == nil { + return nil + } + + switch len(address) { + case 56: + return m.SetAddress(address) + 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") + } + +} + +// SEP23AddressToMuxedAccount returns an MuxedAccount for a given address string +// or SEP23 M-address. +// If the address is not valid the error returned will not be nil +func SEP23AddressToMuxedAccount(address string) (MuxedAccount, error) { + result := MuxedAccount{} + err := result.SetAddressWithSEP23(address) + + return result, err +} + +// Address returns the strkey-encoded form of this MuxedAccount. In particular, it will +// return an M- strkey representation for CryptoKeyTypeKeyTypeMuxedEd25519 variants of the account +// (according to SEP23). This method will panic if the MuxedAccount is backed by a public key of an +// unknown type. +func (m *MuxedAccount) SEP23Address() string { + address, err := m.GetSEP23Address() + if err != nil { + panic(err) + } + return address +} + +// GetAddress returns the strkey-encoded form of this MuxedAccount. In particular, it will +// return an M-strkey representation for CryptoKeyTypeKeyTypeMuxedEd25519 variants of the account +// (according to SEP23). In addition it will return an error if the MuxedAccount is backed by a +// public key of an unknown type. +func (m *MuxedAccount) GetSEP23Address() (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) + } + +} + // ToAccountId transforms a MuxedAccount to an AccountId, dropping the // memo Id if necessary func (m MuxedAccount) ToAccountId() AccountId { From 27a79c6bded74710d05f8870e19e44653179c5bc Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 6 Apr 2021 15:40:06 +0200 Subject: [PATCH 03/22] WIP: Add flag-protected support for SEP23 in txnbuild --- txnbuild/account_merge.go | 60 +++++++++++++++++++++--- txnbuild/operation.go | 33 ++++++++++--- txnbuild/transaction.go | 98 ++++++++++++++++++++++++++++++--------- 3 files changed, 156 insertions(+), 35 deletions(-) diff --git a/txnbuild/account_merge.go b/txnbuild/account_merge.go index 147b85216a..e259d8439a 100644 --- a/txnbuild/account_merge.go +++ b/txnbuild/account_merge.go @@ -14,9 +14,18 @@ type AccountMerge struct { // BuildXDR for AccountMerge returns a fully configured XDR Operation. func (am *AccountMerge) BuildXDR() (xdr.Operation, error) { - var xdrOp xdr.MuxedAccount + return am.buildXDR(false) +} - err := xdrOp.SetAddress(am.Destination) +// BuildXDR for AccountMerge returns a fully configured XDR Operation. +func (am *AccountMerge) buildXDR(withSEP23 bool) (xdr.Operation, error) { + var xdrOp xdr.MuxedAccount + var err error + if withSEP23 { + err = xdrOp.SetAddressWithSEP23(am.Destination) + } else { + err = xdrOp.SetAddress(am.Destination) + } if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") } @@ -27,7 +36,11 @@ func (am *AccountMerge) BuildXDR() (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, am.SourceAccount) + if withSEP23 { + SetOpSourceAccountWithSEP23(&op, am.SourceAccount) + } else { + SetOpSourceAccount(&op, am.SourceAccount) + } return op, nil } @@ -37,10 +50,22 @@ func (am *AccountMerge) FromXDR(xdrOp xdr.Operation) error { return errors.New("error parsing account_merge operation from xdr") } - am.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + return am.fromXDR(xdrOp, false) +} + +func (am *AccountMerge) fromXDR(xdrOp xdr.Operation, withSEP23 bool) error { + if xdrOp.Body.Type != xdr.OperationTypeAccountMerge { + return errors.New("error parsing account_merge operation from xdr") + } + + am.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withSEP23) if xdrOp.Body.Destination != nil { - aid := xdrOp.Body.Destination.ToAccountId() - am.Destination = aid.Address() + if withSEP23 { + am.Destination = xdrOp.Body.Destination.SEP23Address() + } else { + aid := xdrOp.Body.Destination.ToAccountId() + am.Destination = aid.Address() + } } return nil @@ -49,7 +74,16 @@ func (am *AccountMerge) FromXDR(xdrOp xdr.Operation) error { // Validate for AccountMerge validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. func (am *AccountMerge) Validate() error { - _, err := xdr.AddressToAccountId(am.Destination) + return am.validate(false) +} + +func (am *AccountMerge) validate(withSEP23 bool) error { + var err error + if withSEP23 { + _, err = xdr.AddressToAccountId(am.Destination) + } else { + _, err = xdr.SEP23AddressToMuxedAccount(am.Destination) + } if err != nil { return NewValidationError("Destination", err.Error()) } @@ -61,3 +95,15 @@ func (am *AccountMerge) Validate() error { func (am *AccountMerge) GetSourceAccount() string { return am.SourceAccount } + +func (am *AccountMerge) BuildXDRWithSEP23() (xdr.Operation, error) { + return am.buildXDR(true) +} + +func (am *AccountMerge) FromXDRWithSEP23(xdrOp xdr.Operation) error { + return am.fromXDR(xdrOp, true) +} + +func (am *AccountMerge) ValidateWithSEP23() error { + return am.validate(true) +} diff --git a/txnbuild/operation.go b/txnbuild/operation.go index 46a2f92adb..58c10ae676 100644 --- a/txnbuild/operation.go +++ b/txnbuild/operation.go @@ -12,6 +12,9 @@ type Operation interface { FromXDR(xdrOp xdr.Operation) error Validate() error GetSourceAccount() string + BuildXDRWithSEP23() (xdr.Operation, error) + FromXDRWithSEP23(xdrOp xdr.Operation) error + ValidateWithSEP23() error } // SetOpSourceAccount sets the source account ID on an Operation. @@ -24,8 +27,18 @@ func SetOpSourceAccount(op *xdr.Operation, sourceAccount string) { op.SourceAccount = &opSourceAccountID } +// SetOpSourceAccount sets the source account ID on an Operation, allowing M-strkeys (as defined in SEP23). +func SetOpSourceAccountWithSEP23(op *xdr.Operation, sourceAccount string) { + if sourceAccount == "" { + return + } + var opSourceAccountID xdr.MuxedAccount + opSourceAccountID.SetAddressWithSEP23(sourceAccount) + op.SourceAccount = &opSourceAccountID +} + // operationFromXDR returns a txnbuild Operation from its corresponding XDR operation -func operationFromXDR(xdrOp xdr.Operation) (Operation, error) { +func operationFromXDR(xdrOp xdr.Operation, withSEP23 bool) (Operation, error) { var newOp Operation switch xdrOp.Body.Type { case xdr.OperationTypeCreateAccount: @@ -75,15 +88,23 @@ func operationFromXDR(xdrOp xdr.Operation) (Operation, error) { default: return nil, fmt.Errorf("unknown operation type: %d", xdrOp.Body.Type) } - - err := newOp.FromXDR(xdrOp) + var err error + if withSEP23 { + err = newOp.FromXDRWithSEP23(xdrOp) + } else { + err = newOp.FromXDR(xdrOp) + } return newOp, err } -func accountFromXDR(account *xdr.MuxedAccount) string { +func accountFromXDR(account *xdr.MuxedAccount, withSEP23 bool) string { if account != nil { - aid := account.ToAccountId() - return aid.Address() + if withSEP23 { + return account.SEP23Address() + } else { + aid := account.ToAccountId() + return aid.Address() + } } return "" } diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index 813d25dd46..aa9f5b4b5f 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -514,18 +514,33 @@ func (t GenericTransaction) FeeBump() (*FeeBumpTransaction, bool) { return t.feeBump, t.feeBump != nil } +type TransactionOption int + +const ( + TransactionEnableSEP23 = iota +) + +func isSEP23Enabled(options []TransactionOption) bool { + for _, opt := range options { + if opt == TransactionEnableSEP23 { + return true + } + } + return false +} + // TransactionFromXDR parses the supplied transaction envelope in base64 XDR // and returns a GenericTransaction instance. -func TransactionFromXDR(txeB64 string) (*GenericTransaction, error) { +func TransactionFromXDR(txeB64 string, options ...TransactionOption) (*GenericTransaction, error) { var xdrEnv xdr.TransactionEnvelope err := xdr.SafeUnmarshalBase64(txeB64, &xdrEnv) if err != nil { return nil, errors.Wrap(err, "unable to unmarshal transaction envelope") } - return transactionFromParsedXDR(xdrEnv) + return transactionFromParsedXDR(xdrEnv, isSEP23Enabled(options)) } -func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope) (*GenericTransaction, error) { +func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope, withSEP23 bool) (*GenericTransaction, error) { var err error newTx := &GenericTransaction{} @@ -534,11 +549,19 @@ func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope) (*GenericTransacti innerTx, err = transactionFromParsedXDR(xdr.TransactionEnvelope{ Type: xdr.EnvelopeTypeEnvelopeTypeTx, V1: xdrEnv.FeeBump.Tx.InnerTx.V1, - }) + }, withSEP23) if err != nil { return newTx, errors.New("could not parse inner transaction") } - feeBumpAccount := xdrEnv.FeeBumpAccount().ToAccountId() + var feeAccount string + if withSEP23 { + feeBumpAccount := xdrEnv.FeeBumpAccount() + feeAccount = feeBumpAccount.SEP23Address() + } else { + feeBumpAccount := xdrEnv.FeeBumpAccount().ToAccountId() + feeAccount = feeBumpAccount.Address() + } + newTx.feeBump = &FeeBumpTransaction{ envelope: xdrEnv, // A fee-bump transaction has an effective number of operations equal to one plus the @@ -548,12 +571,19 @@ func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope) (*GenericTransacti baseFee: xdrEnv.FeeBumpFee() / int64(len(innerTx.simple.operations)+1), maxFee: xdrEnv.FeeBumpFee(), inner: innerTx.simple, - feeAccount: feeBumpAccount.Address(), + feeAccount: feeAccount, } + return newTx, nil } - - sourceAccount := xdrEnv.SourceAccount().ToAccountId() + var accountID string + if withSEP23 { + sourceAccount := xdrEnv.SourceAccount() + accountID = sourceAccount.SEP23Address() + } else { + sourceAccount := xdrEnv.SourceAccount().ToAccountId() + accountID = sourceAccount.Address() + } totalFee := int64(xdrEnv.Fee()) baseFee := totalFee @@ -566,7 +596,7 @@ func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope) (*GenericTransacti baseFee: baseFee, maxFee: totalFee, sourceAccount: SimpleAccount{ - AccountID: sourceAccount.Address(), + AccountID: accountID, Sequence: xdrEnv.SeqNum(), }, operations: nil, @@ -585,7 +615,7 @@ func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope) (*GenericTransacti operations := xdrEnv.Operations() for _, op := range operations { - newOp, err := operationFromXDR(op) + newOp, err := operationFromXDR(op, withSEP23) if err != nil { return nil, err } @@ -604,6 +634,7 @@ type TransactionParams struct { BaseFee int64 Memo Memo Timebounds Timebounds + Options []TransactionOption } // NewTransaction returns a new Transaction instance @@ -634,10 +665,18 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { memo: params.Memo, timebounds: params.Timebounds, } - - accountID, err := xdr.AddressToAccountId(tx.sourceAccount.AccountID) - if err != nil { - return nil, errors.Wrap(err, "account id is not valid") + withSEP23 := isSEP23Enabled(params.Options) + var sourceAccount xdr.MuxedAccount + if withSEP23 { + if err := sourceAccount.SetAddressWithSEP23(tx.sourceAccount.AccountID); err != nil { + return nil, errors.Wrap(err, "account id is not valid") + } + } else { + accountID, err := xdr.AddressToAccountId(tx.sourceAccount.AccountID) + if err != nil { + return nil, errors.Wrap(err, "account id is not valid") + } + sourceAccount = accountID.ToMuxedAccount() } if tx.baseFee < MinBaseFee { @@ -669,7 +708,7 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { Type: xdr.EnvelopeTypeEnvelopeTypeTx, V1: &xdr.TransactionV1Envelope{ Tx: xdr.Transaction{ - SourceAccount: accountID.ToMuxedAccount(), + SourceAccount: sourceAccount, Fee: xdr.Uint32(tx.maxFee), SeqNum: xdr.SequenceNumber(sequence), TimeBounds: &xdr.TimeBounds{ @@ -694,8 +733,15 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { if verr := op.Validate(); verr != nil { return nil, errors.Wrap(verr, fmt.Sprintf("validation failed for %T operation", op)) } - - xdrOperation, err2 := op.BuildXDR() + var ( + err2 error + xdrOperation xdr.Operation + ) + if withSEP23 { + xdrOperation, err2 = op.BuildXDRWithSEP23() + } else { + xdrOperation, err2 = op.BuildXDR() + } if err2 != nil { return nil, errors.Wrap(err2, fmt.Sprintf("failed to build operation %T", op)) } @@ -712,6 +758,7 @@ type FeeBumpTransactionParams struct { Inner *Transaction FeeAccount string BaseFee int64 + Options []TransactionOption } func convertToV1(tx *Transaction) (*Transaction, error) { @@ -782,16 +829,23 @@ func NewFeeBumpTransaction(params FeeBumpTransactionParams) (*FeeBumpTransaction ) } - accountID, err := xdr.AddressToAccountId(tx.feeAccount) - if err != nil { - return tx, errors.Wrap(err, "fee account is not a valid address") + var feeSource xdr.MuxedAccount + if isSEP23Enabled(params.Options) { + if err := feeSource.SetAddressWithSEP23(tx.feeAccount); err != nil { + return tx, errors.Wrap(err, "fee account is not a valid address") + } + } else { + accountID, err := xdr.AddressToAccountId(tx.feeAccount) + if err != nil { + return tx, errors.Wrap(err, "fee account is not a valid address") + } + feeSource = accountID.ToMuxedAccount() } - tx.envelope = xdr.TransactionEnvelope{ Type: xdr.EnvelopeTypeEnvelopeTypeTxFeeBump, FeeBump: &xdr.FeeBumpTransactionEnvelope{ Tx: xdr.FeeBumpTransaction{ - FeeSource: accountID.ToMuxedAccount(), + FeeSource: feeSource, Fee: xdr.Int64(tx.maxFee), InnerTx: xdr.FeeBumpTransactionInnerTx{ Type: xdr.EnvelopeTypeEnvelopeTypeTx, From 14ec0cb168c1026ee57ef2bd0b75a5e223e05452 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 6 Apr 2021 16:26:00 +0200 Subject: [PATCH 04/22] Fix comment typos --- xdr/muxed_account.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xdr/muxed_account.go b/xdr/muxed_account.go index 2b493f4599..e1d877eace 100644 --- a/xdr/muxed_account.go +++ b/xdr/muxed_account.go @@ -88,7 +88,7 @@ func SEP23AddressToMuxedAccount(address string) (MuxedAccount, error) { return result, err } -// Address returns the strkey-encoded form of this MuxedAccount. In particular, it will +// SEP23Address returns the strkey-encoded form of this MuxedAccount. In particular, it will // return an M- strkey representation for CryptoKeyTypeKeyTypeMuxedEd25519 variants of the account // (according to SEP23). This method will panic if the MuxedAccount is backed by a public key of an // unknown type. @@ -100,7 +100,7 @@ func (m *MuxedAccount) SEP23Address() string { return address } -// GetAddress returns the strkey-encoded form of this MuxedAccount. In particular, it will +// GetSEP23Address returns the strkey-encoded form of this MuxedAccount. In particular, it will // return an M-strkey representation for CryptoKeyTypeKeyTypeMuxedEd25519 variants of the account // (according to SEP23). In addition it will return an error if the MuxedAccount is backed by a // public key of an unknown type. From 8546938ede2120109a306103162e216cfdd6774e Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Wed, 7 Apr 2021 21:49:32 +0200 Subject: [PATCH 05/22] Address review feedback --- txnbuild/account_merge.go | 43 +++++++-------------------------------- txnbuild/operation.go | 25 ++++++++--------------- txnbuild/transaction.go | 39 +++++++++++++++++------------------ 3 files changed, 35 insertions(+), 72 deletions(-) diff --git a/txnbuild/account_merge.go b/txnbuild/account_merge.go index e259d8439a..58c9a516ea 100644 --- a/txnbuild/account_merge.go +++ b/txnbuild/account_merge.go @@ -13,12 +13,7 @@ type AccountMerge struct { } // BuildXDR for AccountMerge returns a fully configured XDR Operation. -func (am *AccountMerge) BuildXDR() (xdr.Operation, error) { - return am.buildXDR(false) -} - -// BuildXDR for AccountMerge returns a fully configured XDR Operation. -func (am *AccountMerge) buildXDR(withSEP23 bool) (xdr.Operation, error) { +func (am *AccountMerge) BuildXDR(withSEP23 bool) (xdr.Operation, error) { var xdrOp xdr.MuxedAccount var err error if withSEP23 { @@ -37,7 +32,7 @@ func (am *AccountMerge) buildXDR(withSEP23 bool) (xdr.Operation, error) { } op := xdr.Operation{Body: body} if withSEP23 { - SetOpSourceAccountWithSEP23(&op, am.SourceAccount) + SetOpMuxedSourceAccount(&op, am.SourceAccount) } else { SetOpSourceAccount(&op, am.SourceAccount) } @@ -45,22 +40,14 @@ func (am *AccountMerge) buildXDR(withSEP23 bool) (xdr.Operation, error) { } // FromXDR for AccountMerge initialises the txnbuild struct from the corresponding xdr Operation. -func (am *AccountMerge) FromXDR(xdrOp xdr.Operation) error { - if xdrOp.Body.Type != xdr.OperationTypeAccountMerge { - return errors.New("error parsing account_merge operation from xdr") - } - - return am.fromXDR(xdrOp, false) -} - -func (am *AccountMerge) fromXDR(xdrOp xdr.Operation, withSEP23 bool) error { +func (am *AccountMerge) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { if xdrOp.Body.Type != xdr.OperationTypeAccountMerge { return errors.New("error parsing account_merge operation from xdr") } - am.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withSEP23) + am.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) if xdrOp.Body.Destination != nil { - if withSEP23 { + if withMuxedAccounts { am.Destination = xdrOp.Body.Destination.SEP23Address() } else { aid := xdrOp.Body.Destination.ToAccountId() @@ -73,13 +60,9 @@ func (am *AccountMerge) fromXDR(xdrOp xdr.Operation, withSEP23 bool) error { // Validate for AccountMerge validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (am *AccountMerge) Validate() error { - return am.validate(false) -} - -func (am *AccountMerge) validate(withSEP23 bool) error { +func (am *AccountMerge) Validate(withMuxedAccounts bool) error { var err error - if withSEP23 { + if withMuxedAccounts { _, err = xdr.AddressToAccountId(am.Destination) } else { _, err = xdr.SEP23AddressToMuxedAccount(am.Destination) @@ -95,15 +78,3 @@ func (am *AccountMerge) validate(withSEP23 bool) error { func (am *AccountMerge) GetSourceAccount() string { return am.SourceAccount } - -func (am *AccountMerge) BuildXDRWithSEP23() (xdr.Operation, error) { - return am.buildXDR(true) -} - -func (am *AccountMerge) FromXDRWithSEP23(xdrOp xdr.Operation) error { - return am.fromXDR(xdrOp, true) -} - -func (am *AccountMerge) ValidateWithSEP23() error { - return am.validate(true) -} diff --git a/txnbuild/operation.go b/txnbuild/operation.go index 58c10ae676..54e20534ee 100644 --- a/txnbuild/operation.go +++ b/txnbuild/operation.go @@ -8,13 +8,10 @@ import ( // Operation represents the operation types of the Stellar network. type Operation interface { - BuildXDR() (xdr.Operation, error) - FromXDR(xdrOp xdr.Operation) error - Validate() error + BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) + FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error + Validate(withMuxedAccounts bool) error GetSourceAccount() string - BuildXDRWithSEP23() (xdr.Operation, error) - FromXDRWithSEP23(xdrOp xdr.Operation) error - ValidateWithSEP23() error } // SetOpSourceAccount sets the source account ID on an Operation. @@ -28,7 +25,7 @@ func SetOpSourceAccount(op *xdr.Operation, sourceAccount string) { } // SetOpSourceAccount sets the source account ID on an Operation, allowing M-strkeys (as defined in SEP23). -func SetOpSourceAccountWithSEP23(op *xdr.Operation, sourceAccount string) { +func SetOpMuxedSourceAccount(op *xdr.Operation, sourceAccount string) { if sourceAccount == "" { return } @@ -38,7 +35,7 @@ func SetOpSourceAccountWithSEP23(op *xdr.Operation, sourceAccount string) { } // operationFromXDR returns a txnbuild Operation from its corresponding XDR operation -func operationFromXDR(xdrOp xdr.Operation, withSEP23 bool) (Operation, error) { +func operationFromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) (Operation, error) { var newOp Operation switch xdrOp.Body.Type { case xdr.OperationTypeCreateAccount: @@ -88,18 +85,14 @@ func operationFromXDR(xdrOp xdr.Operation, withSEP23 bool) (Operation, error) { default: return nil, fmt.Errorf("unknown operation type: %d", xdrOp.Body.Type) } - var err error - if withSEP23 { - err = newOp.FromXDRWithSEP23(xdrOp) - } else { - err = newOp.FromXDR(xdrOp) - } + + err := newOp.FromXDR(xdrOp, withMuxedAccounts) return newOp, err } -func accountFromXDR(account *xdr.MuxedAccount, withSEP23 bool) string { +func accountFromXDR(account *xdr.MuxedAccount, withMuxedAccounts bool) string { if account != nil { - if withSEP23 { + if withMuxedAccounts { return account.SEP23Address() } else { aid := account.ToAccountId() diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index aa9f5b4b5f..fb001f4a2b 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -514,15 +514,15 @@ func (t GenericTransaction) FeeBump() (*FeeBumpTransaction, bool) { return t.feeBump, t.feeBump != nil } -type TransactionOption int +type ParseXDROption int const ( - TransactionEnableSEP23 = iota + ParseXDROptionEnableMuxedAccounts ParseXDROption = iota ) -func isSEP23Enabled(options []TransactionOption) bool { +func areMuxedAccountsEnabled(options []ParseXDROption) bool { for _, opt := range options { - if opt == TransactionEnableSEP23 { + if opt == ParseXDROptionEnableMuxedAccounts { return true } } @@ -531,16 +531,16 @@ func isSEP23Enabled(options []TransactionOption) bool { // TransactionFromXDR parses the supplied transaction envelope in base64 XDR // and returns a GenericTransaction instance. -func TransactionFromXDR(txeB64 string, options ...TransactionOption) (*GenericTransaction, error) { +func TransactionFromXDR(txeB64 string, options ...ParseXDROption) (*GenericTransaction, error) { var xdrEnv xdr.TransactionEnvelope err := xdr.SafeUnmarshalBase64(txeB64, &xdrEnv) if err != nil { return nil, errors.Wrap(err, "unable to unmarshal transaction envelope") } - return transactionFromParsedXDR(xdrEnv, isSEP23Enabled(options)) + return transactionFromParsedXDR(xdrEnv, areMuxedAccountsEnabled(options)) } -func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope, withSEP23 bool) (*GenericTransaction, error) { +func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope, withMuxedAccounts bool) (*GenericTransaction, error) { var err error newTx := &GenericTransaction{} @@ -549,12 +549,12 @@ func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope, withSEP23 bool) (* innerTx, err = transactionFromParsedXDR(xdr.TransactionEnvelope{ Type: xdr.EnvelopeTypeEnvelopeTypeTx, V1: xdrEnv.FeeBump.Tx.InnerTx.V1, - }, withSEP23) + }, withMuxedAccounts) if err != nil { return newTx, errors.New("could not parse inner transaction") } var feeAccount string - if withSEP23 { + if withMuxedAccounts { feeBumpAccount := xdrEnv.FeeBumpAccount() feeAccount = feeBumpAccount.SEP23Address() } else { @@ -577,7 +577,7 @@ func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope, withSEP23 bool) (* return newTx, nil } var accountID string - if withSEP23 { + if withMuxedAccounts { sourceAccount := xdrEnv.SourceAccount() accountID = sourceAccount.SEP23Address() } else { @@ -615,7 +615,7 @@ func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope, withSEP23 bool) (* operations := xdrEnv.Operations() for _, op := range operations { - newOp, err := operationFromXDR(op, withSEP23) + newOp, err := operationFromXDR(op, withMuxedAccounts) if err != nil { return nil, err } @@ -634,7 +634,7 @@ type TransactionParams struct { BaseFee int64 Memo Memo Timebounds Timebounds - Options []TransactionOption + EnableMuxedAccounts bool } // NewTransaction returns a new Transaction instance @@ -665,9 +665,8 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { memo: params.Memo, timebounds: params.Timebounds, } - withSEP23 := isSEP23Enabled(params.Options) var sourceAccount xdr.MuxedAccount - if withSEP23 { + if params.EnableMuxedAccounts { if err := sourceAccount.SetAddressWithSEP23(tx.sourceAccount.AccountID); err != nil { return nil, errors.Wrap(err, "account id is not valid") } @@ -737,7 +736,7 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { err2 error xdrOperation xdr.Operation ) - if withSEP23 { + if params.EnableMuxedAccounts { xdrOperation, err2 = op.BuildXDRWithSEP23() } else { xdrOperation, err2 = op.BuildXDR() @@ -755,10 +754,10 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { // FeeBumpTransactionParams is a container for parameters // which are used to construct new FeeBumpTransaction instances type FeeBumpTransactionParams struct { - Inner *Transaction - FeeAccount string - BaseFee int64 - Options []TransactionOption + Inner *Transaction + FeeAccount string + BaseFee int64 + EnableMuxedAccounts bool } func convertToV1(tx *Transaction) (*Transaction, error) { @@ -830,7 +829,7 @@ func NewFeeBumpTransaction(params FeeBumpTransactionParams) (*FeeBumpTransaction } var feeSource xdr.MuxedAccount - if isSEP23Enabled(params.Options) { + if params.EnableMuxedAccounts { if err := feeSource.SetAddressWithSEP23(tx.feeAccount); err != nil { return tx, errors.Wrap(err, "fee account is not a valid address") } From f862efbed3f2d9958234cf0a835b2e0101742b4b Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Wed, 7 Apr 2021 21:55:36 +0200 Subject: [PATCH 06/22] Address review comments --- clients/horizonclient/client.go | 2 +- strkey/main.go | 2 -- txnbuild/allow_trust.go | 6 ++-- txnbuild/begin_sponsoring_future_reserves.go | 6 ++-- txnbuild/bump_sequence.go | 6 ++-- txnbuild/change_trust.go | 6 ++-- txnbuild/claim_claimable_balance.go | 6 ++-- txnbuild/clawback.go | 6 ++-- txnbuild/clawback_claimable_balance.go | 6 ++-- txnbuild/create_account.go | 6 ++-- txnbuild/create_claimable_balance.go | 6 ++-- txnbuild/create_passive_offer.go | 6 ++-- txnbuild/create_passive_offer_test.go | 4 +-- txnbuild/end_sponsoring_future_reserves.go | 6 ++-- txnbuild/inflation.go | 6 ++-- txnbuild/manage_buy_offer.go | 6 ++-- txnbuild/manage_buy_offer_test.go | 4 +-- txnbuild/manage_data.go | 6 ++-- txnbuild/manage_offer.go | 6 ++-- txnbuild/manage_offer_test.go | 4 +-- txnbuild/operation_test.go | 32 ++++++++++---------- txnbuild/path_payment.go | 6 ++-- txnbuild/path_payment_strict_send.go | 6 ++-- txnbuild/payment.go | 6 ++-- txnbuild/revoke_sponsorship.go | 6 ++-- txnbuild/revoke_sponsorship_test.go | 6 ++-- txnbuild/set_options.go | 6 ++-- txnbuild/set_options_test.go | 2 +- txnbuild/set_trust_line_flags.go | 6 ++-- txnbuild/set_trustline_flags_test.go | 6 ++-- txnbuild/transaction.go | 4 +-- 31 files changed, 95 insertions(+), 97 deletions(-) diff --git a/clients/horizonclient/client.go b/clients/horizonclient/client.go index 92cb00b3ee..36016506d3 100644 --- a/clients/horizonclient/client.go +++ b/clients/horizonclient/client.go @@ -47,7 +47,7 @@ func (c *Client) checkMemoRequired(transaction *txnbuild.Transaction) error { for i, op := range transaction.Operations() { var destination string - if err := op.Validate(); err != nil { + if err := op.Validate(false); err != nil { return err } diff --git a/strkey/main.go b/strkey/main.go index 76582e51af..6facce6e2c 100644 --- a/strkey/main.go +++ b/strkey/main.go @@ -164,8 +164,6 @@ func Version(src string) (VersionByte, error) { // is not one of the defined valid version byte constants. func checkValidVersionByte(version VersionByte) error { switch version { - // intentionally disallow M-strkeys (versionByteMuxedAccount) - // until SEP23 leaves the Draft status. case VersionByteAccountID, VersionByteMuxedAccount, VersionByteSeed, VersionByteHashTx, VersionByteHashX: return nil default: diff --git a/txnbuild/allow_trust.go b/txnbuild/allow_trust.go index 6bb22d97fc..ce67cb99d3 100644 --- a/txnbuild/allow_trust.go +++ b/txnbuild/allow_trust.go @@ -21,7 +21,7 @@ type AllowTrust struct { } // BuildXDR for AllowTrust returns a fully configured XDR Operation. -func (at *AllowTrust) BuildXDR() (xdr.Operation, error) { +func (at *AllowTrust) BuildXDR(bool) (xdr.Operation, error) { var xdrOp xdr.AllowTrustOp // Set XDR address associated with the trustline @@ -75,7 +75,7 @@ func assetCodeToCreditAsset(assetCode xdr.AssetCode) (CreditAsset, error) { } // FromXDR for AllowTrust initialises the txnbuild struct from the corresponding xdr Operation. -func (at *AllowTrust) FromXDR(xdrOp xdr.Operation) error { +func (at *AllowTrust) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetAllowTrustOp() if !ok { return errors.New("error parsing allow_trust operation from xdr") @@ -97,7 +97,7 @@ func (at *AllowTrust) FromXDR(xdrOp xdr.Operation) error { // Validate for AllowTrust validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (at *AllowTrust) Validate() error { +func (at *AllowTrust) Validate(bool) error { err := validateStellarPublicKey(at.Trustor) if err != nil { return NewValidationError("Trustor", err.Error()) diff --git a/txnbuild/begin_sponsoring_future_reserves.go b/txnbuild/begin_sponsoring_future_reserves.go index c4a551389f..5d08361f6d 100644 --- a/txnbuild/begin_sponsoring_future_reserves.go +++ b/txnbuild/begin_sponsoring_future_reserves.go @@ -15,7 +15,7 @@ type BeginSponsoringFutureReserves struct { } // BuildXDR for BeginSponsoringFutureReserves returns a fully configured XDR Operation. -func (bs *BeginSponsoringFutureReserves) BuildXDR() (xdr.Operation, error) { +func (bs *BeginSponsoringFutureReserves) BuildXDR(bool) (xdr.Operation, error) { xdrOp := xdr.BeginSponsoringFutureReservesOp{} err := xdrOp.SponsoredId.SetAddress(bs.SponsoredID) if err != nil { @@ -32,7 +32,7 @@ func (bs *BeginSponsoringFutureReserves) BuildXDR() (xdr.Operation, error) { } // FromXDR for BeginSponsoringFutureReserves initializes the txnbuild struct from the corresponding xdr Operation. -func (bs *BeginSponsoringFutureReserves) FromXDR(xdrOp xdr.Operation) error { +func (bs *BeginSponsoringFutureReserves) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetBeginSponsoringFutureReservesOp() if !ok { return errors.New("error parsing begin_sponsoring_future_reserves operation from xdr") @@ -45,7 +45,7 @@ func (bs *BeginSponsoringFutureReserves) FromXDR(xdrOp xdr.Operation) error { // Validate for BeginSponsoringFutureReserves validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (bs *BeginSponsoringFutureReserves) Validate() error { +func (bs *BeginSponsoringFutureReserves) Validate(bool) error { err := validateStellarPublicKey(bs.SponsoredID) if err != nil { return NewValidationError("SponsoredID", err.Error()) diff --git a/txnbuild/bump_sequence.go b/txnbuild/bump_sequence.go index 09def74c4d..64943f9bef 100644 --- a/txnbuild/bump_sequence.go +++ b/txnbuild/bump_sequence.go @@ -13,7 +13,7 @@ type BumpSequence struct { } // BuildXDR for BumpSequence returns a fully configured XDR Operation. -func (bs *BumpSequence) BuildXDR() (xdr.Operation, error) { +func (bs *BumpSequence) BuildXDR(bool) (xdr.Operation, error) { opType := xdr.OperationTypeBumpSequence xdrOp := xdr.BumpSequenceOp{BumpTo: xdr.SequenceNumber(bs.BumpTo)} body, err := xdr.NewOperationBody(opType, xdrOp) @@ -26,7 +26,7 @@ func (bs *BumpSequence) BuildXDR() (xdr.Operation, error) { } // FromXDR for BumpSequence initialises the txnbuild struct from the corresponding xdr Operation. -func (bs *BumpSequence) FromXDR(xdrOp xdr.Operation) error { +func (bs *BumpSequence) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetBumpSequenceOp() if !ok { return errors.New("error parsing bump_sequence operation from xdr") @@ -39,7 +39,7 @@ func (bs *BumpSequence) FromXDR(xdrOp xdr.Operation) error { // Validate for BumpSequence validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (bs *BumpSequence) Validate() error { +func (bs *BumpSequence) Validate(bool) error { err := validateAmount(bs.BumpTo) if err != nil { return NewValidationError("BumpTo", err.Error()) diff --git a/txnbuild/change_trust.go b/txnbuild/change_trust.go index 8efa08902b..bdd4897cf5 100644 --- a/txnbuild/change_trust.go +++ b/txnbuild/change_trust.go @@ -30,7 +30,7 @@ func RemoveTrustlineOp(issuedAsset Asset) ChangeTrust { } // BuildXDR for ChangeTrust returns a fully configured XDR Operation. -func (ct *ChangeTrust) BuildXDR() (xdr.Operation, error) { +func (ct *ChangeTrust) BuildXDR(bool) (xdr.Operation, error) { if ct.Line.IsNative() { return xdr.Operation{}, errors.New("trustline cannot be extended to a native (XLM) asset") } @@ -63,7 +63,7 @@ func (ct *ChangeTrust) BuildXDR() (xdr.Operation, error) { } // FromXDR for ChangeTrust initialises the txnbuild struct from the corresponding xdr Operation. -func (ct *ChangeTrust) FromXDR(xdrOp xdr.Operation) error { +func (ct *ChangeTrust) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetChangeTrustOp() if !ok { return errors.New("error parsing change_trust operation from xdr") @@ -81,7 +81,7 @@ func (ct *ChangeTrust) FromXDR(xdrOp xdr.Operation) error { // Validate for ChangeTrust validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (ct *ChangeTrust) Validate() error { +func (ct *ChangeTrust) Validate(bool) error { // only validate limit if it has a value. Empty limit is set to the max trustline limit. if ct.Limit != "" { err := validateAmount(ct.Limit) diff --git a/txnbuild/claim_claimable_balance.go b/txnbuild/claim_claimable_balance.go index f9d9d442e0..f1b8f9abe2 100644 --- a/txnbuild/claim_claimable_balance.go +++ b/txnbuild/claim_claimable_balance.go @@ -15,7 +15,7 @@ type ClaimClaimableBalance struct { } // BuildXDR for ClaimClaimableBalance returns a fully configured XDR Operation. -func (cb *ClaimClaimableBalance) BuildXDR() (xdr.Operation, error) { +func (cb *ClaimClaimableBalance) BuildXDR(bool) (xdr.Operation, error) { var xdrBalanceID xdr.ClaimableBalanceId err := xdr.SafeUnmarshalHex(cb.BalanceID, &xdrBalanceID) if err != nil { @@ -36,7 +36,7 @@ func (cb *ClaimClaimableBalance) BuildXDR() (xdr.Operation, error) { } // FromXDR for ClaimClaimableBalance initializes the txnbuild struct from the corresponding xdr Operation. -func (cb *ClaimClaimableBalance) FromXDR(xdrOp xdr.Operation) error { +func (cb *ClaimClaimableBalance) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetClaimClaimableBalanceOp() if !ok { return errors.New("error parsing claim_claimable_balance operation from xdr") @@ -54,7 +54,7 @@ func (cb *ClaimClaimableBalance) FromXDR(xdrOp xdr.Operation) error { // Validate for ClaimClaimableBalance validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (cb *ClaimClaimableBalance) Validate() error { +func (cb *ClaimClaimableBalance) Validate(bool) error { var xdrBalanceID xdr.ClaimableBalanceId err := xdr.SafeUnmarshalHex(cb.BalanceID, &xdrBalanceID) if err != nil { diff --git a/txnbuild/clawback.go b/txnbuild/clawback.go index a19e91743a..957b709610 100644 --- a/txnbuild/clawback.go +++ b/txnbuild/clawback.go @@ -17,7 +17,7 @@ type Clawback struct { } // BuildXDR for Clawback returns a fully configured XDR Operation. -func (cb *Clawback) BuildXDR() (xdr.Operation, error) { +func (cb *Clawback) BuildXDR(bool) (xdr.Operation, error) { var fromMuxedAccount xdr.MuxedAccount err := fromMuxedAccount.SetAddress(cb.From) @@ -59,7 +59,7 @@ func (cb *Clawback) BuildXDR() (xdr.Operation, error) { } // FromXDR for Clawback initialises the txnbuild struct from the corresponding xdr Operation. -func (cb *Clawback) FromXDR(xdrOp xdr.Operation) error { +func (cb *Clawback) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetClawbackOp() if !ok { return errors.New("error parsing clawback operation from xdr") @@ -80,7 +80,7 @@ func (cb *Clawback) FromXDR(xdrOp xdr.Operation) error { // Validate for Clawback validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (cb *Clawback) Validate() error { +func (cb *Clawback) Validate(bool) error { _, err := xdr.AddressToAccountId(cb.From) if err != nil { return NewValidationError("From", err.Error()) diff --git a/txnbuild/clawback_claimable_balance.go b/txnbuild/clawback_claimable_balance.go index 4188ac7c3d..af9d38f09a 100644 --- a/txnbuild/clawback_claimable_balance.go +++ b/txnbuild/clawback_claimable_balance.go @@ -14,7 +14,7 @@ type ClawbackClaimableBalance struct { } // BuildXDR for ClawbackClaimableBalance returns a fully configured XDR Operation. -func (cb *ClawbackClaimableBalance) BuildXDR() (xdr.Operation, error) { +func (cb *ClawbackClaimableBalance) BuildXDR(bool) (xdr.Operation, error) { var xdrBalanceID xdr.ClaimableBalanceId err := xdr.SafeUnmarshalHex(cb.BalanceID, &xdrBalanceID) if err != nil { @@ -35,7 +35,7 @@ func (cb *ClawbackClaimableBalance) BuildXDR() (xdr.Operation, error) { } // FromXDR for ClawbackClaimableBalance initializes the txnbuild struct from the corresponding xdr Operation. -func (cb *ClawbackClaimableBalance) FromXDR(xdrOp xdr.Operation) error { +func (cb *ClawbackClaimableBalance) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetClawbackClaimableBalanceOp() if !ok { return errors.New("error parsing clawback_claimable_balance operation from xdr") @@ -53,7 +53,7 @@ func (cb *ClawbackClaimableBalance) FromXDR(xdrOp xdr.Operation) error { // Validate for ClawbackClaimableBalance validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (cb *ClawbackClaimableBalance) Validate() error { +func (cb *ClawbackClaimableBalance) Validate(bool) error { var xdrBalanceID xdr.ClaimableBalanceId err := xdr.SafeUnmarshalHex(cb.BalanceID, &xdrBalanceID) if err != nil { diff --git a/txnbuild/create_account.go b/txnbuild/create_account.go index 02e8b3e17d..04aed3b48b 100644 --- a/txnbuild/create_account.go +++ b/txnbuild/create_account.go @@ -15,7 +15,7 @@ type CreateAccount struct { } // BuildXDR for CreateAccount returns a fully configured XDR Operation. -func (ca *CreateAccount) BuildXDR() (xdr.Operation, error) { +func (ca *CreateAccount) BuildXDR(bool) (xdr.Operation, error) { var xdrOp xdr.CreateAccountOp err := xdrOp.Destination.SetAddress(ca.Destination) @@ -39,7 +39,7 @@ func (ca *CreateAccount) BuildXDR() (xdr.Operation, error) { } // FromXDR for CreateAccount initialises the txnbuild struct from the corresponding xdr Operation. -func (ca *CreateAccount) FromXDR(xdrOp xdr.Operation) error { +func (ca *CreateAccount) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetCreateAccountOp() if !ok { return errors.New("error parsing create_account operation from xdr") @@ -54,7 +54,7 @@ func (ca *CreateAccount) FromXDR(xdrOp xdr.Operation) error { // Validate for CreateAccount validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (ca *CreateAccount) Validate() error { +func (ca *CreateAccount) Validate(bool) error { err := validateStellarPublicKey(ca.Destination) if err != nil { return NewValidationError("Destination", err.Error()) diff --git a/txnbuild/create_claimable_balance.go b/txnbuild/create_claimable_balance.go index 812102cb43..cb89fc34c2 100644 --- a/txnbuild/create_claimable_balance.go +++ b/txnbuild/create_claimable_balance.go @@ -96,7 +96,7 @@ func BeforeRelativeTimePredicate(secondsBefore int64) xdr.ClaimPredicate { } // BuildXDR for CreateClaimableBalance returns a fully configured XDR Operation. -func (cb *CreateClaimableBalance) BuildXDR() (xdr.Operation, error) { +func (cb *CreateClaimableBalance) BuildXDR(bool) (xdr.Operation, error) { xdrAsset, err := cb.Asset.ToXDR() if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Asset' field") @@ -139,7 +139,7 @@ func (cb *CreateClaimableBalance) BuildXDR() (xdr.Operation, error) { } // FromXDR for CreateClaimableBalance initializes the txnbuild struct from the corresponding xdr Operation. -func (cb *CreateClaimableBalance) FromXDR(xdrOp xdr.Operation) error { +func (cb *CreateClaimableBalance) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetCreateClaimableBalanceOp() if !ok { return errors.New("error parsing create_claimable_balance operation from xdr") @@ -166,7 +166,7 @@ func (cb *CreateClaimableBalance) FromXDR(xdrOp xdr.Operation) error { // Validate for CreateClaimableBalance validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (cb *CreateClaimableBalance) Validate() error { +func (cb *CreateClaimableBalance) Validate(bool) error { for _, d := range cb.Destinations { err := validateStellarPublicKey(d.Destination) if err != nil { diff --git a/txnbuild/create_passive_offer.go b/txnbuild/create_passive_offer.go index 1eaba8e964..c96e800c71 100644 --- a/txnbuild/create_passive_offer.go +++ b/txnbuild/create_passive_offer.go @@ -18,7 +18,7 @@ type CreatePassiveSellOffer struct { } // BuildXDR for CreatePassiveSellOffer returns a fully configured XDR Operation. -func (cpo *CreatePassiveSellOffer) BuildXDR() (xdr.Operation, error) { +func (cpo *CreatePassiveSellOffer) BuildXDR(bool) (xdr.Operation, error) { xdrSelling, err := cpo.Selling.ToXDR() if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Selling' field") @@ -56,7 +56,7 @@ func (cpo *CreatePassiveSellOffer) BuildXDR() (xdr.Operation, error) { } // FromXDR for CreatePassiveSellOffer initialises the txnbuild struct from the corresponding xdr Operation. -func (cpo *CreatePassiveSellOffer) FromXDR(xdrOp xdr.Operation) error { +func (cpo *CreatePassiveSellOffer) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetCreatePassiveSellOfferOp() if !ok { return errors.New("error parsing create_passive_sell_offer operation from xdr") @@ -84,7 +84,7 @@ func (cpo *CreatePassiveSellOffer) FromXDR(xdrOp xdr.Operation) error { // Validate for CreatePassiveSellOffer validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (cpo *CreatePassiveSellOffer) Validate() error { +func (cpo *CreatePassiveSellOffer) Validate(bool) error { return validatePassiveOffer(cpo.Buying, cpo.Selling, cpo.Amount, cpo.Price) } diff --git a/txnbuild/create_passive_offer_test.go b/txnbuild/create_passive_offer_test.go index 3b158d6987..0de8e0ba00 100644 --- a/txnbuild/create_passive_offer_test.go +++ b/txnbuild/create_passive_offer_test.go @@ -125,7 +125,7 @@ func TestCreatePassiveSellOfferPrice(t *testing.T) { SourceAccount: kp0.Address(), } - xdrOp, err := offer.BuildXDR() + xdrOp, err := offer.BuildXDR(false) assert.NoError(t, err) expectedPrice := xdr.Price{N: 1, D: 1000000000} assert.Equal(t, expectedPrice, xdrOp.Body.CreatePassiveSellOfferOp.Price) @@ -133,7 +133,7 @@ func TestCreatePassiveSellOfferPrice(t *testing.T) { assert.Equal(t, expectedPrice, offer.price.toXDR()) parsed := CreatePassiveSellOffer{} - assert.NoError(t, parsed.FromXDR(xdrOp)) + assert.NoError(t, parsed.FromXDR(xdrOp, false)) assert.Equal(t, offer.Price, parsed.Price) assert.Equal(t, offer.price, parsed.price) } diff --git a/txnbuild/end_sponsoring_future_reserves.go b/txnbuild/end_sponsoring_future_reserves.go index 297d4ceee8..425ece6fe1 100644 --- a/txnbuild/end_sponsoring_future_reserves.go +++ b/txnbuild/end_sponsoring_future_reserves.go @@ -14,7 +14,7 @@ type EndSponsoringFutureReserves struct { } // BuildXDR for EndSponsoringFutureReserves returns a fully configured XDR Operation. -func (es *EndSponsoringFutureReserves) BuildXDR() (xdr.Operation, error) { +func (es *EndSponsoringFutureReserves) BuildXDR(bool) (xdr.Operation, error) { opType := xdr.OperationTypeEndSponsoringFutureReserves body, err := xdr.NewOperationBody(opType, nil) if err != nil { @@ -26,7 +26,7 @@ func (es *EndSponsoringFutureReserves) BuildXDR() (xdr.Operation, error) { } // FromXDR for EndSponsoringFutureReserves initializes the txnbuild struct from the corresponding xdr Operation. -func (es *EndSponsoringFutureReserves) FromXDR(xdrOp xdr.Operation) error { +func (es *EndSponsoringFutureReserves) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { if xdrOp.Body.Type != xdr.OperationTypeEndSponsoringFutureReserves { return errors.New("error parsing end_sponsoring_future_reserves operation from xdr") } @@ -37,7 +37,7 @@ func (es *EndSponsoringFutureReserves) FromXDR(xdrOp xdr.Operation) error { // Validate for EndSponsoringFutureReserves validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (es *EndSponsoringFutureReserves) Validate() error { +func (es *EndSponsoringFutureReserves) Validate(bool) error { return nil } diff --git a/txnbuild/inflation.go b/txnbuild/inflation.go index 1944b9c2e6..d1fcbe8737 100644 --- a/txnbuild/inflation.go +++ b/txnbuild/inflation.go @@ -12,7 +12,7 @@ type Inflation struct { } // BuildXDR for Inflation returns a fully configured XDR Operation. -func (inf *Inflation) BuildXDR() (xdr.Operation, error) { +func (inf *Inflation) BuildXDR(bool) (xdr.Operation, error) { opType := xdr.OperationTypeInflation body, err := xdr.NewOperationBody(opType, nil) if err != nil { @@ -24,7 +24,7 @@ func (inf *Inflation) BuildXDR() (xdr.Operation, error) { } // FromXDR for Inflation initialises the txnbuild struct from the corresponding xdr Operation. -func (inf *Inflation) FromXDR(xdrOp xdr.Operation) error { +func (inf *Inflation) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { if xdrOp.Body.Type != xdr.OperationTypeInflation { return errors.New("error parsing inflation operation from xdr") } @@ -34,7 +34,7 @@ func (inf *Inflation) FromXDR(xdrOp xdr.Operation) error { // Validate for Inflation is just a method that implements the Operation interface. No logic is actually performed // because the inflation operation does not have any required field. Nil is always returned. -func (inf *Inflation) Validate() error { +func (inf *Inflation) Validate(bool) error { // no required fields, return nil. return nil } diff --git a/txnbuild/manage_buy_offer.go b/txnbuild/manage_buy_offer.go index 030e750dd0..c56071da44 100644 --- a/txnbuild/manage_buy_offer.go +++ b/txnbuild/manage_buy_offer.go @@ -19,7 +19,7 @@ type ManageBuyOffer struct { } // BuildXDR for ManageBuyOffer returns a fully configured XDR Operation. -func (mo *ManageBuyOffer) BuildXDR() (xdr.Operation, error) { +func (mo *ManageBuyOffer) BuildXDR(bool) (xdr.Operation, error) { xdrSelling, err := mo.Selling.ToXDR() if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Selling' field") @@ -58,7 +58,7 @@ func (mo *ManageBuyOffer) BuildXDR() (xdr.Operation, error) { } // FromXDR for ManageBuyOffer initialises the txnbuild struct from the corresponding xdr Operation. -func (mo *ManageBuyOffer) FromXDR(xdrOp xdr.Operation) error { +func (mo *ManageBuyOffer) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetManageBuyOfferOp() if !ok { return errors.New("error parsing manage_buy_offer operation from xdr") @@ -87,7 +87,7 @@ func (mo *ManageBuyOffer) FromXDR(xdrOp xdr.Operation) error { // Validate for ManageBuyOffer validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (mo *ManageBuyOffer) Validate() error { +func (mo *ManageBuyOffer) Validate(bool) error { return validateOffer(mo.Buying, mo.Selling, mo.Amount, mo.Price, mo.OfferID) } diff --git a/txnbuild/manage_buy_offer_test.go b/txnbuild/manage_buy_offer_test.go index 3eecbb49fc..c12ef072e6 100644 --- a/txnbuild/manage_buy_offer_test.go +++ b/txnbuild/manage_buy_offer_test.go @@ -158,7 +158,7 @@ func TestManageBuyOfferPrice(t *testing.T) { OfferID: 1, } - xdrOp, err := mbo.BuildXDR() + xdrOp, err := mbo.BuildXDR(false) assert.NoError(t, err) expectedPrice := xdr.Price{N: 1, D: 1000000000} assert.Equal(t, expectedPrice, xdrOp.Body.ManageBuyOfferOp.Price) @@ -166,7 +166,7 @@ func TestManageBuyOfferPrice(t *testing.T) { assert.Equal(t, expectedPrice, mbo.price.toXDR()) parsed := ManageBuyOffer{} - assert.NoError(t, parsed.FromXDR(xdrOp)) + assert.NoError(t, parsed.FromXDR(xdrOp, false)) assert.Equal(t, mbo.Price, parsed.Price) assert.Equal(t, mbo.price, parsed.price) } diff --git a/txnbuild/manage_data.go b/txnbuild/manage_data.go index 911d01ed0f..d694e50c0b 100644 --- a/txnbuild/manage_data.go +++ b/txnbuild/manage_data.go @@ -14,7 +14,7 @@ type ManageData struct { } // BuildXDR for ManageData returns a fully configured XDR Operation. -func (md *ManageData) BuildXDR() (xdr.Operation, error) { +func (md *ManageData) BuildXDR(bool) (xdr.Operation, error) { xdrOp := xdr.ManageDataOp{DataName: xdr.String64(md.Name)} // No data value clears the named data entry on the account @@ -36,7 +36,7 @@ func (md *ManageData) BuildXDR() (xdr.Operation, error) { } // FromXDR for ManageData initialises the txnbuild struct from the corresponding xdr Operation. -func (md *ManageData) FromXDR(xdrOp xdr.Operation) error { +func (md *ManageData) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetManageDataOp() if !ok { return errors.New("error parsing create_account operation from xdr") @@ -54,7 +54,7 @@ func (md *ManageData) FromXDR(xdrOp xdr.Operation) error { // Validate for ManageData validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (md *ManageData) Validate() error { +func (md *ManageData) Validate(bool) error { if len(md.Name) > 64 { return NewValidationError("Name", "maximum length is 64 characters") } diff --git a/txnbuild/manage_offer.go b/txnbuild/manage_offer.go index 021250aedb..412e8e0630 100644 --- a/txnbuild/manage_offer.go +++ b/txnbuild/manage_offer.go @@ -83,7 +83,7 @@ type ManageSellOffer struct { } // BuildXDR for ManageSellOffer returns a fully configured XDR Operation. -func (mo *ManageSellOffer) BuildXDR() (xdr.Operation, error) { +func (mo *ManageSellOffer) BuildXDR(bool) (xdr.Operation, error) { xdrSelling, err := mo.Selling.ToXDR() if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Selling' field") @@ -122,7 +122,7 @@ func (mo *ManageSellOffer) BuildXDR() (xdr.Operation, error) { } // FromXDR for ManageSellOffer initialises the txnbuild struct from the corresponding xdr Operation. -func (mo *ManageSellOffer) FromXDR(xdrOp xdr.Operation) error { +func (mo *ManageSellOffer) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetManageSellOfferOp() if !ok { return errors.New("error parsing manage_sell_offer operation from xdr") @@ -151,7 +151,7 @@ func (mo *ManageSellOffer) FromXDR(xdrOp xdr.Operation) error { // Validate for ManageSellOffer validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (mo *ManageSellOffer) Validate() error { +func (mo *ManageSellOffer) Validate(bool) error { return validateOffer(mo.Buying, mo.Selling, mo.Amount, mo.Price, mo.OfferID) } diff --git a/txnbuild/manage_offer_test.go b/txnbuild/manage_offer_test.go index feee18c4b7..8d0f447240 100644 --- a/txnbuild/manage_offer_test.go +++ b/txnbuild/manage_offer_test.go @@ -154,7 +154,7 @@ func TestManageSellOfferPrice(t *testing.T) { OfferID: 1, } - xdrOp, err := mso.BuildXDR() + xdrOp, err := mso.BuildXDR(false) assert.NoError(t, err) expectedPrice := xdr.Price{N: 1, D: 1000000000} assert.Equal(t, expectedPrice, xdrOp.Body.ManageSellOfferOp.Price) @@ -162,7 +162,7 @@ func TestManageSellOfferPrice(t *testing.T) { assert.Equal(t, expectedPrice, mso.price.toXDR()) parsed := ManageSellOffer{} - assert.NoError(t, parsed.FromXDR(xdrOp)) + assert.NoError(t, parsed.FromXDR(xdrOp, false)) assert.Equal(t, mso.Price, parsed.Price) assert.Equal(t, mso.price, parsed.price) } diff --git a/txnbuild/operation_test.go b/txnbuild/operation_test.go index f808bb3420..62eaadc2e9 100644 --- a/txnbuild/operation_test.go +++ b/txnbuild/operation_test.go @@ -15,7 +15,7 @@ func TestCreateAccountFromXDR(t *testing.T) { xdrEnv, err := unmarshalBase64(txeB64) if assert.NoError(t, err) { var ca CreateAccount - err = ca.FromXDR(xdrEnv.Operations()[0]) + err = ca.FromXDR(xdrEnv.Operations()[0], false) if assert.NoError(t, err) { assert.Equal(t, "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR", ca.SourceAccount, "source accounts should match") assert.Equal(t, "GCPHE7DYMAUAY4UWA6OIYDFGLKRXGLLEMT6MVETC36L7LW4Z3A37EJW5", ca.Destination, "destination should match") @@ -27,7 +27,7 @@ func TestCreateAccountFromXDR(t *testing.T) { xdrEnv, err = unmarshalBase64(txeB64NoSource) if assert.NoError(t, err) { var ca CreateAccount - err = ca.FromXDR(xdrEnv.Operations()[0]) + err = ca.FromXDR(xdrEnv.Operations()[0], false) if assert.NoError(t, err) { assert.Equal(t, "", ca.SourceAccount, "source accounts should match") assert.Equal(t, "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR", ca.Destination, "destination should match") @@ -69,7 +69,7 @@ func TestPaymentFromXDR(t *testing.T) { xdrEnv, err := unmarshalBase64(txeB64) if assert.NoError(t, err) { var p Payment - err = p.FromXDR(xdrEnv.Operations()[0]) + err = p.FromXDR(xdrEnv.Operations()[0], false) if assert.NoError(t, err) { assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", p.SourceAccount, "source accounts should match") assert.Equal(t, "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR", p.Destination, "destination should match") @@ -77,7 +77,7 @@ func TestPaymentFromXDR(t *testing.T) { assert.Equal(t, true, p.Asset.IsNative(), "Asset should be native") } - err = p.FromXDR(xdrEnv.Operations()[1]) + err = p.FromXDR(xdrEnv.Operations()[1], false) if assert.NoError(t, err) { assert.Equal(t, "", p.SourceAccount, "source accounts should match") assert.Equal(t, "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR", p.Destination, "destination should match") @@ -97,7 +97,7 @@ func TestPathPaymentFromXDR(t *testing.T) { xdrEnv, err := unmarshalBase64(txeB64) if assert.NoError(t, err) { var pp PathPayment - err = pp.FromXDR(xdrEnv.Operations()[0]) + err = pp.FromXDR(xdrEnv.Operations()[0], false) if assert.NoError(t, err) { assert.Equal(t, "", pp.SourceAccount, "source accounts should match") assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", pp.Destination, "destination should match") @@ -120,7 +120,7 @@ func TestManageSellOfferFromXDR(t *testing.T) { xdrEnv, err := unmarshalBase64(txeB64) if assert.NoError(t, err) { var mso ManageSellOffer - err = mso.FromXDR(xdrEnv.Operations()[0]) + err = mso.FromXDR(xdrEnv.Operations()[0], false) if assert.NoError(t, err) { assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", mso.SourceAccount, "source accounts should match") assert.Equal(t, int64(0), mso.OfferID, "OfferID should match") @@ -134,7 +134,7 @@ func TestManageSellOfferFromXDR(t *testing.T) { assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", mso.Buying.GetIssuer(), "Asset issuer should match") } - err = mso.FromXDR(xdrEnv.Operations()[1]) + err = mso.FromXDR(xdrEnv.Operations()[1], false) if assert.NoError(t, err) { assert.Equal(t, "", mso.SourceAccount, "source accounts should match") assert.Equal(t, int64(0), mso.OfferID, "OfferID should match") @@ -157,7 +157,7 @@ func TestManageBuyOfferFromXDR(t *testing.T) { xdrEnv, err := unmarshalBase64(txeB64) if assert.NoError(t, err) { var mbo ManageBuyOffer - err = mbo.FromXDR(xdrEnv.Operations()[0]) + err = mbo.FromXDR(xdrEnv.Operations()[0], false) if assert.NoError(t, err) { assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", mbo.SourceAccount, "source accounts should match") assert.Equal(t, int64(0), mbo.OfferID, "OfferID should match") @@ -171,7 +171,7 @@ func TestManageBuyOfferFromXDR(t *testing.T) { assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", mbo.Buying.GetIssuer(), "Asset issuer should match") } - err = mbo.FromXDR(xdrEnv.Operations()[1]) + err = mbo.FromXDR(xdrEnv.Operations()[1], false) if assert.NoError(t, err) { assert.Equal(t, "", mbo.SourceAccount, "source accounts should match") assert.Equal(t, int64(0), mbo.OfferID, "OfferID should match") @@ -194,7 +194,7 @@ func TestCreatePassiveSellOfferFromXDR(t *testing.T) { xdrEnv, err := unmarshalBase64(txeB64) if assert.NoError(t, err) { var cpo CreatePassiveSellOffer - err = cpo.FromXDR(xdrEnv.Operations()[0]) + err = cpo.FromXDR(xdrEnv.Operations()[0], false) if assert.NoError(t, err) { assert.Equal(t, "", cpo.SourceAccount, "source accounts should match") assert.Equal(t, "10.0000000", cpo.Amount, "Amount should match") @@ -251,7 +251,7 @@ func TestSetOptionsFromXDR(t *testing.T) { } var so SetOptions - err = so.FromXDR(xdrOp) + err = so.FromXDR(xdrOp, false) if assert.NoError(t, err) { assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", so.SourceAccount, "source accounts should match") assert.Equal(t, Threshold(7), *so.MasterWeight, "master weight should match") @@ -295,7 +295,7 @@ func TestChangeTrustFromXDR(t *testing.T) { } var ct ChangeTrust - err = ct.FromXDR(xdrOp) + err = ct.FromXDR(xdrOp, false) if assert.NoError(t, err) { assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", ct.SourceAccount, "source accounts should match") assetType, e := ct.Line.GetType() @@ -336,7 +336,7 @@ func TestAllowTrustFromXDR(t *testing.T) { } var at AllowTrust - err = at.FromXDR(xdrOp) + err = at.FromXDR(xdrOp, false) if assert.NoError(t, err) { assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", at.SourceAccount, "source accounts should match") @@ -385,7 +385,7 @@ func TestInflationFromXDR(t *testing.T) { } var inf Inflation - err = inf.FromXDR(xdrOp) + err = inf.FromXDR(xdrOp, false) if assert.NoError(t, err) { assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", inf.SourceAccount, "source accounts should match") } @@ -412,7 +412,7 @@ func TestManageDataFromXDR(t *testing.T) { } var md ManageData - err = md.FromXDR(xdrOp) + err = md.FromXDR(xdrOp, false) if assert.NoError(t, err) { assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", md.SourceAccount, "source accounts should match") assert.Equal(t, "data", md.Name, "Name should match") @@ -438,7 +438,7 @@ func TestBumpSequenceFromXDR(t *testing.T) { } var bs BumpSequence - err = bs.FromXDR(xdrOp) + err = bs.FromXDR(xdrOp, false) if assert.NoError(t, err) { assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", bs.SourceAccount, "source accounts should match") assert.Equal(t, int64(45), bs.BumpTo, "BumpTo should match") diff --git a/txnbuild/path_payment.go b/txnbuild/path_payment.go index c60c2c4370..ac19c711d4 100644 --- a/txnbuild/path_payment.go +++ b/txnbuild/path_payment.go @@ -25,7 +25,7 @@ type PathPaymentStrictReceive struct { } // BuildXDR for PathPaymentStrictReceive returns a fully configured XDR Operation. -func (pp *PathPaymentStrictReceive) BuildXDR() (xdr.Operation, error) { +func (pp *PathPaymentStrictReceive) BuildXDR(bool) (xdr.Operation, error) { // Set XDR send asset if pp.SendAsset == nil { return xdr.Operation{}, errors.New("you must specify an asset to send for payment") @@ -93,7 +93,7 @@ func (pp *PathPaymentStrictReceive) BuildXDR() (xdr.Operation, error) { } // FromXDR for PathPaymentStrictReceive initialises the txnbuild struct from the corresponding xdr Operation. -func (pp *PathPaymentStrictReceive) FromXDR(xdrOp xdr.Operation) error { +func (pp *PathPaymentStrictReceive) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetPathPaymentStrictReceiveOp() if !ok { return errors.New("error parsing path_payment operation from xdr") @@ -131,7 +131,7 @@ func (pp *PathPaymentStrictReceive) FromXDR(xdrOp xdr.Operation) error { // Validate for PathPaymentStrictReceive validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (pp *PathPaymentStrictReceive) Validate() error { +func (pp *PathPaymentStrictReceive) Validate(bool) error { _, err := xdr.AddressToAccountId(pp.Destination) if err != nil { return NewValidationError("Destination", err.Error()) diff --git a/txnbuild/path_payment_strict_send.go b/txnbuild/path_payment_strict_send.go index 07eb720c7c..61decaeae0 100644 --- a/txnbuild/path_payment_strict_send.go +++ b/txnbuild/path_payment_strict_send.go @@ -19,7 +19,7 @@ type PathPaymentStrictSend struct { } // BuildXDR for Payment returns a fully configured XDR Operation. -func (pp *PathPaymentStrictSend) BuildXDR() (xdr.Operation, error) { +func (pp *PathPaymentStrictSend) BuildXDR(bool) (xdr.Operation, error) { // Set XDR send asset if pp.SendAsset == nil { return xdr.Operation{}, errors.New("you must specify an asset to send for payment") @@ -87,7 +87,7 @@ func (pp *PathPaymentStrictSend) BuildXDR() (xdr.Operation, error) { } // FromXDR for PathPaymentStrictSend initialises the txnbuild struct from the corresponding xdr Operation. -func (pp *PathPaymentStrictSend) FromXDR(xdrOp xdr.Operation) error { +func (pp *PathPaymentStrictSend) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetPathPaymentStrictSendOp() if !ok { return errors.New("error parsing path_payment operation from xdr") @@ -125,7 +125,7 @@ func (pp *PathPaymentStrictSend) FromXDR(xdrOp xdr.Operation) error { // Validate for PathPaymentStrictSend validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (pp *PathPaymentStrictSend) Validate() error { +func (pp *PathPaymentStrictSend) Validate(bool) error { _, err := xdr.AddressToAccountId(pp.Destination) if err != nil { return NewValidationError("Destination", err.Error()) diff --git a/txnbuild/payment.go b/txnbuild/payment.go index 22356872d6..b6ce54dc2e 100644 --- a/txnbuild/payment.go +++ b/txnbuild/payment.go @@ -16,7 +16,7 @@ type Payment struct { } // BuildXDR for Payment returns a fully configured XDR Operation. -func (p *Payment) BuildXDR() (xdr.Operation, error) { +func (p *Payment) BuildXDR(bool) (xdr.Operation, error) { var destMuxedAccount xdr.MuxedAccount err := destMuxedAccount.SetAddress(p.Destination) @@ -53,7 +53,7 @@ func (p *Payment) BuildXDR() (xdr.Operation, error) { } // FromXDR for Payment initialises the txnbuild struct from the corresponding xdr Operation. -func (p *Payment) FromXDR(xdrOp xdr.Operation) error { +func (p *Payment) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetPaymentOp() if !ok { return errors.New("error parsing payment operation from xdr") @@ -75,7 +75,7 @@ func (p *Payment) FromXDR(xdrOp xdr.Operation) error { // Validate for Payment validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (p *Payment) Validate() error { +func (p *Payment) Validate(bool) error { _, err := xdr.AddressToAccountId(p.Destination) if err != nil { return NewValidationError("Destination", err.Error()) diff --git a/txnbuild/revoke_sponsorship.go b/txnbuild/revoke_sponsorship.go index 293d9112fa..e43b041112 100644 --- a/txnbuild/revoke_sponsorship.go +++ b/txnbuild/revoke_sponsorship.go @@ -55,7 +55,7 @@ type SignerID struct { SignerAddress string } -func (r *RevokeSponsorship) BuildXDR() (xdr.Operation, error) { +func (r *RevokeSponsorship) BuildXDR(bool) (xdr.Operation, error) { xdrOp := xdr.RevokeSponsorshipOp{} switch r.SponsorshipType { case RevokeSponsorshipTypeAccount: @@ -157,7 +157,7 @@ func (r *RevokeSponsorship) BuildXDR() (xdr.Operation, error) { return op, nil } -func (r *RevokeSponsorship) FromXDR(xdrOp xdr.Operation) error { +func (r *RevokeSponsorship) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { r.SourceAccount = accountFromXDR(xdrOp.SourceAccount) op, ok := xdrOp.Body.GetRevokeSponsorshipOp() if !ok { @@ -222,7 +222,7 @@ func (r *RevokeSponsorship) FromXDR(xdrOp xdr.Operation) error { return nil } -func (r *RevokeSponsorship) Validate() error { +func (r *RevokeSponsorship) Validate(bool) error { switch r.SponsorshipType { case RevokeSponsorshipTypeAccount: if r.Account == nil { diff --git a/txnbuild/revoke_sponsorship_test.go b/txnbuild/revoke_sponsorship_test.go index 40e5687d37..2cf0202073 100644 --- a/txnbuild/revoke_sponsorship_test.go +++ b/txnbuild/revoke_sponsorship_test.go @@ -88,15 +88,15 @@ func TestRevokeSponsorship(t *testing.T) { } { t.Run(testcase.name, func(t *testing.T) { op := testcase.op - assert.NoError(t, op.Validate()) - xdrOp, err := op.BuildXDR() + assert.NoError(t, op.Validate(false)) + xdrOp, err := op.BuildXDR(false) assert.NoError(t, err) xdrBin, err := xdrOp.MarshalBinary() assert.NoError(t, err) var xdrOp2 xdr.Operation assert.NoError(t, xdr.SafeUnmarshal(xdrBin, &xdrOp2)) var op2 RevokeSponsorship - assert.NoError(t, op2.FromXDR(xdrOp2)) + assert.NoError(t, op2.FromXDR(xdrOp2, false)) assert.Equal(t, op, op2) testOperationsMarshallingRoundtrip(t, []Operation{&testcase.op}) }) diff --git a/txnbuild/set_options.go b/txnbuild/set_options.go index dcf2c9fdaa..95f948400b 100644 --- a/txnbuild/set_options.go +++ b/txnbuild/set_options.go @@ -67,7 +67,7 @@ type SetOptions struct { } // BuildXDR for SetOptions returns a fully configured XDR Operation. -func (so *SetOptions) BuildXDR() (xdr.Operation, error) { +func (so *SetOptions) BuildXDR(bool) (xdr.Operation, error) { err := so.handleInflation() if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set inflation destination address") @@ -293,7 +293,7 @@ func (so *SetOptions) handleSignerXDR(xSigner *xdr.Signer) { } // FromXDR for SetOptions initialises the txnbuild struct from the corresponding xdr Operation. -func (so *SetOptions) FromXDR(xdrOp xdr.Operation) error { +func (so *SetOptions) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { result, ok := xdrOp.Body.GetSetOptionsOp() if !ok { return errors.New("error parsing set_options operation from xdr") @@ -315,7 +315,7 @@ func (so *SetOptions) FromXDR(xdrOp xdr.Operation) error { // Validate for SetOptions validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (so *SetOptions) Validate() error { +func (so *SetOptions) Validate(bool) error { // skipping checks here because the individual methods above already check for required fields. // Refactoring is out of the scope of this issue(https://github.com/stellar/go/issues/1041) so will leave as is for now. return nil diff --git a/txnbuild/set_options_test.go b/txnbuild/set_options_test.go index 0398f37257..3201065869 100644 --- a/txnbuild/set_options_test.go +++ b/txnbuild/set_options_test.go @@ -131,7 +131,7 @@ func TestEmptyHomeDomainOK(t *testing.T) { options := SetOptions{ HomeDomain: NewHomeDomain(""), } - options.BuildXDR() + options.BuildXDR(false) assert.Equal(t, string(*options.xdrOp.HomeDomain), "", "empty string home domain is set") diff --git a/txnbuild/set_trust_line_flags.go b/txnbuild/set_trust_line_flags.go index 705b6d4310..edc8cda975 100644 --- a/txnbuild/set_trust_line_flags.go +++ b/txnbuild/set_trust_line_flags.go @@ -29,7 +29,7 @@ type SetTrustLineFlags struct { } // BuildXDR for ASetTrustLineFlags returns a fully configured XDR Operation. -func (stf *SetTrustLineFlags) BuildXDR() (xdr.Operation, error) { +func (stf *SetTrustLineFlags) BuildXDR(bool) (xdr.Operation, error) { var xdrOp xdr.SetTrustLineFlagsOp // Set XDR address associated with the trustline @@ -70,7 +70,7 @@ func trustLineFlagsToXDR(flags []TrustLineFlag) xdr.Uint32 { } // FromXDR for SetTrustLineFlags initialises the txnbuild struct from the corresponding xdr Operation. -func (stf *SetTrustLineFlags) FromXDR(xdrOp xdr.Operation) error { +func (stf *SetTrustLineFlags) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { op, ok := xdrOp.Body.GetSetTrustLineFlagsOp() if !ok { return errors.New("error parsing allow_trust operation from xdr") @@ -106,7 +106,7 @@ func fromXDRTrustlineFlag(flags xdr.Uint32) []TrustLineFlag { // Validate for SetTrustLineFlags validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (stf *SetTrustLineFlags) Validate() error { +func (stf *SetTrustLineFlags) Validate(bool) error { err := validateStellarPublicKey(stf.Trustor) if err != nil { return NewValidationError("Trustor", err.Error()) diff --git a/txnbuild/set_trustline_flags_test.go b/txnbuild/set_trustline_flags_test.go index 7990986a2e..516b1a8d2d 100644 --- a/txnbuild/set_trustline_flags_test.go +++ b/txnbuild/set_trustline_flags_test.go @@ -78,15 +78,15 @@ func TestSetTrustLineFlags(t *testing.T) { } { t.Run(testcase.name, func(t *testing.T) { op := testcase.op - assert.NoError(t, op.Validate()) - xdrOp, err := op.BuildXDR() + assert.NoError(t, op.Validate(false)) + xdrOp, err := op.BuildXDR(false) assert.NoError(t, err) xdrBin, err := xdrOp.MarshalBinary() assert.NoError(t, err) var xdrOp2 xdr.Operation assert.NoError(t, xdr.SafeUnmarshal(xdrBin, &xdrOp2)) var op2 SetTrustLineFlags - assert.NoError(t, op2.FromXDR(xdrOp2)) + assert.NoError(t, op2.FromXDR(xdrOp2, false)) assert.Equal(t, op, op2) testOperationsMarshallingRoundtrip(t, []Operation{&testcase.op}) }) diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index fb001f4a2b..cd5ae54729 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -729,7 +729,7 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { } for _, op := range tx.operations { - if verr := op.Validate(); verr != nil { + if verr := op.Validate(false); verr != nil { return nil, errors.Wrap(verr, fmt.Sprintf("validation failed for %T operation", op)) } var ( @@ -739,7 +739,7 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { if params.EnableMuxedAccounts { xdrOperation, err2 = op.BuildXDRWithSEP23() } else { - xdrOperation, err2 = op.BuildXDR() + xdrOperation, err2 = op.BuildXDR(false) } if err2 != nil { return nil, errors.Wrap(err2, fmt.Sprintf("failed to build operation %T", op)) From 0742e88c8882911b28cd3e6579e3c355dc35807e Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Wed, 7 Apr 2021 23:14:31 +0200 Subject: [PATCH 07/22] WIP: port more oeprations to the new interface --- txnbuild/account_merge.go | 2 +- txnbuild/allow_trust.go | 12 ++++--- txnbuild/begin_sponsoring_future_reserves.go | 4 +-- txnbuild/bump_sequence.go | 10 ++++-- txnbuild/change_trust.go | 12 ++++--- txnbuild/create_account.go | 12 ++++--- txnbuild/create_passive_offer.go | 10 ++++-- txnbuild/inflation.go | 10 ++++-- txnbuild/manage_buy_offer.go | 10 ++++-- txnbuild/manage_data.go | 10 ++++-- txnbuild/manage_offer.go | 14 +++++--- txnbuild/operation.go | 2 +- txnbuild/path_payment.go | 32 +++++++++++++---- txnbuild/path_payment_strict_send.go | 33 ++++++++++++----- txnbuild/payment.go | 38 +++++++++++++++----- txnbuild/set_options.go | 10 ++++-- txnbuild/transaction.go | 10 +----- 17 files changed, 161 insertions(+), 70 deletions(-) diff --git a/txnbuild/account_merge.go b/txnbuild/account_merge.go index 58c9a516ea..7fb7871002 100644 --- a/txnbuild/account_merge.go +++ b/txnbuild/account_merge.go @@ -32,7 +32,7 @@ func (am *AccountMerge) BuildXDR(withSEP23 bool) (xdr.Operation, error) { } op := xdr.Operation{Body: body} if withSEP23 { - SetOpMuxedSourceAccount(&op, am.SourceAccount) + SetOpSourceMuxedAccount(&op, am.SourceAccount) } else { SetOpSourceAccount(&op, am.SourceAccount) } diff --git a/txnbuild/allow_trust.go b/txnbuild/allow_trust.go index ce67cb99d3..f616a585ca 100644 --- a/txnbuild/allow_trust.go +++ b/txnbuild/allow_trust.go @@ -21,7 +21,7 @@ type AllowTrust struct { } // BuildXDR for AllowTrust returns a fully configured XDR Operation. -func (at *AllowTrust) BuildXDR(bool) (xdr.Operation, error) { +func (at *AllowTrust) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { var xdrOp xdr.AllowTrustOp // Set XDR address associated with the trustline @@ -56,7 +56,11 @@ func (at *AllowTrust) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, at.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, at.SourceAccount) + } else { + SetOpSourceAccount(&op, at.SourceAccount) + } return op, nil } @@ -81,7 +85,7 @@ func (at *AllowTrust) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error return errors.New("error parsing allow_trust operation from xdr") } - at.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + at.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) at.Trustor = result.Trustor.Address() flag := xdr.TrustLineFlags(result.Authorize) at.Authorize = flag.IsAuthorized() @@ -97,7 +101,7 @@ func (at *AllowTrust) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error // Validate for AllowTrust validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (at *AllowTrust) Validate(bool) error { +func (at *AllowTrust) Validate(withMuxedAccounts bool) error { err := validateStellarPublicKey(at.Trustor) if err != nil { return NewValidationError("Trustor", err.Error()) diff --git a/txnbuild/begin_sponsoring_future_reserves.go b/txnbuild/begin_sponsoring_future_reserves.go index 5d08361f6d..c5ad55f0ae 100644 --- a/txnbuild/begin_sponsoring_future_reserves.go +++ b/txnbuild/begin_sponsoring_future_reserves.go @@ -15,7 +15,7 @@ type BeginSponsoringFutureReserves struct { } // BuildXDR for BeginSponsoringFutureReserves returns a fully configured XDR Operation. -func (bs *BeginSponsoringFutureReserves) BuildXDR(bool) (xdr.Operation, error) { +func (bs *BeginSponsoringFutureReserves) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { xdrOp := xdr.BeginSponsoringFutureReservesOp{} err := xdrOp.SponsoredId.SetAddress(bs.SponsoredID) if err != nil { @@ -37,7 +37,7 @@ func (bs *BeginSponsoringFutureReserves) FromXDR(xdrOp xdr.Operation, withMuxedA if !ok { return errors.New("error parsing begin_sponsoring_future_reserves operation from xdr") } - bs.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + bs.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) bs.SponsoredID = result.SponsoredId.Address() return nil diff --git a/txnbuild/bump_sequence.go b/txnbuild/bump_sequence.go index 64943f9bef..e9f213ca7b 100644 --- a/txnbuild/bump_sequence.go +++ b/txnbuild/bump_sequence.go @@ -13,7 +13,7 @@ type BumpSequence struct { } // BuildXDR for BumpSequence returns a fully configured XDR Operation. -func (bs *BumpSequence) BuildXDR(bool) (xdr.Operation, error) { +func (bs *BumpSequence) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { opType := xdr.OperationTypeBumpSequence xdrOp := xdr.BumpSequenceOp{BumpTo: xdr.SequenceNumber(bs.BumpTo)} body, err := xdr.NewOperationBody(opType, xdrOp) @@ -21,7 +21,11 @@ func (bs *BumpSequence) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, bs.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, bs.SourceAccount) + } else { + SetOpSourceAccount(&op, bs.SourceAccount) + } return op, nil } @@ -32,7 +36,7 @@ func (bs *BumpSequence) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) err return errors.New("error parsing bump_sequence operation from xdr") } - bs.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + bs.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) bs.BumpTo = int64(result.BumpTo) return nil } diff --git a/txnbuild/change_trust.go b/txnbuild/change_trust.go index bdd4897cf5..d276d302c1 100644 --- a/txnbuild/change_trust.go +++ b/txnbuild/change_trust.go @@ -30,7 +30,7 @@ func RemoveTrustlineOp(issuedAsset Asset) ChangeTrust { } // BuildXDR for ChangeTrust returns a fully configured XDR Operation. -func (ct *ChangeTrust) BuildXDR(bool) (xdr.Operation, error) { +func (ct *ChangeTrust) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { if ct.Line.IsNative() { return xdr.Operation{}, errors.New("trustline cannot be extended to a native (XLM) asset") } @@ -58,7 +58,11 @@ func (ct *ChangeTrust) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, ct.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, ct.SourceAccount) + } else { + SetOpSourceAccount(&op, ct.SourceAccount) + } return op, nil } @@ -69,7 +73,7 @@ func (ct *ChangeTrust) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) erro return errors.New("error parsing change_trust operation from xdr") } - ct.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + ct.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) ct.Limit = amount.String(result.Limit) asset, err := assetFromXDR(result.Line) if err != nil { @@ -81,7 +85,7 @@ func (ct *ChangeTrust) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) erro // Validate for ChangeTrust validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (ct *ChangeTrust) Validate(bool) error { +func (ct *ChangeTrust) Validate(withMuxedAccounts bool) error { // only validate limit if it has a value. Empty limit is set to the max trustline limit. if ct.Limit != "" { err := validateAmount(ct.Limit) diff --git a/txnbuild/create_account.go b/txnbuild/create_account.go index 04aed3b48b..abbfdacfa3 100644 --- a/txnbuild/create_account.go +++ b/txnbuild/create_account.go @@ -15,7 +15,7 @@ type CreateAccount struct { } // BuildXDR for CreateAccount returns a fully configured XDR Operation. -func (ca *CreateAccount) BuildXDR(bool) (xdr.Operation, error) { +func (ca *CreateAccount) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { var xdrOp xdr.CreateAccountOp err := xdrOp.Destination.SetAddress(ca.Destination) @@ -34,7 +34,11 @@ func (ca *CreateAccount) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, ca.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, ca.SourceAccount) + } else { + SetOpSourceAccount(&op, ca.SourceAccount) + } return op, nil } @@ -45,7 +49,7 @@ func (ca *CreateAccount) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) er return errors.New("error parsing create_account operation from xdr") } - ca.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + ca.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) ca.Destination = result.Destination.Address() ca.Amount = amount.String(result.StartingBalance) @@ -54,7 +58,7 @@ func (ca *CreateAccount) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) er // Validate for CreateAccount validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (ca *CreateAccount) Validate(bool) error { +func (ca *CreateAccount) Validate(withMuxedAccounts bool) error { err := validateStellarPublicKey(ca.Destination) if err != nil { return NewValidationError("Destination", err.Error()) diff --git a/txnbuild/create_passive_offer.go b/txnbuild/create_passive_offer.go index c96e800c71..6eb3cdd325 100644 --- a/txnbuild/create_passive_offer.go +++ b/txnbuild/create_passive_offer.go @@ -18,7 +18,7 @@ type CreatePassiveSellOffer struct { } // BuildXDR for CreatePassiveSellOffer returns a fully configured XDR Operation. -func (cpo *CreatePassiveSellOffer) BuildXDR(bool) (xdr.Operation, error) { +func (cpo *CreatePassiveSellOffer) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { xdrSelling, err := cpo.Selling.ToXDR() if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Selling' field") @@ -51,7 +51,11 @@ func (cpo *CreatePassiveSellOffer) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, cpo.SourceAccount) + if withMuxedAccounts { + SetOpSourceAccount(&op, cpo.SourceAccount) + } else { + SetOpSourceMuxedAccount(&op, cpo.SourceAccount) + } return op, nil } @@ -62,7 +66,7 @@ func (cpo *CreatePassiveSellOffer) FromXDR(xdrOp xdr.Operation, withMuxedAccount return errors.New("error parsing create_passive_sell_offer operation from xdr") } - cpo.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + cpo.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) cpo.Amount = amount.String(result.Amount) if result.Price != (xdr.Price{}) { cpo.price.fromXDR(result.Price) diff --git a/txnbuild/inflation.go b/txnbuild/inflation.go index d1fcbe8737..ec80299462 100644 --- a/txnbuild/inflation.go +++ b/txnbuild/inflation.go @@ -12,14 +12,18 @@ type Inflation struct { } // BuildXDR for Inflation returns a fully configured XDR Operation. -func (inf *Inflation) BuildXDR(bool) (xdr.Operation, error) { +func (inf *Inflation) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { opType := xdr.OperationTypeInflation body, err := xdr.NewOperationBody(opType, nil) if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, inf.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, inf.SourceAccount) + } else { + SetOpSourceAccount(&op, inf.SourceAccount) + } return op, nil } @@ -28,7 +32,7 @@ func (inf *Inflation) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error if xdrOp.Body.Type != xdr.OperationTypeInflation { return errors.New("error parsing inflation operation from xdr") } - inf.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + inf.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) return nil } diff --git a/txnbuild/manage_buy_offer.go b/txnbuild/manage_buy_offer.go index c56071da44..9360cf1af7 100644 --- a/txnbuild/manage_buy_offer.go +++ b/txnbuild/manage_buy_offer.go @@ -19,7 +19,7 @@ type ManageBuyOffer struct { } // BuildXDR for ManageBuyOffer returns a fully configured XDR Operation. -func (mo *ManageBuyOffer) BuildXDR(bool) (xdr.Operation, error) { +func (mo *ManageBuyOffer) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { xdrSelling, err := mo.Selling.ToXDR() if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Selling' field") @@ -53,7 +53,11 @@ func (mo *ManageBuyOffer) BuildXDR(bool) (xdr.Operation, error) { } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, mo.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, mo.SourceAccount) + } else { + SetOpSourceAccount(&op, mo.SourceAccount) + } return op, nil } @@ -64,7 +68,7 @@ func (mo *ManageBuyOffer) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) e return errors.New("error parsing manage_buy_offer operation from xdr") } - mo.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + mo.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) mo.OfferID = int64(result.OfferId) mo.Amount = amount.String(result.BuyAmount) if result.Price != (xdr.Price{}) { diff --git a/txnbuild/manage_data.go b/txnbuild/manage_data.go index d694e50c0b..b26a9b3fe7 100644 --- a/txnbuild/manage_data.go +++ b/txnbuild/manage_data.go @@ -14,7 +14,7 @@ type ManageData struct { } // BuildXDR for ManageData returns a fully configured XDR Operation. -func (md *ManageData) BuildXDR(bool) (xdr.Operation, error) { +func (md *ManageData) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { xdrOp := xdr.ManageDataOp{DataName: xdr.String64(md.Name)} // No data value clears the named data entry on the account @@ -31,7 +31,11 @@ func (md *ManageData) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, md.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, md.SourceAccount) + } else { + SetOpSourceAccount(&op, md.SourceAccount) + } return op, nil } @@ -42,7 +46,7 @@ func (md *ManageData) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error return errors.New("error parsing create_account operation from xdr") } - md.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + md.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) md.Name = string(result.DataName) if result.DataValue != nil { md.Value = *result.DataValue diff --git a/txnbuild/manage_offer.go b/txnbuild/manage_offer.go index 412e8e0630..fd292f05bc 100644 --- a/txnbuild/manage_offer.go +++ b/txnbuild/manage_offer.go @@ -6,7 +6,7 @@ import ( "github.com/stellar/go/xdr" ) -//CreateOfferOp returns a ManageSellOffer operation to create a new offer, by +// CreateOfferOp returns a ManageSellOffer operation to create a new offer, by // setting the OfferID to "0". The sourceAccount is optional, and if not provided, // will be that of the surrounding transaction. func CreateOfferOp(selling, buying Asset, amount, price string, sourceAccount ...string) (ManageSellOffer, error) { @@ -46,7 +46,7 @@ func UpdateOfferOp(selling, buying Asset, amount, price string, offerID int64, s return offer, nil } -//DeleteOfferOp returns a ManageSellOffer operation to delete an offer, by +// DeleteOfferOp returns a ManageSellOffer operation to delete an offer, by // setting the Amount to "0". The sourceAccount is optional, and if not provided, // will be that of the surrounding transaction. func DeleteOfferOp(offerID int64, sourceAccount ...string) (ManageSellOffer, error) { @@ -83,7 +83,7 @@ type ManageSellOffer struct { } // BuildXDR for ManageSellOffer returns a fully configured XDR Operation. -func (mo *ManageSellOffer) BuildXDR(bool) (xdr.Operation, error) { +func (mo *ManageSellOffer) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { xdrSelling, err := mo.Selling.ToXDR() if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Selling' field") @@ -117,7 +117,11 @@ func (mo *ManageSellOffer) BuildXDR(bool) (xdr.Operation, error) { } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, mo.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, mo.SourceAccount) + } else { + SetOpSourceAccount(&op, mo.SourceAccount) + } return op, nil } @@ -128,7 +132,7 @@ func (mo *ManageSellOffer) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) return errors.New("error parsing manage_sell_offer operation from xdr") } - mo.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + mo.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) mo.OfferID = int64(result.OfferId) mo.Amount = amount.String(result.Amount) if result.Price != (xdr.Price{}) { diff --git a/txnbuild/operation.go b/txnbuild/operation.go index 54e20534ee..31ee1f5cb9 100644 --- a/txnbuild/operation.go +++ b/txnbuild/operation.go @@ -25,7 +25,7 @@ func SetOpSourceAccount(op *xdr.Operation, sourceAccount string) { } // SetOpSourceAccount sets the source account ID on an Operation, allowing M-strkeys (as defined in SEP23). -func SetOpMuxedSourceAccount(op *xdr.Operation, sourceAccount string) { +func SetOpSourceMuxedAccount(op *xdr.Operation, sourceAccount string) { if sourceAccount == "" { return } diff --git a/txnbuild/path_payment.go b/txnbuild/path_payment.go index ac19c711d4..a18d3c76bc 100644 --- a/txnbuild/path_payment.go +++ b/txnbuild/path_payment.go @@ -25,7 +25,7 @@ type PathPaymentStrictReceive struct { } // BuildXDR for PathPaymentStrictReceive returns a fully configured XDR Operation. -func (pp *PathPaymentStrictReceive) BuildXDR(bool) (xdr.Operation, error) { +func (pp *PathPaymentStrictReceive) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { // Set XDR send asset if pp.SendAsset == nil { return xdr.Operation{}, errors.New("you must specify an asset to send for payment") @@ -43,7 +43,11 @@ func (pp *PathPaymentStrictReceive) BuildXDR(bool) (xdr.Operation, error) { // Set XDR destination var xdrDestination xdr.MuxedAccount - err = xdrDestination.SetAddress(pp.Destination) + if withMuxedAccounts { + err = xdrDestination.SetAddressWithSEP23(pp.Destination) + } else { + err = xdrDestination.SetAddress(pp.Destination) + } if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") } @@ -88,7 +92,11 @@ func (pp *PathPaymentStrictReceive) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, pp.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, pp.SourceAccount) + } else { + SetOpSourceAccount(&op, pp.SourceAccount) + } return op, nil } @@ -99,7 +107,13 @@ func (pp *PathPaymentStrictReceive) FromXDR(xdrOp xdr.Operation, withMuxedAccoun return errors.New("error parsing path_payment operation from xdr") } - pp.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + pp.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) + if withMuxedAccounts { + pp.Destination = xdrOp.Body.Destination.SEP23Address() + } else { + destAID := result.Destination.ToAccountId() + pp.Destination = destAID.Address() + } destAID := result.Destination.ToAccountId() pp.Destination = destAID.Address() pp.DestAmount = amount.String(result.DestAmount) @@ -131,8 +145,14 @@ func (pp *PathPaymentStrictReceive) FromXDR(xdrOp xdr.Operation, withMuxedAccoun // Validate for PathPaymentStrictReceive validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (pp *PathPaymentStrictReceive) Validate(bool) error { - _, err := xdr.AddressToAccountId(pp.Destination) +func (pp *PathPaymentStrictReceive) Validate(withMuxedAccounts bool) error { + var err error + if withMuxedAccounts { + _, err = xdr.AddressToAccountId(pp.Destination) + } else { + _, err = xdr.SEP23AddressToMuxedAccount(pp.Destination) + } + if err != nil { return NewValidationError("Destination", err.Error()) } diff --git a/txnbuild/path_payment_strict_send.go b/txnbuild/path_payment_strict_send.go index 61decaeae0..1e2787f827 100644 --- a/txnbuild/path_payment_strict_send.go +++ b/txnbuild/path_payment_strict_send.go @@ -19,7 +19,7 @@ type PathPaymentStrictSend struct { } // BuildXDR for Payment returns a fully configured XDR Operation. -func (pp *PathPaymentStrictSend) BuildXDR(bool) (xdr.Operation, error) { +func (pp *PathPaymentStrictSend) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { // Set XDR send asset if pp.SendAsset == nil { return xdr.Operation{}, errors.New("you must specify an asset to send for payment") @@ -37,7 +37,11 @@ func (pp *PathPaymentStrictSend) BuildXDR(bool) (xdr.Operation, error) { // Set XDR destination var xdrDestination xdr.MuxedAccount - err = xdrDestination.SetAddress(pp.Destination) + if withMuxedAccounts { + err = xdrDestination.SetAddressWithSEP23(pp.Destination) + } else { + err = xdrDestination.SetAddress(pp.Destination) + } if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") } @@ -82,7 +86,11 @@ func (pp *PathPaymentStrictSend) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, pp.SourceAccount) + if withMuxedAccounts { + SetOpSourceAccount(&op, pp.SourceAccount) + } else { + SetOpSourceMuxedAccount(&op, pp.SourceAccount) + } return op, nil } @@ -93,9 +101,13 @@ func (pp *PathPaymentStrictSend) FromXDR(xdrOp xdr.Operation, withMuxedAccounts return errors.New("error parsing path_payment operation from xdr") } - pp.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - destAID := result.Destination.ToAccountId() - pp.Destination = destAID.Address() + pp.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) + if withMuxedAccounts { + destAID := result.Destination.ToAccountId() + pp.Destination = destAID.Address() + } else { + pp.Destination = result.Destination.SEP23Address() + } pp.SendAmount = amount.String(result.SendAmount) pp.DestMin = amount.String(result.DestMin) @@ -125,8 +137,13 @@ func (pp *PathPaymentStrictSend) FromXDR(xdrOp xdr.Operation, withMuxedAccounts // Validate for PathPaymentStrictSend validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (pp *PathPaymentStrictSend) Validate(bool) error { - _, err := xdr.AddressToAccountId(pp.Destination) +func (pp *PathPaymentStrictSend) Validate(withMuxedAccounts bool) error { + var err error + if withMuxedAccounts { + _, err = xdr.AddressToAccountId(pp.Destination) + } else { + _, err = xdr.SEP23AddressToMuxedAccount(pp.Destination) + } if err != nil { return NewValidationError("Destination", err.Error()) } diff --git a/txnbuild/payment.go b/txnbuild/payment.go index b6ce54dc2e..2858a6d998 100644 --- a/txnbuild/payment.go +++ b/txnbuild/payment.go @@ -16,10 +16,16 @@ type Payment struct { } // BuildXDR for Payment returns a fully configured XDR Operation. -func (p *Payment) BuildXDR(bool) (xdr.Operation, error) { + +func (p *Payment) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { var destMuxedAccount xdr.MuxedAccount - err := destMuxedAccount.SetAddress(p.Destination) + var err error + if withMuxedAccounts { + err = destMuxedAccount.SetAddressWithSEP23(p.Destination) + } else { + err = destMuxedAccount.SetAddress(p.Destination) + } if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") } @@ -48,7 +54,11 @@ func (p *Payment) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR Operation") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, p.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, p.SourceAccount) + } else { + SetOpSourceAccount(&op, p.SourceAccount) + } return op, nil } @@ -59,9 +69,15 @@ func (p *Payment) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { return errors.New("error parsing payment operation from xdr") } - p.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - destAID := result.Destination.ToAccountId() - p.Destination = destAID.Address() + p.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) + if withMuxedAccounts { + p.Destination = xdrOp.Body.Destination.SEP23Address() + } else { + destAID := result.Destination.ToAccountId() + p.Destination = destAID.Address() + p.Destination = destAID.Address() + } + p.Amount = amount.String(result.Amount) asset, err := assetFromXDR(result.Asset) @@ -75,8 +91,14 @@ func (p *Payment) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { // Validate for Payment validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (p *Payment) Validate(bool) error { - _, err := xdr.AddressToAccountId(p.Destination) +func (p *Payment) Validate(withMuxedAccounts bool) error { + var err error + if withMuxedAccounts { + _, err = xdr.AddressToAccountId(p.Destination) + } else { + _, err = xdr.SEP23AddressToMuxedAccount(p.Destination) + } + if err != nil { return NewValidationError("Destination", err.Error()) } diff --git a/txnbuild/set_options.go b/txnbuild/set_options.go index 95f948400b..e2e299c4b7 100644 --- a/txnbuild/set_options.go +++ b/txnbuild/set_options.go @@ -67,7 +67,7 @@ type SetOptions struct { } // BuildXDR for SetOptions returns a fully configured XDR Operation. -func (so *SetOptions) BuildXDR(bool) (xdr.Operation, error) { +func (so *SetOptions) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { err := so.handleInflation() if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set inflation destination address") @@ -95,7 +95,11 @@ func (so *SetOptions) BuildXDR(bool) (xdr.Operation, error) { } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, so.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, so.SourceAccount) + } else { + SetOpSourceAccount(&op, so.SourceAccount) + } return op, nil } @@ -299,7 +303,7 @@ func (so *SetOptions) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error return errors.New("error parsing set_options operation from xdr") } - so.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + so.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) so.handleInflationXDR(result.InflationDest) so.handleClearFlagsXDR(result.ClearFlags) so.handleSetFlagsXDR(result.SetFlags) diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index cd5ae54729..42bb5d6796 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -732,15 +732,7 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { if verr := op.Validate(false); verr != nil { return nil, errors.Wrap(verr, fmt.Sprintf("validation failed for %T operation", op)) } - var ( - err2 error - xdrOperation xdr.Operation - ) - if params.EnableMuxedAccounts { - xdrOperation, err2 = op.BuildXDRWithSEP23() - } else { - xdrOperation, err2 = op.BuildXDR(false) - } + xdrOperation, err2 := op.BuildXDR(params.EnableMuxedAccounts) if err2 != nil { return nil, errors.Wrap(err2, fmt.Sprintf("failed to build operation %T", op)) } From 35338293dddcbb360a13357cf24ae8eb7466f9ba Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Thu, 8 Apr 2021 00:28:05 +0200 Subject: [PATCH 08/22] Update all the remaining operations --- txnbuild/account_merge_test.go | 2 +- txnbuild/claim_claimable_balance.go | 10 +++++--- txnbuild/clawback.go | 29 ++++++++++++++++------ txnbuild/clawback_claimable_balance.go | 10 +++++--- txnbuild/create_claimable_balance.go | 10 +++++--- txnbuild/end_sponsoring_future_reserves.go | 10 +++++--- txnbuild/operation_test.go | 2 +- txnbuild/revoke_sponsorship.go | 10 +++++--- txnbuild/set_trust_line_flags.go | 10 +++++--- 9 files changed, 65 insertions(+), 28 deletions(-) diff --git a/txnbuild/account_merge_test.go b/txnbuild/account_merge_test.go index ebf002526d..e2bf33d262 100644 --- a/txnbuild/account_merge_test.go +++ b/txnbuild/account_merge_test.go @@ -23,7 +23,7 @@ func TestAccountMergeValidate(t *testing.T) { }, ) if assert.Error(t, err) { - expected := "strkey is 4 bytes long; minimum valid length is 5" + expected := "invalid address" assert.Contains(t, err.Error(), expected) } } diff --git a/txnbuild/claim_claimable_balance.go b/txnbuild/claim_claimable_balance.go index f1b8f9abe2..96ae94e1d3 100644 --- a/txnbuild/claim_claimable_balance.go +++ b/txnbuild/claim_claimable_balance.go @@ -15,7 +15,7 @@ type ClaimClaimableBalance struct { } // BuildXDR for ClaimClaimableBalance returns a fully configured XDR Operation. -func (cb *ClaimClaimableBalance) BuildXDR(bool) (xdr.Operation, error) { +func (cb *ClaimClaimableBalance) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { var xdrBalanceID xdr.ClaimableBalanceId err := xdr.SafeUnmarshalHex(cb.BalanceID, &xdrBalanceID) if err != nil { @@ -31,7 +31,11 @@ func (cb *ClaimClaimableBalance) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, cb.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, cb.SourceAccount) + } else { + SetOpSourceAccount(&op, cb.SourceAccount) + } return op, nil } @@ -42,7 +46,7 @@ func (cb *ClaimClaimableBalance) FromXDR(xdrOp xdr.Operation, withMuxedAccounts return errors.New("error parsing claim_claimable_balance operation from xdr") } - cb.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + cb.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) balanceID, err := xdr.MarshalHex(result.BalanceId) if err != nil { return errors.New("error parsing BalanceID in claim_claimable_balance operation from xdr") diff --git a/txnbuild/clawback.go b/txnbuild/clawback.go index 957b709610..bce93bee30 100644 --- a/txnbuild/clawback.go +++ b/txnbuild/clawback.go @@ -17,10 +17,14 @@ type Clawback struct { } // BuildXDR for Clawback returns a fully configured XDR Operation. -func (cb *Clawback) BuildXDR(bool) (xdr.Operation, error) { +func (cb *Clawback) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { var fromMuxedAccount xdr.MuxedAccount - - err := fromMuxedAccount.SetAddress(cb.From) + var err error + if withMuxedAccounts { + err = fromMuxedAccount.SetAddressWithSEP23(cb.From) + } else { + err = fromMuxedAccount.SetAddress(cb.From) + } if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set from address") } @@ -65,9 +69,13 @@ func (cb *Clawback) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { return errors.New("error parsing clawback operation from xdr") } - cb.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - fromAID := result.From.ToAccountId() - cb.From = fromAID.Address() + cb.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) + if withMuxedAccounts { + cb.From = result.From.SEP23Address() + } else { + fromAID := result.From.ToAccountId() + cb.From = fromAID.Address() + } cb.Amount = amount.String(result.Amount) asset, err := assetFromXDR(result.Asset) if err != nil { @@ -80,8 +88,13 @@ func (cb *Clawback) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { // Validate for Clawback validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (cb *Clawback) Validate(bool) error { - _, err := xdr.AddressToAccountId(cb.From) +func (cb *Clawback) Validate(withMuxedAccounts bool) error { + var err error + if withMuxedAccounts { + _, err = xdr.SEP23AddressToMuxedAccount(cb.From) + } else { + _, err = xdr.AddressToAccountId(cb.From) + } if err != nil { return NewValidationError("From", err.Error()) } diff --git a/txnbuild/clawback_claimable_balance.go b/txnbuild/clawback_claimable_balance.go index af9d38f09a..6d6ee85f14 100644 --- a/txnbuild/clawback_claimable_balance.go +++ b/txnbuild/clawback_claimable_balance.go @@ -14,7 +14,7 @@ type ClawbackClaimableBalance struct { } // BuildXDR for ClawbackClaimableBalance returns a fully configured XDR Operation. -func (cb *ClawbackClaimableBalance) BuildXDR(bool) (xdr.Operation, error) { +func (cb *ClawbackClaimableBalance) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { var xdrBalanceID xdr.ClaimableBalanceId err := xdr.SafeUnmarshalHex(cb.BalanceID, &xdrBalanceID) if err != nil { @@ -30,7 +30,11 @@ func (cb *ClawbackClaimableBalance) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, cb.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, cb.SourceAccount) + } else { + SetOpSourceAccount(&op, cb.SourceAccount) + } return op, nil } @@ -41,7 +45,7 @@ func (cb *ClawbackClaimableBalance) FromXDR(xdrOp xdr.Operation, withMuxedAccoun return errors.New("error parsing clawback_claimable_balance operation from xdr") } - cb.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + cb.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) balanceID, err := xdr.MarshalHex(result.BalanceId) if err != nil { return errors.New("error parsing BalanceID in claim_claimable_balance operation from xdr") diff --git a/txnbuild/create_claimable_balance.go b/txnbuild/create_claimable_balance.go index cb89fc34c2..6513b28c09 100644 --- a/txnbuild/create_claimable_balance.go +++ b/txnbuild/create_claimable_balance.go @@ -96,7 +96,7 @@ func BeforeRelativeTimePredicate(secondsBefore int64) xdr.ClaimPredicate { } // BuildXDR for CreateClaimableBalance returns a fully configured XDR Operation. -func (cb *CreateClaimableBalance) BuildXDR(bool) (xdr.Operation, error) { +func (cb *CreateClaimableBalance) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { xdrAsset, err := cb.Asset.ToXDR() if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Asset' field") @@ -134,7 +134,11 @@ func (cb *CreateClaimableBalance) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, cb.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, cb.SourceAccount) + } else { + SetOpSourceAccount(&op, cb.SourceAccount) + } return op, nil } @@ -145,7 +149,7 @@ func (cb *CreateClaimableBalance) FromXDR(xdrOp xdr.Operation, withMuxedAccounts return errors.New("error parsing create_claimable_balance operation from xdr") } - cb.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + cb.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) for _, c := range result.Claimants { claimant := c.MustV0() cb.Destinations = append(cb.Destinations, Claimant{ diff --git a/txnbuild/end_sponsoring_future_reserves.go b/txnbuild/end_sponsoring_future_reserves.go index 425ece6fe1..9bb2f2c9dd 100644 --- a/txnbuild/end_sponsoring_future_reserves.go +++ b/txnbuild/end_sponsoring_future_reserves.go @@ -14,14 +14,18 @@ type EndSponsoringFutureReserves struct { } // BuildXDR for EndSponsoringFutureReserves returns a fully configured XDR Operation. -func (es *EndSponsoringFutureReserves) BuildXDR(bool) (xdr.Operation, error) { +func (es *EndSponsoringFutureReserves) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { opType := xdr.OperationTypeEndSponsoringFutureReserves body, err := xdr.NewOperationBody(opType, nil) if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, es.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, es.SourceAccount) + } else { + SetOpSourceAccount(&op, es.SourceAccount) + } return op, nil } @@ -31,7 +35,7 @@ func (es *EndSponsoringFutureReserves) FromXDR(xdrOp xdr.Operation, withMuxedAcc return errors.New("error parsing end_sponsoring_future_reserves operation from xdr") } - es.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + es.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) return nil } diff --git a/txnbuild/operation_test.go b/txnbuild/operation_test.go index 62eaadc2e9..4720b4a74d 100644 --- a/txnbuild/operation_test.go +++ b/txnbuild/operation_test.go @@ -367,7 +367,7 @@ func TestAccountMergeFromXDR(t *testing.T) { } var am AccountMerge - err = am.FromXDR(xdrOp) + err = am.FromXDR(xdrOp, false) if assert.NoError(t, err) { assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", am.SourceAccount, "source accounts should match") assert.Equal(t, "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", am.Destination, "destination accounts should match") diff --git a/txnbuild/revoke_sponsorship.go b/txnbuild/revoke_sponsorship.go index e43b041112..0481b24d6d 100644 --- a/txnbuild/revoke_sponsorship.go +++ b/txnbuild/revoke_sponsorship.go @@ -55,7 +55,7 @@ type SignerID struct { SignerAddress string } -func (r *RevokeSponsorship) BuildXDR(bool) (xdr.Operation, error) { +func (r *RevokeSponsorship) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { xdrOp := xdr.RevokeSponsorshipOp{} switch r.SponsorshipType { case RevokeSponsorshipTypeAccount: @@ -153,12 +153,16 @@ func (r *RevokeSponsorship) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, r.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, r.SourceAccount) + } else { + SetOpSourceAccount(&op, r.SourceAccount) + } return op, nil } func (r *RevokeSponsorship) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { - r.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + r.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) op, ok := xdrOp.Body.GetRevokeSponsorshipOp() if !ok { return errors.New("error parsing revoke_sponsorhip operation from xdr") diff --git a/txnbuild/set_trust_line_flags.go b/txnbuild/set_trust_line_flags.go index edc8cda975..0ac412ac67 100644 --- a/txnbuild/set_trust_line_flags.go +++ b/txnbuild/set_trust_line_flags.go @@ -29,7 +29,7 @@ type SetTrustLineFlags struct { } // BuildXDR for ASetTrustLineFlags returns a fully configured XDR Operation. -func (stf *SetTrustLineFlags) BuildXDR(bool) (xdr.Operation, error) { +func (stf *SetTrustLineFlags) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { var xdrOp xdr.SetTrustLineFlagsOp // Set XDR address associated with the trustline @@ -57,7 +57,11 @@ func (stf *SetTrustLineFlags) BuildXDR(bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, stf.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, stf.SourceAccount) + } else { + SetOpSourceAccount(&op, stf.SourceAccount) + } return op, nil } @@ -76,7 +80,7 @@ func (stf *SetTrustLineFlags) FromXDR(xdrOp xdr.Operation, withMuxedAccounts boo return errors.New("error parsing allow_trust operation from xdr") } - stf.SourceAccount = accountFromXDR(xdrOp.SourceAccount) + stf.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) stf.Trustor = op.Trustor.Address() asset, err := assetFromXDR(op.Asset) if err != nil { From d8e1e2ab58527ca8a17ac8a14f44153bd82b99a7 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Thu, 8 Apr 2021 00:32:58 +0200 Subject: [PATCH 09/22] Appease go vet --- txnbuild/transaction.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index 42bb5d6796..b4ecd44ec7 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -667,12 +667,12 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { } var sourceAccount xdr.MuxedAccount if params.EnableMuxedAccounts { - if err := sourceAccount.SetAddressWithSEP23(tx.sourceAccount.AccountID); err != nil { + if err = sourceAccount.SetAddressWithSEP23(tx.sourceAccount.AccountID); err != nil { return nil, errors.Wrap(err, "account id is not valid") } } else { - accountID, err := xdr.AddressToAccountId(tx.sourceAccount.AccountID) - if err != nil { + accountID, err2 := xdr.AddressToAccountId(tx.sourceAccount.AccountID) + if err2 != nil { return nil, errors.Wrap(err, "account id is not valid") } sourceAccount = accountID.ToMuxedAccount() From 29bf10c3b9caa9341d9017e62d16773947627e5d Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Thu, 8 Apr 2021 00:46:25 +0200 Subject: [PATCH 10/22] Add CHANGELOG entry --- txnbuild/CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/txnbuild/CHANGELOG.md b/txnbuild/CHANGELOG.md index f2ae1ef1d0..1740ce0fa9 100644 --- a/txnbuild/CHANGELOG.md +++ b/txnbuild/CHANGELOG.md @@ -8,11 +8,16 @@ file. This project adheres to [Semantic Versioning](http://semver.org/). ### Breaking changes * `AllowTrustOpAsset` was renamed to `AssetCode`, `{Must}NewAllowTrustAsset` was renamed to `{Must}NewAssetCodeFromString`. +* Some methods from the `Operation` interface (`BuildXDR()`,`FromXDR()` and `Validate()`) now take an additional `bool` parameter (`withMuxedAccounts`) + to indicate whether [SEP23](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0023.md) M-strkeys should be enabled. ### New features -Add support for Stellar Protocol 16 (CAP35): `Clawback` operations. - +* Add support for Stellar Protocol 16 (CAP35): `Clawback`, `ClawbackClaimableBalance` and `SetTrustlineFlags` operations. +* Add opt-in support for [SEP23](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0023.md) M-strkeys for `MuxedAccount`s: + * Some methods from the `Operation` interface (`BuildXDR()`,`FromXDR()` and `Validate()`) now take an additional `bool` parameter (`withMuxedAccounts`) + * The parameters from `NewFeeBumpTransaction()` and `NewTransaction()` now include a new field (`EnableMuxedAccounts`) to enable M-strekeys. + * `TransactionFromXDR()` now allows passing a `ParseXDROptionEnableMuxedAccounts` option, to enable M-strkey parsing. ## [v6.0.0](https://github.com/stellar/go/releases/tag/horizonclient-v6.0.0) - 2021-02-22 ### Breaking changes From 828be1061e7dbc34c0243420c2e059ba48bf6206 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Thu, 8 Apr 2021 01:58:54 +0200 Subject: [PATCH 11/22] Update client memo check to support Muxed accounts --- clients/horizonclient/client.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/clients/horizonclient/client.go b/clients/horizonclient/client.go index 36016506d3..063efdd512 100644 --- a/clients/horizonclient/client.go +++ b/clients/horizonclient/client.go @@ -14,6 +14,7 @@ import ( "time" "github.com/stellar/go/txnbuild" + "github.com/stellar/go/xdr" "github.com/manucorporat/sse" @@ -47,7 +48,7 @@ func (c *Client) checkMemoRequired(transaction *txnbuild.Transaction) error { for i, op := range transaction.Operations() { var destination string - if err := op.Validate(false); err != nil { + if err := op.Validate(true); err != nil { return err } @@ -64,10 +65,15 @@ func (c *Client) checkMemoRequired(transaction *txnbuild.Transaction) error { continue } - // TODO: once we support M-strkeys (SEP23), also check whether the destination - // is a muxed account with a memo ID. + muxed, err := xdr.SEP23AddressToMuxedAccount(destination) + if err != nil { + return errors.Wrapf(err, "destination %v is not a valid address", destination) + } + // Skip destination addresses with a memo id because the address has a memo + // encoded within it + destinationHasMemoID := muxed.Type == xdr.CryptoKeyTypeKeyTypeMuxedEd25519 - if destinations[destination] { + if destinations[destination] || destinationHasMemoID { continue } destinations[destination] = true From f77f35238e752a51b4fd2cfa31d239f5a9ec1a4f Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Thu, 8 Apr 2021 12:54:59 +0200 Subject: [PATCH 12/22] Update strkey test cases with new format --- strkey/decode_test.go | 43 +++++++++++++++++++++++++++++-------------- strkey/encode_test.go | 4 ++-- strkey/main.go | 4 ++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/strkey/decode_test.go b/strkey/decode_test.go index 932b096388..bcab7b45c6 100644 --- a/strkey/decode_test.go +++ b/strkey/decode_test.go @@ -27,14 +27,14 @@ func TestDecode(t *testing.T) { { Name: "MuxedAccount", - Address: "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + Address: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", 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, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, { @@ -100,7 +100,7 @@ func TestDecode(t *testing.T) { assert.Error(t, err) // non-canonical representation due to extra character - _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOGA") + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLKA") if assert.Error(t, err) { assert.Contains(t, err.Error(), "unused leftover character") } @@ -109,57 +109,72 @@ func TestDecode(t *testing.T) { // but they comply with the test's purpose all the same) // 1 unused bit (length 69) - _, err = Decode(VersionByteMuxedAccount, "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOH") + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLH") + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "unused bits should be set to 0") + } + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUR") 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") + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJB") 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") + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJC") 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") + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJE") 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") + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJI") 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") + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJ7") 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") + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJ6") 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") + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJ4") 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") + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJY") 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 + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJQ") assert.NotContains(t, err.Error(), "unused bits should be set to 0") + + // Padding bytes are not allowed + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK===") + assert.Contains(t, err.Error(), "illegal base32 data") + + // Invalid algorithm (low 3 bits of version byte are 7) + _, err = Decode(VersionByteMuxedAccount, "M47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ") + assert.Contains(t, err.Error(), "invalid version byte") + + // Invalid checksum + _, err = Decode(VersionByteMuxedAccount, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUO") + assert.Contains(t, err.Error(), "invalid checksum") } func TestMalformed(t *testing.T) { diff --git a/strkey/encode_test.go b/strkey/encode_test.go index 4111fd0f12..141dc0c8a2 100644 --- a/strkey/encode_test.go +++ b/strkey/encode_test.go @@ -28,13 +28,13 @@ func TestEncode(t *testing.T) { 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, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, - Expected: "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + Expected: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", }, { Name: "Seed", diff --git a/strkey/main.go b/strkey/main.go index 6facce6e2c..80ad1b9264 100644 --- a/strkey/main.go +++ b/strkey/main.go @@ -208,6 +208,10 @@ func decodeString(src string) ([]byte, error) { // 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] + if int(lastChar) >= len(decodingTable) { + // let's output an error matching the errors from the base32 decoder invocation below + return nil, errors.Wrap(base32.CorruptInputError(len(srcBytes)), "base32 decode failed") + } decodedLastChar := decodingTable[lastChar] if decodedLastChar == 0xff { // The last character from the input wasn't in the expected input alphabet. From 3b5df73130758d438998c1717fe0d61ab07c75ff Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Thu, 8 Apr 2021 17:34:37 +0200 Subject: [PATCH 13/22] Implement new SEP23 encoding ordering --- strkey/decode_test.go | 1 - xdr/muxed_account.go | 22 ++++++------ xdr/muxed_account_test.go | 73 ++++++++++++++++++++++++++++++++++----- 3 files changed, 76 insertions(+), 20 deletions(-) diff --git a/strkey/decode_test.go b/strkey/decode_test.go index bcab7b45c6..431a87d287 100644 --- a/strkey/decode_test.go +++ b/strkey/decode_test.go @@ -24,7 +24,6 @@ func TestDecode(t *testing.T) { 0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03, }, }, - { Name: "MuxedAccount", Address: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", diff --git a/xdr/muxed_account.go b/xdr/muxed_account.go index e1d877eace..7dbc4f8e5b 100644 --- a/xdr/muxed_account.go +++ b/xdr/muxed_account.go @@ -1,9 +1,9 @@ package xdr import ( - "errors" "fmt" + "github.com/pkg/errors" "github.com/stellar/go/strkey" ) @@ -35,14 +35,14 @@ func (m *MuxedAccount) SetAddress(address string) error { return err } if len(raw) != 32 { - return errors.New("invalid address") + return fmt.Errorf("invalid binary length: %d", len(raw)) } var ui Uint256 copy(ui[:], raw) *m, err = NewMuxedAccount(CryptoKeyTypeKeyTypeEd25519, ui) return err default: - return errors.New("invalid address") + return errors.New("invalid address length") } } @@ -63,17 +63,17 @@ func (m *MuxedAccount) SetAddressWithSEP23(address string) error { return err } if len(raw) != 40 { - return errors.New("invalid muxed address") + return fmt.Errorf("invalid binary length: %d", len(raw)) } var muxed MuxedAccountMed25519 - if err = muxed.Id.UnmarshalBinary(raw[:8]); err != nil { + copy(muxed.Ed25519[:], raw[:32]) + if err = muxed.Id.UnmarshalBinary(raw[32:]); err != nil { return err } - copy(muxed.Ed25519[:], raw[8:]) *m, err = NewMuxedAccount(CryptoKeyTypeKeyTypeMuxedEd25519, muxed) return err default: - return errors.New("invalid address") + return errors.New("invalid address length") } } @@ -114,21 +114,21 @@ func (m *MuxedAccount) GetSEP23Address() (string, error) { case CryptoKeyTypeKeyTypeEd25519: ed, ok := m.GetEd25519() if !ok { - return "", fmt.Errorf("Could not get Ed25519") + return "", errors.New("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") + return "", errors.New("could not get Med25519") } idBytes, err := ed.Id.MarshalBinary() if err != nil { - return "", fmt.Errorf("Could not marshal ID") + return "", errors.Wrap(err, "could not marshal ID") } - raw = append(raw, idBytes...) raw = append(raw, ed.Ed25519[:]...) + raw = append(raw, idBytes...) return strkey.Encode(strkey.VersionByteMuxedAccount, raw) default: return "", fmt.Errorf("Unknown muxed account type: %v", m.Type) diff --git a/xdr/muxed_account_test.go b/xdr/muxed_account_test.go index 136ee7498d..71aa7e8c30 100644 --- a/xdr/muxed_account_test.go +++ b/xdr/muxed_account_test.go @@ -8,6 +8,37 @@ import ( ) var _ = Describe("xdr.MuxedAccount#Get/SetAddress()", func() { + It("returns an empty string when muxed account is nil", func() { + addy := (*MuxedAccount)(nil).SEP23Address() + 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.SEP23Address() + Expect(muxedy).To(Equal("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ")) + + var muxed MuxedAccount + err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK") + 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.SEP23Address() + Expect(muxedy).To(Equal("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK")) + + err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ") + 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.SEP23Address() + Expect(muxedy).To(Equal("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ")) + }) It("returns an error when the strkey is invalid", func() { var muxed MuxedAccount @@ -17,6 +48,9 @@ var _ = Describe("xdr.MuxedAccount#Get/SetAddress()", func() { err := muxed.SetAddress("GAAAAAAAACGC6") Expect(err).Should(HaveOccurred()) + err = muxed.SetAddress("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUR") + Expect(err).Should(HaveOccurred()) + err = muxed.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZA") Expect(err).Should(HaveOccurred()) @@ -26,25 +60,48 @@ var _ = Describe("xdr.MuxedAccount#Get/SetAddress()", func() { err = muxed.SetAddress("G47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVP2I") Expect(err).Should(HaveOccurred()) + err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLKA") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAAV75I") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddressWithSEP23("M47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUK===") + Expect(err).Should(HaveOccurred()) + + err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUO") + Expect(err).Should(HaveOccurred()) + }) +}) + +var _ = Describe("xdr.AddressToMuxedAccount()", func() { + It("works", func() { + address := "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ" + muxedAccount, err := SEP23AddressToMuxedAccount(address) + + Expect(muxedAccount.SEP23Address()).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")) - muxed = MuxedAccount{ - Type: CryptoKeyTypeKeyTypeMuxedEd25519, - Med25519: &MuxedAccountMed25519{ - Id: 0xcafebabe, - Ed25519: *muxed.Ed25519, - }, - } - + err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK") + Expect(err).ShouldNot(HaveOccurred()) aid = muxed.ToAccountId() Expect(aid.Address()).To(Equal("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ")) }) From 32073d9c85241f48bbce2bdf9627ca98047db6e1 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Mon, 12 Apr 2021 14:02:26 +0200 Subject: [PATCH 14/22] Address review comments --- clients/horizonclient/client.go | 2 +- txnbuild/account_merge.go | 8 +++--- txnbuild/clawback.go | 13 +++------ txnbuild/operation.go | 6 ++-- txnbuild/operation_test.go | 14 +++++----- txnbuild/path_payment.go | 8 +++--- txnbuild/path_payment_strict_send.go | 8 +++--- txnbuild/payment.go | 8 +++--- txnbuild/transaction.go | 10 +++---- xdr/muxed_account.go | 30 ++++++++++---------- xdr/muxed_account_test.go | 42 ++++++++++++++-------------- 11 files changed, 72 insertions(+), 77 deletions(-) diff --git a/clients/horizonclient/client.go b/clients/horizonclient/client.go index 063efdd512..e996f27b09 100644 --- a/clients/horizonclient/client.go +++ b/clients/horizonclient/client.go @@ -65,7 +65,7 @@ func (c *Client) checkMemoRequired(transaction *txnbuild.Transaction) error { continue } - muxed, err := xdr.SEP23AddressToMuxedAccount(destination) + muxed, err := xdr.AddressToMuxedAccount(destination) if err != nil { return errors.Wrapf(err, "destination %v is not a valid address", destination) } diff --git a/txnbuild/account_merge.go b/txnbuild/account_merge.go index 7fb7871002..66b801d279 100644 --- a/txnbuild/account_merge.go +++ b/txnbuild/account_merge.go @@ -17,9 +17,9 @@ func (am *AccountMerge) BuildXDR(withSEP23 bool) (xdr.Operation, error) { var xdrOp xdr.MuxedAccount var err error if withSEP23 { - err = xdrOp.SetAddressWithSEP23(am.Destination) - } else { err = xdrOp.SetAddress(am.Destination) + } else { + err = xdrOp.SetEd25519Address(am.Destination) } if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") @@ -48,7 +48,7 @@ func (am *AccountMerge) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) err am.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) if xdrOp.Body.Destination != nil { if withMuxedAccounts { - am.Destination = xdrOp.Body.Destination.SEP23Address() + am.Destination = xdrOp.Body.Destination.Address() } else { aid := xdrOp.Body.Destination.ToAccountId() am.Destination = aid.Address() @@ -65,7 +65,7 @@ func (am *AccountMerge) Validate(withMuxedAccounts bool) error { if withMuxedAccounts { _, err = xdr.AddressToAccountId(am.Destination) } else { - _, err = xdr.SEP23AddressToMuxedAccount(am.Destination) + _, err = xdr.AddressToMuxedAccount(am.Destination) } if err != nil { return NewValidationError("Destination", err.Error()) diff --git a/txnbuild/clawback.go b/txnbuild/clawback.go index bce93bee30..f3884f051e 100644 --- a/txnbuild/clawback.go +++ b/txnbuild/clawback.go @@ -21,9 +21,9 @@ func (cb *Clawback) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { var fromMuxedAccount xdr.MuxedAccount var err error if withMuxedAccounts { - err = fromMuxedAccount.SetAddressWithSEP23(cb.From) - } else { err = fromMuxedAccount.SetAddress(cb.From) + } else { + err = fromMuxedAccount.SetEd25519Address(cb.From) } if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set from address") @@ -70,12 +70,7 @@ func (cb *Clawback) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { } cb.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) - if withMuxedAccounts { - cb.From = result.From.SEP23Address() - } else { - fromAID := result.From.ToAccountId() - cb.From = fromAID.Address() - } + cb.From = accountFromXDR(&result.From, withMuxedAccounts) cb.Amount = amount.String(result.Amount) asset, err := assetFromXDR(result.Asset) if err != nil { @@ -91,7 +86,7 @@ func (cb *Clawback) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { func (cb *Clawback) Validate(withMuxedAccounts bool) error { var err error if withMuxedAccounts { - _, err = xdr.SEP23AddressToMuxedAccount(cb.From) + _, err = xdr.AddressToMuxedAccount(cb.From) } else { _, err = xdr.AddressToAccountId(cb.From) } diff --git a/txnbuild/operation.go b/txnbuild/operation.go index 31ee1f5cb9..58a3298c9c 100644 --- a/txnbuild/operation.go +++ b/txnbuild/operation.go @@ -20,7 +20,7 @@ func SetOpSourceAccount(op *xdr.Operation, sourceAccount string) { return } var opSourceAccountID xdr.MuxedAccount - opSourceAccountID.SetAddress(sourceAccount) + opSourceAccountID.SetEd25519Address(sourceAccount) op.SourceAccount = &opSourceAccountID } @@ -30,7 +30,7 @@ func SetOpSourceMuxedAccount(op *xdr.Operation, sourceAccount string) { return } var opSourceAccountID xdr.MuxedAccount - opSourceAccountID.SetAddressWithSEP23(sourceAccount) + opSourceAccountID.SetAddress(sourceAccount) op.SourceAccount = &opSourceAccountID } @@ -93,7 +93,7 @@ func operationFromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) (Operation, e func accountFromXDR(account *xdr.MuxedAccount, withMuxedAccounts bool) string { if account != nil { if withMuxedAccounts { - return account.SEP23Address() + return account.Address() } else { aid := account.ToAccountId() return aid.Address() diff --git a/txnbuild/operation_test.go b/txnbuild/operation_test.go index 4720b4a74d..cac1a2e052 100644 --- a/txnbuild/operation_test.go +++ b/txnbuild/operation_test.go @@ -284,7 +284,7 @@ func TestChangeTrustFromXDR(t *testing.T) { } var opSource xdr.MuxedAccount - err = opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") + err = opSource.SetEd25519Address("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) xdrOp := xdr.Operation{ SourceAccount: &opSource, @@ -314,7 +314,7 @@ func TestAllowTrustFromXDR(t *testing.T) { assert.NoError(t, err) var opSource xdr.MuxedAccount - err = opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") + err = opSource.SetEd25519Address("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) var trustor xdr.AccountId @@ -351,11 +351,11 @@ func TestAllowTrustFromXDR(t *testing.T) { func TestAccountMergeFromXDR(t *testing.T) { var opSource xdr.MuxedAccount - err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") + err := opSource.SetEd25519Address("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) var destination xdr.MuxedAccount - err = destination.SetAddress("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + err = destination.SetEd25519Address("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") assert.NoError(t, err) xdrOp := xdr.Operation{ @@ -376,7 +376,7 @@ func TestAccountMergeFromXDR(t *testing.T) { func TestInflationFromXDR(t *testing.T) { var opSource xdr.MuxedAccount - err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") + err := opSource.SetEd25519Address("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) xdrOp := xdr.Operation{ @@ -393,7 +393,7 @@ func TestInflationFromXDR(t *testing.T) { func TestManageDataFromXDR(t *testing.T) { var opSource xdr.MuxedAccount - err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") + err := opSource.SetEd25519Address("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) dv := []byte("value") @@ -422,7 +422,7 @@ func TestManageDataFromXDR(t *testing.T) { func TestBumpSequenceFromXDR(t *testing.T) { var opSource xdr.MuxedAccount - err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") + err := opSource.SetEd25519Address("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") assert.NoError(t, err) bsOp := xdr.BumpSequenceOp{ diff --git a/txnbuild/path_payment.go b/txnbuild/path_payment.go index a18d3c76bc..4f7f18151d 100644 --- a/txnbuild/path_payment.go +++ b/txnbuild/path_payment.go @@ -44,9 +44,9 @@ func (pp *PathPaymentStrictReceive) BuildXDR(withMuxedAccounts bool) (xdr.Operat // Set XDR destination var xdrDestination xdr.MuxedAccount if withMuxedAccounts { - err = xdrDestination.SetAddressWithSEP23(pp.Destination) - } else { err = xdrDestination.SetAddress(pp.Destination) + } else { + err = xdrDestination.SetEd25519Address(pp.Destination) } if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") @@ -109,7 +109,7 @@ func (pp *PathPaymentStrictReceive) FromXDR(xdrOp xdr.Operation, withMuxedAccoun pp.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) if withMuxedAccounts { - pp.Destination = xdrOp.Body.Destination.SEP23Address() + pp.Destination = xdrOp.Body.Destination.Address() } else { destAID := result.Destination.ToAccountId() pp.Destination = destAID.Address() @@ -150,7 +150,7 @@ func (pp *PathPaymentStrictReceive) Validate(withMuxedAccounts bool) error { if withMuxedAccounts { _, err = xdr.AddressToAccountId(pp.Destination) } else { - _, err = xdr.SEP23AddressToMuxedAccount(pp.Destination) + _, err = xdr.AddressToMuxedAccount(pp.Destination) } if err != nil { diff --git a/txnbuild/path_payment_strict_send.go b/txnbuild/path_payment_strict_send.go index 1e2787f827..2b497a8ffd 100644 --- a/txnbuild/path_payment_strict_send.go +++ b/txnbuild/path_payment_strict_send.go @@ -38,9 +38,9 @@ func (pp *PathPaymentStrictSend) BuildXDR(withMuxedAccounts bool) (xdr.Operation // Set XDR destination var xdrDestination xdr.MuxedAccount if withMuxedAccounts { - err = xdrDestination.SetAddressWithSEP23(pp.Destination) - } else { err = xdrDestination.SetAddress(pp.Destination) + } else { + err = xdrDestination.SetEd25519Address(pp.Destination) } if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") @@ -106,7 +106,7 @@ func (pp *PathPaymentStrictSend) FromXDR(xdrOp xdr.Operation, withMuxedAccounts destAID := result.Destination.ToAccountId() pp.Destination = destAID.Address() } else { - pp.Destination = result.Destination.SEP23Address() + pp.Destination = result.Destination.Address() } pp.SendAmount = amount.String(result.SendAmount) pp.DestMin = amount.String(result.DestMin) @@ -142,7 +142,7 @@ func (pp *PathPaymentStrictSend) Validate(withMuxedAccounts bool) error { if withMuxedAccounts { _, err = xdr.AddressToAccountId(pp.Destination) } else { - _, err = xdr.SEP23AddressToMuxedAccount(pp.Destination) + _, err = xdr.AddressToMuxedAccount(pp.Destination) } if err != nil { return NewValidationError("Destination", err.Error()) diff --git a/txnbuild/payment.go b/txnbuild/payment.go index 2858a6d998..d16c60633e 100644 --- a/txnbuild/payment.go +++ b/txnbuild/payment.go @@ -22,9 +22,9 @@ func (p *Payment) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { var err error if withMuxedAccounts { - err = destMuxedAccount.SetAddressWithSEP23(p.Destination) - } else { err = destMuxedAccount.SetAddress(p.Destination) + } else { + err = destMuxedAccount.SetEd25519Address(p.Destination) } if err != nil { return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") @@ -71,7 +71,7 @@ func (p *Payment) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { p.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) if withMuxedAccounts { - p.Destination = xdrOp.Body.Destination.SEP23Address() + p.Destination = xdrOp.Body.Destination.Address() } else { destAID := result.Destination.ToAccountId() p.Destination = destAID.Address() @@ -96,7 +96,7 @@ func (p *Payment) Validate(withMuxedAccounts bool) error { if withMuxedAccounts { _, err = xdr.AddressToAccountId(p.Destination) } else { - _, err = xdr.SEP23AddressToMuxedAccount(p.Destination) + _, err = xdr.AddressToMuxedAccount(p.Destination) } if err != nil { diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index b4ecd44ec7..4c15cb153e 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -556,7 +556,7 @@ func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope, withMuxedAccounts var feeAccount string if withMuxedAccounts { feeBumpAccount := xdrEnv.FeeBumpAccount() - feeAccount = feeBumpAccount.SEP23Address() + feeAccount = feeBumpAccount.Address() } else { feeBumpAccount := xdrEnv.FeeBumpAccount().ToAccountId() feeAccount = feeBumpAccount.Address() @@ -579,7 +579,7 @@ func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope, withMuxedAccounts var accountID string if withMuxedAccounts { sourceAccount := xdrEnv.SourceAccount() - accountID = sourceAccount.SEP23Address() + accountID = sourceAccount.Address() } else { sourceAccount := xdrEnv.SourceAccount().ToAccountId() accountID = sourceAccount.Address() @@ -667,7 +667,7 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { } var sourceAccount xdr.MuxedAccount if params.EnableMuxedAccounts { - if err = sourceAccount.SetAddressWithSEP23(tx.sourceAccount.AccountID); err != nil { + if err = sourceAccount.SetAddress(tx.sourceAccount.AccountID); err != nil { return nil, errors.Wrap(err, "account id is not valid") } } else { @@ -729,7 +729,7 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { } for _, op := range tx.operations { - if verr := op.Validate(false); verr != nil { + if verr := op.Validate(params.EnableMuxedAccounts); verr != nil { return nil, errors.Wrap(verr, fmt.Sprintf("validation failed for %T operation", op)) } xdrOperation, err2 := op.BuildXDR(params.EnableMuxedAccounts) @@ -822,7 +822,7 @@ func NewFeeBumpTransaction(params FeeBumpTransactionParams) (*FeeBumpTransaction var feeSource xdr.MuxedAccount if params.EnableMuxedAccounts { - if err := feeSource.SetAddressWithSEP23(tx.feeAccount); err != nil { + if err := feeSource.SetAddress(tx.feeAccount); err != nil { return tx, errors.Wrap(err, "fee account is not a valid address") } } else { diff --git a/xdr/muxed_account.go b/xdr/muxed_account.go index 7dbc4f8e5b..7a4a057977 100644 --- a/xdr/muxed_account.go +++ b/xdr/muxed_account.go @@ -9,7 +9,7 @@ import ( func MustMuxedAddress(address string) MuxedAccount { muxed := MuxedAccount{} - err := muxed.SetAddress(address) + err := muxed.SetEd25519Address(address) if err != nil { panic(err) } @@ -21,9 +21,9 @@ func MustMuxedAddressPtr(address string) *MuxedAccount { return &muxed } -// SetAddress modifies the receiver, setting it's value to the MuxedAccount form -// of the provided G-address. -func (m *MuxedAccount) SetAddress(address string) error { +// SetEd25519Address modifies the receiver, setting it's value to the MuxedAccount form +// of the provided G-address. Unlike SetAddress(), it only supports G-addresses. +func (m *MuxedAccount) SetEd25519Address(address string) error { if m == nil { return nil } @@ -47,16 +47,16 @@ func (m *MuxedAccount) SetAddress(address string) error { } -// SetAddressWithSEP23 modifies the receiver, setting it's value to the MuxedAccount form +// SetAddress modifies the receiver, setting it's value to the MuxedAccount form // of the provided strkey G-address or M-address, as described in SEP23. -func (m *MuxedAccount) SetAddressWithSEP23(address string) error { +func (m *MuxedAccount) SetAddress(address string) error { if m == nil { return nil } switch len(address) { case 56: - return m.SetAddress(address) + return m.SetEd25519Address(address) case 69: raw, err := strkey.Decode(strkey.VersionByteMuxedAccount, address) if err != nil { @@ -78,33 +78,33 @@ func (m *MuxedAccount) SetAddressWithSEP23(address string) error { } -// SEP23AddressToMuxedAccount returns an MuxedAccount for a given address string +// AddressToMuxedAccount returns an MuxedAccount for a given address string // or SEP23 M-address. // If the address is not valid the error returned will not be nil -func SEP23AddressToMuxedAccount(address string) (MuxedAccount, error) { +func AddressToMuxedAccount(address string) (MuxedAccount, error) { result := MuxedAccount{} - err := result.SetAddressWithSEP23(address) + err := result.SetAddress(address) return result, err } -// SEP23Address returns the strkey-encoded form of this MuxedAccount. In particular, it will +// Address returns the strkey-encoded form of this MuxedAccount. In particular, it will // return an M- strkey representation for CryptoKeyTypeKeyTypeMuxedEd25519 variants of the account // (according to SEP23). This method will panic if the MuxedAccount is backed by a public key of an // unknown type. -func (m *MuxedAccount) SEP23Address() string { - address, err := m.GetSEP23Address() +func (m *MuxedAccount) Address() string { + address, err := m.GetAddress() if err != nil { panic(err) } return address } -// GetSEP23Address returns the strkey-encoded form of this MuxedAccount. In particular, it will +// GetAddress returns the strkey-encoded form of this MuxedAccount. In particular, it will // return an M-strkey representation for CryptoKeyTypeKeyTypeMuxedEd25519 variants of the account // (according to SEP23). In addition it will return an error if the MuxedAccount is backed by a // public key of an unknown type. -func (m *MuxedAccount) GetSEP23Address() (string, error) { +func (m *MuxedAccount) GetAddress() (string, error) { if m == nil { return "", nil } diff --git a/xdr/muxed_account_test.go b/xdr/muxed_account_test.go index 71aa7e8c30..47cc3a43f6 100644 --- a/xdr/muxed_account_test.go +++ b/xdr/muxed_account_test.go @@ -9,34 +9,34 @@ import ( var _ = Describe("xdr.MuxedAccount#Get/SetAddress()", func() { It("returns an empty string when muxed account is nil", func() { - addy := (*MuxedAccount)(nil).SEP23Address() + 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") + err := unmuxed.SetEd25519Address("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.SEP23Address() + muxedy := unmuxed.Address() Expect(muxedy).To(Equal("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ")) var muxed MuxedAccount - err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK") + err = muxed.SetAddress("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK") 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.SEP23Address() + muxedy = muxed.Address() Expect(muxedy).To(Equal("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK")) - err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ") + err = muxed.SetAddress("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ") 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.SEP23Address() + muxedy = muxed.Address() Expect(muxedy).To(Equal("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ")) }) @@ -45,34 +45,34 @@ var _ = Describe("xdr.MuxedAccount#Get/SetAddress()", func() { // Test cases from SEP23 - err := muxed.SetAddress("GAAAAAAAACGC6") + err := muxed.SetEd25519Address("GAAAAAAAACGC6") Expect(err).Should(HaveOccurred()) - err = muxed.SetAddress("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUR") + err = muxed.SetEd25519Address("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUR") Expect(err).Should(HaveOccurred()) - err = muxed.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZA") + err = muxed.SetEd25519Address("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZA") Expect(err).Should(HaveOccurred()) - err = muxed.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUACUSI") + err = muxed.SetEd25519Address("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUACUSI") Expect(err).Should(HaveOccurred()) - err = muxed.SetAddress("G47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVP2I") + err = muxed.SetEd25519Address("G47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVP2I") Expect(err).Should(HaveOccurred()) - err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLKA") + err = muxed.SetAddress("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLKA") Expect(err).Should(HaveOccurred()) - err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAAV75I") + err = muxed.SetAddress("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAAV75I") Expect(err).Should(HaveOccurred()) - err = muxed.SetAddressWithSEP23("M47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ") + err = muxed.SetAddress("M47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ") Expect(err).Should(HaveOccurred()) - err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUK===") + err = muxed.SetAddress("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUK===") Expect(err).Should(HaveOccurred()) - err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUO") + err = muxed.SetAddress("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUO") Expect(err).Should(HaveOccurred()) }) }) @@ -80,9 +80,9 @@ var _ = Describe("xdr.MuxedAccount#Get/SetAddress()", func() { var _ = Describe("xdr.AddressToMuxedAccount()", func() { It("works", func() { address := "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ" - muxedAccount, err := SEP23AddressToMuxedAccount(address) + muxedAccount, err := AddressToMuxedAccount(address) - Expect(muxedAccount.SEP23Address()).To(Equal("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ")) + Expect(muxedAccount.Address()).To(Equal("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ")) Expect(err).ShouldNot(HaveOccurred()) _, err = AddressToAccountId("GCR22L3") @@ -95,12 +95,12 @@ var _ = Describe("xdr.MuxedAccount.ToAccountId()", func() { It("works", func() { var muxed MuxedAccount - err := muxed.SetAddress("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ") + err := muxed.SetEd25519Address("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ") Expect(err).ShouldNot(HaveOccurred()) aid := muxed.ToAccountId() Expect(aid.Address()).To(Equal("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ")) - err = muxed.SetAddressWithSEP23("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK") + err = muxed.SetAddress("MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK") Expect(err).ShouldNot(HaveOccurred()) aid = muxed.ToAccountId() Expect(aid.Address()).To(Equal("GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ")) From 5010f357df5c7930de829c1cad07a133011bf99c Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Mon, 12 Apr 2021 18:59:06 +0200 Subject: [PATCH 15/22] Remove decoding check (it's part of another PR) --- strkey/main.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/strkey/main.go b/strkey/main.go index 80ad1b9264..6facce6e2c 100644 --- a/strkey/main.go +++ b/strkey/main.go @@ -208,10 +208,6 @@ func decodeString(src string) ([]byte, error) { // 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] - if int(lastChar) >= len(decodingTable) { - // let's output an error matching the errors from the base32 decoder invocation below - return nil, errors.Wrap(base32.CorruptInputError(len(srcBytes)), "base32 decode failed") - } decodedLastChar := decodingTable[lastChar] if decodedLastChar == 0xff { // The last character from the input wasn't in the expected input alphabet. From 10c5ed47cdd67932c6f8d738361f8c2a154c2965 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 13 Apr 2021 11:08:51 +0200 Subject: [PATCH 16/22] Address review comments from leigh and add some tests --- txnbuild/account_merge.go | 6 +- txnbuild/begin_sponsoring_future_reserves.go | 2 +- txnbuild/bump_sequence.go | 2 +- txnbuild/claim_claimable_balance.go | 2 +- txnbuild/clawback_claimable_balance.go | 2 +- txnbuild/create_claimable_balance.go | 2 +- txnbuild/create_passive_offer.go | 2 +- txnbuild/end_sponsoring_future_reserves.go | 2 +- txnbuild/inflation.go | 2 +- txnbuild/manage_buy_offer.go | 2 +- txnbuild/manage_data.go | 2 +- txnbuild/manage_offer.go | 2 +- txnbuild/payment.go | 4 +- txnbuild/revoke_sponsorship.go | 2 +- txnbuild/set_options.go | 2 +- txnbuild/set_trust_line_flags.go | 2 +- txnbuild/transaction.go | 12 ++-- txnbuild/transaction_test.go | 69 ++++++++++++++++++++ 18 files changed, 94 insertions(+), 25 deletions(-) diff --git a/txnbuild/account_merge.go b/txnbuild/account_merge.go index 66b801d279..ddee989b4c 100644 --- a/txnbuild/account_merge.go +++ b/txnbuild/account_merge.go @@ -13,10 +13,10 @@ type AccountMerge struct { } // BuildXDR for AccountMerge returns a fully configured XDR Operation. -func (am *AccountMerge) BuildXDR(withSEP23 bool) (xdr.Operation, error) { +func (am *AccountMerge) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { var xdrOp xdr.MuxedAccount var err error - if withSEP23 { + if withMuxedAccounts { err = xdrOp.SetAddress(am.Destination) } else { err = xdrOp.SetEd25519Address(am.Destination) @@ -31,7 +31,7 @@ func (am *AccountMerge) BuildXDR(withSEP23 bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") } op := xdr.Operation{Body: body} - if withSEP23 { + if withMuxedAccounts { SetOpSourceMuxedAccount(&op, am.SourceAccount) } else { SetOpSourceAccount(&op, am.SourceAccount) diff --git a/txnbuild/begin_sponsoring_future_reserves.go b/txnbuild/begin_sponsoring_future_reserves.go index c5ad55f0ae..e4a383258c 100644 --- a/txnbuild/begin_sponsoring_future_reserves.go +++ b/txnbuild/begin_sponsoring_future_reserves.go @@ -45,7 +45,7 @@ func (bs *BeginSponsoringFutureReserves) FromXDR(xdrOp xdr.Operation, withMuxedA // Validate for BeginSponsoringFutureReserves validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (bs *BeginSponsoringFutureReserves) Validate(bool) error { +func (bs *BeginSponsoringFutureReserves) Validate(withMuxedAccounts bool) error { err := validateStellarPublicKey(bs.SponsoredID) if err != nil { return NewValidationError("SponsoredID", err.Error()) diff --git a/txnbuild/bump_sequence.go b/txnbuild/bump_sequence.go index e9f213ca7b..61e32acd28 100644 --- a/txnbuild/bump_sequence.go +++ b/txnbuild/bump_sequence.go @@ -43,7 +43,7 @@ func (bs *BumpSequence) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) err // Validate for BumpSequence validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (bs *BumpSequence) Validate(bool) error { +func (bs *BumpSequence) Validate(withMuxedAccounts bool) error { err := validateAmount(bs.BumpTo) if err != nil { return NewValidationError("BumpTo", err.Error()) diff --git a/txnbuild/claim_claimable_balance.go b/txnbuild/claim_claimable_balance.go index 96ae94e1d3..4f39eb9141 100644 --- a/txnbuild/claim_claimable_balance.go +++ b/txnbuild/claim_claimable_balance.go @@ -58,7 +58,7 @@ func (cb *ClaimClaimableBalance) FromXDR(xdrOp xdr.Operation, withMuxedAccounts // Validate for ClaimClaimableBalance validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (cb *ClaimClaimableBalance) Validate(bool) error { +func (cb *ClaimClaimableBalance) Validate(withMuxedAccounts bool) error { var xdrBalanceID xdr.ClaimableBalanceId err := xdr.SafeUnmarshalHex(cb.BalanceID, &xdrBalanceID) if err != nil { diff --git a/txnbuild/clawback_claimable_balance.go b/txnbuild/clawback_claimable_balance.go index 6d6ee85f14..da289233a5 100644 --- a/txnbuild/clawback_claimable_balance.go +++ b/txnbuild/clawback_claimable_balance.go @@ -57,7 +57,7 @@ func (cb *ClawbackClaimableBalance) FromXDR(xdrOp xdr.Operation, withMuxedAccoun // Validate for ClawbackClaimableBalance validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (cb *ClawbackClaimableBalance) Validate(bool) error { +func (cb *ClawbackClaimableBalance) Validate(withMuxedAccounts bool) error { var xdrBalanceID xdr.ClaimableBalanceId err := xdr.SafeUnmarshalHex(cb.BalanceID, &xdrBalanceID) if err != nil { diff --git a/txnbuild/create_claimable_balance.go b/txnbuild/create_claimable_balance.go index 6513b28c09..578fb8931b 100644 --- a/txnbuild/create_claimable_balance.go +++ b/txnbuild/create_claimable_balance.go @@ -170,7 +170,7 @@ func (cb *CreateClaimableBalance) FromXDR(xdrOp xdr.Operation, withMuxedAccounts // Validate for CreateClaimableBalance validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (cb *CreateClaimableBalance) Validate(bool) error { +func (cb *CreateClaimableBalance) Validate(withMuxedAccounts bool) error { for _, d := range cb.Destinations { err := validateStellarPublicKey(d.Destination) if err != nil { diff --git a/txnbuild/create_passive_offer.go b/txnbuild/create_passive_offer.go index 6eb3cdd325..b4c56fe54b 100644 --- a/txnbuild/create_passive_offer.go +++ b/txnbuild/create_passive_offer.go @@ -88,7 +88,7 @@ func (cpo *CreatePassiveSellOffer) FromXDR(xdrOp xdr.Operation, withMuxedAccount // Validate for CreatePassiveSellOffer validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (cpo *CreatePassiveSellOffer) Validate(bool) error { +func (cpo *CreatePassiveSellOffer) Validate(withMuxedAccounts bool) error { return validatePassiveOffer(cpo.Buying, cpo.Selling, cpo.Amount, cpo.Price) } diff --git a/txnbuild/end_sponsoring_future_reserves.go b/txnbuild/end_sponsoring_future_reserves.go index 9bb2f2c9dd..e0617a1891 100644 --- a/txnbuild/end_sponsoring_future_reserves.go +++ b/txnbuild/end_sponsoring_future_reserves.go @@ -41,7 +41,7 @@ func (es *EndSponsoringFutureReserves) FromXDR(xdrOp xdr.Operation, withMuxedAcc // Validate for EndSponsoringFutureReserves validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (es *EndSponsoringFutureReserves) Validate(bool) error { +func (es *EndSponsoringFutureReserves) Validate(withMuxedAccounts bool) error { return nil } diff --git a/txnbuild/inflation.go b/txnbuild/inflation.go index ec80299462..0677bed2fc 100644 --- a/txnbuild/inflation.go +++ b/txnbuild/inflation.go @@ -38,7 +38,7 @@ func (inf *Inflation) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error // Validate for Inflation is just a method that implements the Operation interface. No logic is actually performed // because the inflation operation does not have any required field. Nil is always returned. -func (inf *Inflation) Validate(bool) error { +func (inf *Inflation) Validate(withMuxedAccounts bool) error { // no required fields, return nil. return nil } diff --git a/txnbuild/manage_buy_offer.go b/txnbuild/manage_buy_offer.go index 9360cf1af7..e6badcb19e 100644 --- a/txnbuild/manage_buy_offer.go +++ b/txnbuild/manage_buy_offer.go @@ -91,7 +91,7 @@ func (mo *ManageBuyOffer) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) e // Validate for ManageBuyOffer validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (mo *ManageBuyOffer) Validate(bool) error { +func (mo *ManageBuyOffer) Validate(withMuxedAccounts bool) error { return validateOffer(mo.Buying, mo.Selling, mo.Amount, mo.Price, mo.OfferID) } diff --git a/txnbuild/manage_data.go b/txnbuild/manage_data.go index b26a9b3fe7..38194b05e6 100644 --- a/txnbuild/manage_data.go +++ b/txnbuild/manage_data.go @@ -58,7 +58,7 @@ func (md *ManageData) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error // Validate for ManageData validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (md *ManageData) Validate(bool) error { +func (md *ManageData) Validate(withMuxedAccounts bool) error { if len(md.Name) > 64 { return NewValidationError("Name", "maximum length is 64 characters") } diff --git a/txnbuild/manage_offer.go b/txnbuild/manage_offer.go index fd292f05bc..248ffc2ada 100644 --- a/txnbuild/manage_offer.go +++ b/txnbuild/manage_offer.go @@ -155,7 +155,7 @@ func (mo *ManageSellOffer) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) // Validate for ManageSellOffer validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (mo *ManageSellOffer) Validate(bool) error { +func (mo *ManageSellOffer) Validate(withMuxedAccounts bool) error { return validateOffer(mo.Buying, mo.Selling, mo.Amount, mo.Price, mo.OfferID) } diff --git a/txnbuild/payment.go b/txnbuild/payment.go index d16c60633e..a9b7bb071e 100644 --- a/txnbuild/payment.go +++ b/txnbuild/payment.go @@ -94,9 +94,9 @@ func (p *Payment) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { func (p *Payment) Validate(withMuxedAccounts bool) error { var err error if withMuxedAccounts { - _, err = xdr.AddressToAccountId(p.Destination) - } else { _, err = xdr.AddressToMuxedAccount(p.Destination) + } else { + _, err = xdr.AddressToAccountId(p.Destination) } if err != nil { diff --git a/txnbuild/revoke_sponsorship.go b/txnbuild/revoke_sponsorship.go index 0481b24d6d..22e97fdadc 100644 --- a/txnbuild/revoke_sponsorship.go +++ b/txnbuild/revoke_sponsorship.go @@ -226,7 +226,7 @@ func (r *RevokeSponsorship) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) return nil } -func (r *RevokeSponsorship) Validate(bool) error { +func (r *RevokeSponsorship) Validate(withMuxedAccounts bool) error { switch r.SponsorshipType { case RevokeSponsorshipTypeAccount: if r.Account == nil { diff --git a/txnbuild/set_options.go b/txnbuild/set_options.go index e2e299c4b7..e34a900f1e 100644 --- a/txnbuild/set_options.go +++ b/txnbuild/set_options.go @@ -319,7 +319,7 @@ func (so *SetOptions) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error // Validate for SetOptions validates the required struct fields. It returns an error if any // of the fields are invalid. Otherwise, it returns nil. -func (so *SetOptions) Validate(bool) error { +func (so *SetOptions) Validate(withMuxedAccounts bool) error { // skipping checks here because the individual methods above already check for required fields. // Refactoring is out of the scope of this issue(https://github.com/stellar/go/issues/1041) so will leave as is for now. return nil diff --git a/txnbuild/set_trust_line_flags.go b/txnbuild/set_trust_line_flags.go index 0ac412ac67..12c59ab824 100644 --- a/txnbuild/set_trust_line_flags.go +++ b/txnbuild/set_trust_line_flags.go @@ -110,7 +110,7 @@ func fromXDRTrustlineFlag(flags xdr.Uint32) []TrustLineFlag { // Validate for SetTrustLineFlags validates the required struct fields. It returns an error if any of the fields are // invalid. Otherwise, it returns nil. -func (stf *SetTrustLineFlags) Validate(bool) error { +func (stf *SetTrustLineFlags) Validate(withMuxedAccounts bool) error { err := validateStellarPublicKey(stf.Trustor) if err != nil { return NewValidationError("Trustor", err.Error()) diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index 4c15cb153e..871a2a5089 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -514,15 +514,15 @@ func (t GenericTransaction) FeeBump() (*FeeBumpTransaction, bool) { return t.feeBump, t.feeBump != nil } -type ParseXDROption int +type TransactionFromXDROption int const ( - ParseXDROptionEnableMuxedAccounts ParseXDROption = iota + TransactionFromXDROptionEnableMuxedAccounts TransactionFromXDROption = iota ) -func areMuxedAccountsEnabled(options []ParseXDROption) bool { +func areMuxedAccountsEnabled(options []TransactionFromXDROption) bool { for _, opt := range options { - if opt == ParseXDROptionEnableMuxedAccounts { + if opt == TransactionFromXDROptionEnableMuxedAccounts { return true } } @@ -531,7 +531,7 @@ func areMuxedAccountsEnabled(options []ParseXDROption) bool { // TransactionFromXDR parses the supplied transaction envelope in base64 XDR // and returns a GenericTransaction instance. -func TransactionFromXDR(txeB64 string, options ...ParseXDROption) (*GenericTransaction, error) { +func TransactionFromXDR(txeB64 string, options ...TransactionFromXDROption) (*GenericTransaction, error) { var xdrEnv xdr.TransactionEnvelope err := xdr.SafeUnmarshalBase64(txeB64, &xdrEnv) if err != nil { @@ -673,7 +673,7 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { } else { accountID, err2 := xdr.AddressToAccountId(tx.sourceAccount.AccountID) if err2 != nil { - return nil, errors.Wrap(err, "account id is not valid") + return nil, errors.Wrap(err2, "account id is not valid") } sourceAccount = accountID.ToMuxedAccount() } diff --git a/txnbuild/transaction_test.go b/txnbuild/transaction_test.go index e85a49ca37..cc16c2b4a3 100644 --- a/txnbuild/transaction_test.go +++ b/txnbuild/transaction_test.go @@ -165,6 +165,75 @@ func TestPayment(t *testing.T) { assert.Equal(t, expected, received, "Base 64 XDR should match") } +func TestPaymentMuxedAccounts(t *testing.T) { + kp0 := newKeypair0() + accountID := xdr.MustAddress(kp0.Address()) + mx := xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xcafebabe, + Ed25519: *accountID.Ed25519, + }, + } + sourceAccount := NewSimpleAccount(mx.Address(), int64(9605939170639898)) + + payment := Payment{ + Destination: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Amount: "10", + Asset: NativeAsset{}, + } + + received, err := newSignedTransaction( + TransactionParams{ + SourceAccount: &sourceAccount, + IncrementSequenceNum: true, + Operations: []Operation{&payment}, + BaseFee: MinBaseFee, + Timebounds: NewInfiniteTimeout(), + EnableMuxedAccounts: true, + }, + network.TestNetworkPassphrase, + kp0, + ) + assert.NoError(t, err) + + expected := "AAAAAgAAAQAAAAAAyv66vuDcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAABAIAAAAAAAAAAPww0v5OtDZlx0EzMkPcFURyDiq2XNKSi+w16A/x/6JoAAAAAAAAAAAX14QAAAAAAAAAAAeoucsUAAABAKFlZsd3f3LJKm+mzxzNyTb8Q7477tk9XWFYXPGN5vVJir5wOoFMU4X3dd2Um8Z8ByfvxcaM/gr2zVsTBCYuaAA==" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestPaymentFailsMuxedAccountsIfNotEnabled(t *testing.T) { + kp0 := newKeypair0() + accountID := xdr.MustAddress(kp0.Address()) + mx := xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xcafebabe, + Ed25519: *accountID.Ed25519, + }, + } + sourceAccount := NewSimpleAccount(mx.Address(), int64(9605939170639898)) + + payment := Payment{ + Destination: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Amount: "10", + Asset: NativeAsset{}, + } + + _, err := newSignedTransaction( + TransactionParams{ + SourceAccount: &sourceAccount, + IncrementSequenceNum: true, + Operations: []Operation{&payment}, + BaseFee: MinBaseFee, + Timebounds: NewInfiniteTimeout(), + EnableMuxedAccounts: false, + }, + network.TestNetworkPassphrase, + kp0, + ) + assert.Error(t, err) +} + func TestPaymentFailsIfNoAssetSpecified(t *testing.T) { kp0 := newKeypair0() sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) From 265b60182afeb3e2e825d111412b7c75167eae01 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 13 Apr 2021 12:38:30 +0200 Subject: [PATCH 17/22] Add more tests --- txnbuild/fee_bump_test.go | 113 +++++++++++++---------------------- txnbuild/payment.go | 3 +- txnbuild/transaction_test.go | 35 +++++++++-- 3 files changed, 72 insertions(+), 79 deletions(-) diff --git a/txnbuild/fee_bump_test.go b/txnbuild/fee_bump_test.go index b731dfa744..63162b96fb 100644 --- a/txnbuild/fee_bump_test.go +++ b/txnbuild/fee_bump_test.go @@ -346,92 +346,59 @@ func TestFeeBumpAddSignatureBase64(t *testing.T) { assert.Equal(t, expected, b64) } -func TestFeeBumpRoundTrip(t *testing.T) { +func TestFeeBumpMuxedAccounts(t *testing.T) { kp0, kp1 := newKeypair0(), newKeypair1() - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - + accountID0 := xdr.MustAddress(kp0.Address()) + mx0 := xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xcafebabe, + Ed25519: *accountID0.Ed25519, + }, + } + sourceAccount := NewSimpleAccount(mx0.Address(), 1) tx, err := NewTransaction( TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), + SourceAccount: &sourceAccount, + Operations: []Operation{&Inflation{}}, + BaseFee: MinBaseFee, + Timebounds: NewInfiniteTimeout(), + EnableMuxedAccounts: true, }, ) assert.NoError(t, err) tx, err = tx.Sign(network.TestNetworkPassphrase, kp0) assert.NoError(t, err) - expectedInnerB64, err := tx.Base64() - assert.NoError(t, err) + accountID1 := xdr.MustAddress(kp1.Address()) + mx1 := xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xdeadbeef, + Ed25519: *accountID1.Ed25519, + }, + } feeBumpTx, err := NewFeeBumpTransaction( FeeBumpTransactionParams{ - FeeAccount: kp1.Address(), - BaseFee: 2 * MinBaseFee, - Inner: tx, + FeeAccount: mx1.Address(), + BaseFee: 2 * MinBaseFee, + Inner: tx, + EnableMuxedAccounts: true, }, ) assert.NoError(t, err) - feeBumpTx, err = feeBumpTx.Sign(network.TestNetworkPassphrase, kp1) - assert.NoError(t, err) - - innerB64, err := feeBumpTx.InnerTransaction().Base64() - assert.NoError(t, err) - assert.Equal(t, expectedInnerB64, innerB64) + assert.Equal(t, mx0.Address(), feeBumpTx.InnerTransaction().sourceAccount.AccountID) + assert.Equal(t, mx1.Address(), feeBumpTx.FeeAccount()) - assert.Equal(t, kp1.Address(), feeBumpTx.FeeAccount()) - assert.Equal(t, int64(2*MinBaseFee), feeBumpTx.BaseFee()) - assert.Equal(t, int64(4*MinBaseFee), feeBumpTx.MaxFee()) - - outerHash, err := feeBumpTx.HashHex(network.TestNetworkPassphrase) - assert.NoError(t, err) - - env := feeBumpTx.ToXDR() - assert.NoError(t, err) - assert.Equal(t, xdr.EnvelopeTypeEnvelopeTypeTxFeeBump, env.Type) - assert.Equal(t, xdr.MustAddress(kp1.Address()), env.FeeBumpAccount().ToAccountId()) - assert.Equal(t, int64(4*MinBaseFee), env.FeeBumpFee()) - assert.Equal(t, feeBumpTx.Signatures(), env.FeeBumpSignatures()) - innerB64, err = xdr.MarshalBase64(xdr.TransactionEnvelope{ - Type: xdr.EnvelopeTypeEnvelopeTypeTx, - V1: env.FeeBump.Tx.InnerTx.V1, - }) - assert.NoError(t, err) - assert.Equal(t, expectedInnerB64, innerB64) - - expectedFeeBumpB64, err := xdr.MarshalBase64(env) - assert.NoError(t, err) - - b64, err := feeBumpTx.Base64() - assert.NoError(t, err) - assert.Equal(t, expectedFeeBumpB64, b64) - - binary, err := feeBumpTx.MarshalBinary() - assert.NoError(t, err) - assert.Equal(t, expectedFeeBumpB64, base64.StdEncoding.EncodeToString(binary)) - - parsed, err := TransactionFromXDR(expectedFeeBumpB64) - assert.NoError(t, err) - parsedFeeBump, ok := parsed.FeeBump() - assert.True(t, ok) - _, ok = parsed.Transaction() - assert.False(t, ok) - - parsedHash, err := parsedFeeBump.HashHex(network.TestNetworkPassphrase) - assert.NoError(t, err) - - assert.Equal(t, feeBumpTx.Signatures(), parsedFeeBump.Signatures()) - assert.Equal(t, kp1.Address(), parsedFeeBump.FeeAccount()) - assert.Equal(t, int64(2*MinBaseFee), parsedFeeBump.BaseFee()) - assert.Equal(t, int64(4*MinBaseFee), parsedFeeBump.MaxFee()) - innerB64, err = xdr.MarshalBase64(xdr.TransactionEnvelope{ - Type: xdr.EnvelopeTypeEnvelopeTypeTx, - V1: parsedFeeBump.envelope.FeeBump.Tx.InnerTx.V1, - }) - assert.NoError(t, err) - assert.Equal(t, expectedInnerB64, innerB64) - b64, err = parsedFeeBump.Base64() - assert.NoError(t, err) - assert.Equal(t, expectedFeeBumpB64, b64) - assert.Equal(t, outerHash, parsedHash) + // It fails when not enabling muxed accounts + feeBumpTx, err = NewFeeBumpTransaction( + FeeBumpTransactionParams{ + FeeAccount: mx1.Address(), + BaseFee: 2 * MinBaseFee, + Inner: tx, + EnableMuxedAccounts: false, + }, + ) + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid version byte") } diff --git a/txnbuild/payment.go b/txnbuild/payment.go index a9b7bb071e..c183cfe144 100644 --- a/txnbuild/payment.go +++ b/txnbuild/payment.go @@ -71,11 +71,10 @@ func (p *Payment) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error { p.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) if withMuxedAccounts { - p.Destination = xdrOp.Body.Destination.Address() + p.Destination = result.Destination.Address() } else { destAID := result.Destination.ToAccountId() p.Destination = destAID.Address() - p.Destination = destAID.Address() } p.Amount = amount.String(result.Amount) diff --git a/txnbuild/transaction_test.go b/txnbuild/transaction_test.go index cc16c2b4a3..cb26411e55 100644 --- a/txnbuild/transaction_test.go +++ b/txnbuild/transaction_test.go @@ -178,9 +178,10 @@ func TestPaymentMuxedAccounts(t *testing.T) { sourceAccount := NewSimpleAccount(mx.Address(), int64(9605939170639898)) payment := Payment{ - Destination: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", - Amount: "10", - Asset: NativeAsset{}, + Destination: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Amount: "10", + Asset: NativeAsset{}, + SourceAccount: sourceAccount.AccountID, } received, err := newSignedTransaction( @@ -197,7 +198,7 @@ func TestPaymentMuxedAccounts(t *testing.T) { ) assert.NoError(t, err) - expected := "AAAAAgAAAQAAAAAAyv66vuDcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAABAIAAAAAAAAAAPww0v5OtDZlx0EzMkPcFURyDiq2XNKSi+w16A/x/6JoAAAAAAAAAAAX14QAAAAAAAAAAAeoucsUAAABAKFlZsd3f3LJKm+mzxzNyTb8Q7477tk9XWFYXPGN5vVJir5wOoFMU4X3dd2Um8Z8ByfvxcaM/gr2zVsTBCYuaAA==" + expected := "AAAAAgAAAQAAAAAAyv66vuDcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAEAAAAAAMr+ur7g3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAAEAAAEAgAAAAAAAAAA/DDS/k60NmXHQTMyQ9wVRHIOKrZc0pKL7DXoD/H/omgAAAAAAAAAABfXhAAAAAAAAAAAB6i5yxQAAAED4Wkvwf/BJV+fqa6Kvi+T/7ZL82pOinN68GlvEi9qK4klH+qITyvN3jRj5Nfz0+VrE2xBJPVc8sS/qN9LlznoC" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -1385,6 +1386,32 @@ func TestFromXDR(t *testing.T) { assert.Equal(t, "", op2.SourceAccount, "Operation source should match") assert.Equal(t, "test", op2.Name, "Name should match") assert.Equal(t, "value", string(op2.Value), "Value should match") + + // Muxed accounts + txB64WithMuxedAccounts := "AAAAAgAAAQAAAAAAyv66vuDcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAEAAAAAAMr+ur7g3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAAEAAAEAgAAAAAAAAAA/DDS/k60NmXHQTMyQ9wVRHIOKrZc0pKL7DXoD/H/omgAAAAAAAAAABfXhAAAAAAAAAAAB6i5yxQAAAED4Wkvwf/BJV+fqa6Kvi+T/7ZL82pOinN68GlvEi9qK4klH+qITyvN3jRj5Nfz0+VrE2xBJPVc8sS/qN9LlznoC" + + // It provides M-addreses when enabling muxed accounts + tx3, err := TransactionFromXDR(txB64WithMuxedAccounts, TransactionFromXDROptionEnableMuxedAccounts) + assert.NoError(t, err) + newTx3, ok := tx3.Transaction() + assert.True(t, ok) + assert.Equal(t, "MDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKAAAAAAMV7V2XYGQO", newTx3.sourceAccount.AccountID) + op3, ok3 := newTx3.Operations()[0].(*Payment) + assert.True(t, ok3) + assert.Equal(t, "MDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKAAAAAAMV7V2XYGQO", op3.SourceAccount) + assert.Equal(t, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", op3.Destination) + + // It does provide G-addreses when not enabling muxed accounts + tx3, err = TransactionFromXDR(txB64WithMuxedAccounts) + assert.NoError(t, err) + newTx3, ok = tx3.Transaction() + assert.True(t, ok) + assert.Equal(t, "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", newTx3.sourceAccount.AccountID) + op3, ok3 = newTx3.Operations()[0].(*Payment) + assert.True(t, ok3) + assert.Equal(t, "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", op3.SourceAccount) + assert.Equal(t, "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ", op3.Destination) + } func TestBuild(t *testing.T) { From 85a33de5539a5ba2ba2fb4297b38b3fd37ad7354 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 13 Apr 2021 21:17:38 +0200 Subject: [PATCH 18/22] Add client test --- clients/horizonclient/main_test.go | 66 ++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/clients/horizonclient/main_test.go b/clients/horizonclient/main_test.go index 21f1c8c3d6..dc935305a2 100644 --- a/clients/horizonclient/main_test.go +++ b/clients/horizonclient/main_test.go @@ -17,6 +17,8 @@ import ( "github.com/stellar/go/support/errors" "github.com/stellar/go/support/http/httptest" "github.com/stellar/go/txnbuild" + "github.com/stellar/go/xdr" + "github.com/stretchr/testify/assert" ) @@ -903,6 +905,70 @@ func TestSubmitTransactionRequest(t *testing.T) { assert.Equal(t, ErrAccountRequiresMemo, errors.Cause(err)) } +func TestSubmitTransactionRequestMuxedAccounts(t *testing.T) { + hmock := httptest.NewClient() + client := &Client{ + HorizonURL: "https://localhost/", + HTTP: hmock, + } + + kp := keypair.MustParseFull("SA26PHIKZM6CXDGR472SSGUQQRYXM6S437ZNHZGRM6QA4FOPLLLFRGDX") + accountID := xdr.MustAddress(kp.Address()) + mx := xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xcafebabe, + Ed25519: *accountID.Ed25519, + }, + } + sourceAccount := txnbuild.NewSimpleAccount(mx.Address(), int64(0)) + + payment := txnbuild.Payment{ + Destination: kp.Address(), + Amount: "10", + Asset: txnbuild.NativeAsset{}, + } + + tx, err := txnbuild.NewTransaction( + txnbuild.TransactionParams{ + SourceAccount: &sourceAccount, + IncrementSequenceNum: true, + Operations: []txnbuild.Operation{&payment}, + BaseFee: txnbuild.MinBaseFee, + Timebounds: txnbuild.NewTimebounds(0, 10), + EnableMuxedAccounts: true, + }, + ) + assert.NoError(t, err) + + tx, err = tx.Sign(network.TestNetworkPassphrase, kp) + assert.NoError(t, err) + + // successful tx with config.memo_required not found + hmock.On( + "POST", + "https://localhost/transactions?tx=AAAAAgAAAQAAAAAAyv66vgU08yUQ8sHqhY8j9mXWwERfHC%2F3cKFSe%2FspAr0rGtO2AAAAZAAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAQAAAAAAAAABAAAAAAU08yUQ8sHqhY8j9mXWwERfHC%2F3cKFSe%2FspAr0rGtO2AAAAAAAAAAAF9eEAAAAAAAAAAAErGtO2AAAAQJvQkE9UVo%2FmfFBl%2F8ZPTzSUyVO4nvW0BYfnbowoBPEdRfLOLQz28v6sBKQc2b86NUfVHN5TQVo3%2BjH4nK9wVgk%3D", + ).ReturnString(200, txSuccess) + + hmock.On( + "GET", + "https://localhost/accounts/GACTJ4ZFCDZMD2UFR4R7MZOWYBCF6HBP65YKCUT37MUQFPJLDLJ3N5D2/data/config.memo_required", + ).ReturnString(404, notFoundResponse) + + _, err = client.SubmitTransaction(tx) + assert.NoError(t, err) + + // memo required - does not submit transaction + hmock.On( + "GET", + "https://localhost/accounts/GACTJ4ZFCDZMD2UFR4R7MZOWYBCF6HBP65YKCUT37MUQFPJLDLJ3N5D2/data/config.memo_required", + ).ReturnJSON(200, memoRequiredResponse) + + _, err = client.SubmitTransaction(tx) + assert.Error(t, err) + assert.Equal(t, ErrAccountRequiresMemo, errors.Cause(err)) +} + func TestSubmitFeeBumpTransaction(t *testing.T) { hmock := httptest.NewClient() client := &Client{ From afa16c384dd0ed8cda23ec902a43805b0abc7170 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 13 Apr 2021 22:17:11 +0200 Subject: [PATCH 19/22] Fix problems after auto-review --- txnbuild/create_passive_offer.go | 4 ++-- txnbuild/path_payment.go | 8 +++----- txnbuild/path_payment_strict_send.go | 12 ++++++------ 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/txnbuild/create_passive_offer.go b/txnbuild/create_passive_offer.go index b4c56fe54b..b77fb23c56 100644 --- a/txnbuild/create_passive_offer.go +++ b/txnbuild/create_passive_offer.go @@ -52,9 +52,9 @@ func (cpo *CreatePassiveSellOffer) BuildXDR(withMuxedAccounts bool) (xdr.Operati } op := xdr.Operation{Body: body} if withMuxedAccounts { - SetOpSourceAccount(&op, cpo.SourceAccount) - } else { SetOpSourceMuxedAccount(&op, cpo.SourceAccount) + } else { + SetOpSourceAccount(&op, cpo.SourceAccount) } return op, nil } diff --git a/txnbuild/path_payment.go b/txnbuild/path_payment.go index 4f7f18151d..26cb9967ea 100644 --- a/txnbuild/path_payment.go +++ b/txnbuild/path_payment.go @@ -109,13 +109,11 @@ func (pp *PathPaymentStrictReceive) FromXDR(xdrOp xdr.Operation, withMuxedAccoun pp.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) if withMuxedAccounts { - pp.Destination = xdrOp.Body.Destination.Address() + pp.Destination = result.Destination.Address() } else { destAID := result.Destination.ToAccountId() pp.Destination = destAID.Address() } - destAID := result.Destination.ToAccountId() - pp.Destination = destAID.Address() pp.DestAmount = amount.String(result.DestAmount) pp.SendMax = amount.String(result.SendMax) @@ -148,9 +146,9 @@ func (pp *PathPaymentStrictReceive) FromXDR(xdrOp xdr.Operation, withMuxedAccoun func (pp *PathPaymentStrictReceive) Validate(withMuxedAccounts bool) error { var err error if withMuxedAccounts { - _, err = xdr.AddressToAccountId(pp.Destination) - } else { _, err = xdr.AddressToMuxedAccount(pp.Destination) + } else { + _, err = xdr.AddressToAccountId(pp.Destination) } if err != nil { diff --git a/txnbuild/path_payment_strict_send.go b/txnbuild/path_payment_strict_send.go index 2b497a8ffd..2b9726a32d 100644 --- a/txnbuild/path_payment_strict_send.go +++ b/txnbuild/path_payment_strict_send.go @@ -87,9 +87,9 @@ func (pp *PathPaymentStrictSend) BuildXDR(withMuxedAccounts bool) (xdr.Operation } op := xdr.Operation{Body: body} if withMuxedAccounts { - SetOpSourceAccount(&op, pp.SourceAccount) - } else { SetOpSourceMuxedAccount(&op, pp.SourceAccount) + } else { + SetOpSourceAccount(&op, pp.SourceAccount) } return op, nil } @@ -103,10 +103,10 @@ func (pp *PathPaymentStrictSend) FromXDR(xdrOp xdr.Operation, withMuxedAccounts pp.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts) if withMuxedAccounts { + pp.Destination = result.Destination.Address() + } else { destAID := result.Destination.ToAccountId() pp.Destination = destAID.Address() - } else { - pp.Destination = result.Destination.Address() } pp.SendAmount = amount.String(result.SendAmount) pp.DestMin = amount.String(result.DestMin) @@ -140,9 +140,9 @@ func (pp *PathPaymentStrictSend) FromXDR(xdrOp xdr.Operation, withMuxedAccounts func (pp *PathPaymentStrictSend) Validate(withMuxedAccounts bool) error { var err error if withMuxedAccounts { - _, err = xdr.AddressToAccountId(pp.Destination) - } else { _, err = xdr.AddressToMuxedAccount(pp.Destination) + } else { + _, err = xdr.AddressToAccountId(pp.Destination) } if err != nil { return NewValidationError("Destination", err.Error()) From 0a6beda04eb273a401c13296f06b5c20265cd5fc Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Wed, 14 Apr 2021 02:03:12 +0200 Subject: [PATCH 20/22] Add operation tests --- txnbuild/account_merge_test.go | 14 ++++++++ txnbuild/allow_trust_test.go | 19 +++++++++++ .../begin_sponsoring_future_reserves_test.go | 2 +- txnbuild/bump_sequence_test.go | 14 ++++++++ txnbuild/change_trust_test.go | 17 ++++++++++ txnbuild/claim_claimable_balance_test.go | 13 ++++++-- txnbuild/clawback.go | 6 +++- txnbuild/clawback_claimable_balance_test.go | 13 ++++++-- txnbuild/clawback_test.go | 17 +++++++--- txnbuild/create_account_test.go | 18 +++++++++++ txnbuild/create_claimable_balance_test.go | 14 +++++++- .../end_sponsoring_future_reserves_test.go | 5 ++- txnbuild/inflation_test.go | 16 ++++++++++ txnbuild/manage_buy_offer_test.go | 23 +++++++++++++ txnbuild/manage_data_test.go | 17 ++++++++++ txnbuild/manage_offer_test.go | 23 +++++++++++++ txnbuild/operation_test.go | 32 +++++++++++++++---- txnbuild/path_payment_strict_send_test.go | 25 +++++++++++++++ txnbuild/path_payment_test.go | 25 +++++++++++++++ txnbuild/payment_test.go | 19 +++++++++++ txnbuild/revoke_sponsorship_test.go | 18 ++++++++++- txnbuild/set_trustline_flags_test.go | 12 ++++++- 22 files changed, 341 insertions(+), 21 deletions(-) create mode 100644 txnbuild/inflation_test.go diff --git a/txnbuild/account_merge_test.go b/txnbuild/account_merge_test.go index e2bf33d262..7d1e21ff32 100644 --- a/txnbuild/account_merge_test.go +++ b/txnbuild/account_merge_test.go @@ -27,3 +27,17 @@ func TestAccountMergeValidate(t *testing.T) { assert.Contains(t, err.Error(), expected) } } + +func TestAccountMergeRoundtrip(t *testing.T) { + accountMerge := AccountMerge{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Destination: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + } + testOperationsMarshallingRoundtrip(t, []Operation{&accountMerge}, false) + + accountMerge = AccountMerge{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Destination: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + } + testOperationsMarshallingRoundtrip(t, []Operation{&accountMerge}, true) +} diff --git a/txnbuild/allow_trust_test.go b/txnbuild/allow_trust_test.go index 4800a82beb..607e58a81b 100644 --- a/txnbuild/allow_trust_test.go +++ b/txnbuild/allow_trust_test.go @@ -57,3 +57,22 @@ func TestAllowTrustValidateTrustor(t *testing.T) { assert.Contains(t, err.Error(), expected) } } + +func TestAllowTrustRoundtrip(t *testing.T) { + allowTrust := AllowTrust{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Trustor: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Type: CreditAsset{"USD", ""}, + Authorize: true, + } + testOperationsMarshallingRoundtrip(t, []Operation{&allowTrust}, false) + + // with muxed accounts + allowTrust = AllowTrust{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Trustor: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Type: CreditAsset{"USD", ""}, + Authorize: true, + } + testOperationsMarshallingRoundtrip(t, []Operation{&allowTrust}, true) +} diff --git a/txnbuild/begin_sponsoring_future_reserves_test.go b/txnbuild/begin_sponsoring_future_reserves_test.go index bd93863610..bcacb96c1d 100644 --- a/txnbuild/begin_sponsoring_future_reserves_test.go +++ b/txnbuild/begin_sponsoring_future_reserves_test.go @@ -9,5 +9,5 @@ func TestBeginSponsoringFutureReservesRoundTrip(t *testing.T) { SponsoredID: newKeypair1().Address(), } - testOperationsMarshallingRoundtrip(t, []Operation{beginSponsoring}) + testOperationsMarshallingRoundtrip(t, []Operation{beginSponsoring}, false) } diff --git a/txnbuild/bump_sequence_test.go b/txnbuild/bump_sequence_test.go index 3f8d470fed..067cdd0b8f 100644 --- a/txnbuild/bump_sequence_test.go +++ b/txnbuild/bump_sequence_test.go @@ -27,3 +27,17 @@ func TestBumpSequenceValidate(t *testing.T) { assert.Contains(t, err.Error(), expected) } } + +func TestBumpSequenceRountrip(t *testing.T) { + bumpSequence := BumpSequence{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + BumpTo: 10, + } + testOperationsMarshallingRoundtrip(t, []Operation{&bumpSequence}, false) + + bumpSequence = BumpSequence{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + BumpTo: 10, + } + testOperationsMarshallingRoundtrip(t, []Operation{&bumpSequence}, true) +} diff --git a/txnbuild/change_trust_test.go b/txnbuild/change_trust_test.go index b33ad41f39..245d3caf06 100644 --- a/txnbuild/change_trust_test.go +++ b/txnbuild/change_trust_test.go @@ -78,3 +78,20 @@ func TestChangeTrustValidateInvalidLimit(t *testing.T) { assert.Contains(t, err.Error(), expected) } } + +func TestChangeTrustRoundtrip(t *testing.T) { + changeTrust := ChangeTrust{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Line: CreditAsset{"ABCD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}, + Limit: "1.0000000", + } + testOperationsMarshallingRoundtrip(t, []Operation{&changeTrust}, false) + + // with muxed accounts + changeTrust = ChangeTrust{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Line: CreditAsset{"ABCD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}, + Limit: "1.0000000", + } + testOperationsMarshallingRoundtrip(t, []Operation{&changeTrust}, true) +} diff --git a/txnbuild/claim_claimable_balance_test.go b/txnbuild/claim_claimable_balance_test.go index 5de9d25aa0..1a1e285292 100644 --- a/txnbuild/claim_claimable_balance_test.go +++ b/txnbuild/claim_claimable_balance_test.go @@ -6,8 +6,17 @@ import ( func TestClaimClaimableBalanceRoundTrip(t *testing.T) { claimClaimableBalance := &ClaimClaimableBalance{ - BalanceID: "00000000929b20b72e5890ab51c24f1cc46fa01c4f318d8d33367d24dd614cfdf5491072", + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + BalanceID: "00000000929b20b72e5890ab51c24f1cc46fa01c4f318d8d33367d24dd614cfdf5491072", } - testOperationsMarshallingRoundtrip(t, []Operation{claimClaimableBalance}) + testOperationsMarshallingRoundtrip(t, []Operation{claimClaimableBalance}, false) + + // with muxed accounts + claimClaimableBalance = &ClaimClaimableBalance{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + BalanceID: "00000000929b20b72e5890ab51c24f1cc46fa01c4f318d8d33367d24dd614cfdf5491072", + } + + testOperationsMarshallingRoundtrip(t, []Operation{claimClaimableBalance}, true) } diff --git a/txnbuild/clawback.go b/txnbuild/clawback.go index f3884f051e..7538b5aae9 100644 --- a/txnbuild/clawback.go +++ b/txnbuild/clawback.go @@ -58,7 +58,11 @@ func (cb *Clawback) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) { return xdr.Operation{}, errors.Wrap(err, "failed to build XDR Operation") } op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, cb.SourceAccount) + if withMuxedAccounts { + SetOpSourceMuxedAccount(&op, cb.SourceAccount) + } else { + SetOpSourceAccount(&op, cb.SourceAccount) + } return op, nil } diff --git a/txnbuild/clawback_claimable_balance_test.go b/txnbuild/clawback_claimable_balance_test.go index fd699a8637..9ff2914a0e 100644 --- a/txnbuild/clawback_claimable_balance_test.go +++ b/txnbuild/clawback_claimable_balance_test.go @@ -6,8 +6,17 @@ import ( func TestClawbackClaimableBalanceRoundTrip(t *testing.T) { claimClaimableBalance := &ClawbackClaimableBalance{ - BalanceID: "00000000929b20b72e5890ab51c24f1cc46fa01c4f318d8d33367d24dd614cfdf5491072", + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + BalanceID: "00000000929b20b72e5890ab51c24f1cc46fa01c4f318d8d33367d24dd614cfdf5491072", } - testOperationsMarshallingRoundtrip(t, []Operation{claimClaimableBalance}) + testOperationsMarshallingRoundtrip(t, []Operation{claimClaimableBalance}, false) + + // with muxed accounts + claimClaimableBalance = &ClawbackClaimableBalance{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + BalanceID: "00000000929b20b72e5890ab51c24f1cc46fa01c4f318d8d33367d24dd614cfdf5491072", + } + + testOperationsMarshallingRoundtrip(t, []Operation{claimClaimableBalance}, false) } diff --git a/txnbuild/clawback_test.go b/txnbuild/clawback_test.go index a7e329b997..dab757c5a3 100644 --- a/txnbuild/clawback_test.go +++ b/txnbuild/clawback_test.go @@ -83,10 +83,19 @@ func TestClawbackValidateAsset(t *testing.T) { func TestClawbackRoundTrip(t *testing.T) { clawback := Clawback{ - From: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", - Amount: "10.0000000", - Asset: CreditAsset{"USD", "GCXKG6RN4ONIEPCMNFB732A436Z5PNDSRLGWK7GBLCMQLIFO4S7EYWVU"}, + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + From: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Amount: "10.0000000", + Asset: CreditAsset{"USD", "GCXKG6RN4ONIEPCMNFB732A436Z5PNDSRLGWK7GBLCMQLIFO4S7EYWVU"}, } + testOperationsMarshallingRoundtrip(t, []Operation{&clawback}, false) - testOperationsMarshallingRoundtrip(t, []Operation{&clawback}) + // with muxed accounts + clawback = Clawback{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + From: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Amount: "10.0000000", + Asset: CreditAsset{"USD", "GCXKG6RN4ONIEPCMNFB732A436Z5PNDSRLGWK7GBLCMQLIFO4S7EYWVU"}, + } + testOperationsMarshallingRoundtrip(t, []Operation{&clawback}, true) } diff --git a/txnbuild/create_account_test.go b/txnbuild/create_account_test.go index 0e941d0068..b715dca70d 100644 --- a/txnbuild/create_account_test.go +++ b/txnbuild/create_account_test.go @@ -54,3 +54,21 @@ func TestCreateAccountValidateAmount(t *testing.T) { assert.Contains(t, err.Error(), expected) } } + +func TestCreateAccountRoundtrip(t *testing.T) { + createAccount := CreateAccount{ + SourceAccount: "GDYNXQFHU6W5RBW2CCCDDAAU3TMTSU2RMGIBM6HGHAR4NJJKY3IJETHT", + Destination: "GDYNXQFHU6W5RBW2CCCDDAAU3TMTSU2RMGIBM6HGHAR4NJJKY3IJETHT", + Amount: "1.0000000", + } + testOperationsMarshallingRoundtrip(t, []Operation{&createAccount}, false) + + // with muxed accounts + createAccount = CreateAccount{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Destination: "GDYNXQFHU6W5RBW2CCCDDAAU3TMTSU2RMGIBM6HGHAR4NJJKY3IJETHT", + Amount: "1.0000000", + } + testOperationsMarshallingRoundtrip(t, []Operation{&createAccount}, true) + +} diff --git a/txnbuild/create_claimable_balance_test.go b/txnbuild/create_claimable_balance_test.go index 49e95b9c64..d6516c7983 100644 --- a/txnbuild/create_claimable_balance_test.go +++ b/txnbuild/create_claimable_balance_test.go @@ -34,7 +34,19 @@ func TestCreateClaimableBalanceRoundTrip(t *testing.T) { }, } - testOperationsMarshallingRoundtrip(t, []Operation{createNativeBalance, createAssetBalance}) + testOperationsMarshallingRoundtrip(t, []Operation{createNativeBalance, createAssetBalance}, false) + + createNativeBalanceWithMuxedAcounts := &CreateClaimableBalance{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Amount: "1234.0000000", + Asset: NativeAsset{}, + Destinations: []Claimant{ + NewClaimant(newKeypair1().Address(), &UnconditionalPredicate), + NewClaimant(newKeypair1().Address(), &and), + }, + } + + testOperationsMarshallingRoundtrip(t, []Operation{createNativeBalanceWithMuxedAcounts}, true) } func TestClaimableBalanceID(t *testing.T) { diff --git a/txnbuild/end_sponsoring_future_reserves_test.go b/txnbuild/end_sponsoring_future_reserves_test.go index e11601f472..8b5ab5cdde 100644 --- a/txnbuild/end_sponsoring_future_reserves_test.go +++ b/txnbuild/end_sponsoring_future_reserves_test.go @@ -3,5 +3,8 @@ package txnbuild import "testing" func TestEndSponsoringFutureReservesRoundTrip(t *testing.T) { - testOperationsMarshallingRoundtrip(t, []Operation{&EndSponsoringFutureReserves{}}) + withoutMuxedAccounts := &EndSponsoringFutureReserves{SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"} + testOperationsMarshallingRoundtrip(t, []Operation{withoutMuxedAccounts}, false) + withMuxedAccounts := &EndSponsoringFutureReserves{SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK"} + testOperationsMarshallingRoundtrip(t, []Operation{withMuxedAccounts}, true) } diff --git a/txnbuild/inflation_test.go b/txnbuild/inflation_test.go new file mode 100644 index 0000000000..038dc10d08 --- /dev/null +++ b/txnbuild/inflation_test.go @@ -0,0 +1,16 @@ +package txnbuild + +import "testing" + +func TestInflationRoundtrip(t *testing.T) { + inflation := Inflation{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + } + testOperationsMarshallingRoundtrip(t, []Operation{&inflation}, false) + + // with muxed accounts + inflation = Inflation{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + } + testOperationsMarshallingRoundtrip(t, []Operation{&inflation}, true) +} diff --git a/txnbuild/manage_buy_offer_test.go b/txnbuild/manage_buy_offer_test.go index c12ef072e6..43e5230612 100644 --- a/txnbuild/manage_buy_offer_test.go +++ b/txnbuild/manage_buy_offer_test.go @@ -170,3 +170,26 @@ func TestManageBuyOfferPrice(t *testing.T) { assert.Equal(t, mbo.Price, parsed.Price) assert.Equal(t, mbo.price, parsed.price) } + +func TestManageBuyOfferRoundtrip(t *testing.T) { + manageBuyOffer := ManageBuyOffer{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Selling: CreditAsset{"USD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}, + Buying: NativeAsset{}, + Amount: "100.0000000", + Price: "0.01", + OfferID: 0, + } + testOperationsMarshallingRoundtrip(t, []Operation{&manageBuyOffer}, false) + + // with muxed accounts + manageBuyOffer = ManageBuyOffer{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Selling: CreditAsset{"USD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}, + Buying: NativeAsset{}, + Amount: "100.0000000", + Price: "0.01", + OfferID: 0, + } + testOperationsMarshallingRoundtrip(t, []Operation{&manageBuyOffer}, true) +} diff --git a/txnbuild/manage_data_test.go b/txnbuild/manage_data_test.go index a4303e0afe..9c8074371a 100644 --- a/txnbuild/manage_data_test.go +++ b/txnbuild/manage_data_test.go @@ -126,3 +126,20 @@ func TestManageDataRoundTrip(t *testing.T) { }) } } + +func TestManageDataRoundtrip(t *testing.T) { + manageData := ManageData{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Name: "foo", + Value: []byte("bar"), + } + testOperationsMarshallingRoundtrip(t, []Operation{&manageData}, false) + + // with muxed accounts + manageData = ManageData{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Name: "foo", + Value: []byte("bar"), + } + testOperationsMarshallingRoundtrip(t, []Operation{&manageData}, true) +} diff --git a/txnbuild/manage_offer_test.go b/txnbuild/manage_offer_test.go index 8d0f447240..87a978368c 100644 --- a/txnbuild/manage_offer_test.go +++ b/txnbuild/manage_offer_test.go @@ -166,3 +166,26 @@ func TestManageSellOfferPrice(t *testing.T) { assert.Equal(t, mso.Price, parsed.Price) assert.Equal(t, mso.price, parsed.price) } + +func TestManageSellOfferRoundtrip(t *testing.T) { + manageSellOffer := ManageSellOffer{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Selling: CreditAsset{"USD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}, + Buying: NativeAsset{}, + Amount: "100.0000000", + Price: "0.01", + OfferID: 0, + } + testOperationsMarshallingRoundtrip(t, []Operation{&manageSellOffer}, false) + + // with muxed accounts + manageSellOffer = ManageSellOffer{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Selling: CreditAsset{"USD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}, + Buying: NativeAsset{}, + Amount: "100.0000000", + Price: "0.01", + OfferID: 0, + } + testOperationsMarshallingRoundtrip(t, []Operation{&manageSellOffer}, true) +} diff --git a/txnbuild/operation_test.go b/txnbuild/operation_test.go index cac1a2e052..f8de64842d 100644 --- a/txnbuild/operation_test.go +++ b/txnbuild/operation_test.go @@ -445,16 +445,30 @@ func TestBumpSequenceFromXDR(t *testing.T) { } } -func testOperationsMarshallingRoundtrip(t *testing.T, operations []Operation) { +func testOperationsMarshallingRoundtrip(t *testing.T, operations []Operation, withMuxedAccounts bool) { kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) + accountID := xdr.MustAddress(kp1.Address()) + mx := xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xcafebabe, + Ed25519: *accountID.Ed25519, + }, + } + var sourceAccount SimpleAccount + if withMuxedAccounts { + sourceAccount = NewSimpleAccount(mx.Address(), int64(9605939170639898)) + } else { + sourceAccount = NewSimpleAccount(kp1.Address(), int64(9606132444168199)) + } tx, err := NewTransaction( TransactionParams{ - SourceAccount: &sourceAccount, - Operations: operations, - Timebounds: NewInfiniteTimeout(), - BaseFee: MinBaseFee, + SourceAccount: &sourceAccount, + Operations: operations, + Timebounds: NewInfiniteTimeout(), + BaseFee: MinBaseFee, + EnableMuxedAccounts: withMuxedAccounts, }, ) assert.NoError(t, err) @@ -464,7 +478,11 @@ func testOperationsMarshallingRoundtrip(t *testing.T, operations []Operation) { assert.NoError(t, err) var parsedTx *GenericTransaction - parsedTx, err = TransactionFromXDR(b64) + if withMuxedAccounts { + parsedTx, err = TransactionFromXDR(b64, TransactionFromXDROptionEnableMuxedAccounts) + } else { + parsedTx, err = TransactionFromXDR(b64) + } assert.NoError(t, err) var ok bool tx, ok = parsedTx.Transaction() diff --git a/txnbuild/path_payment_strict_send_test.go b/txnbuild/path_payment_strict_send_test.go index 66a79a7b58..3e3d73a01a 100644 --- a/txnbuild/path_payment_strict_send_test.go +++ b/txnbuild/path_payment_strict_send_test.go @@ -156,3 +156,28 @@ func TestPathPaymentStrictSendValidateDestAmount(t *testing.T) { assert.Contains(t, err.Error(), expected) } } + +func TestPathPaymentStrictSendRoundtrip(t *testing.T) { + pathPaymentStrictSend := PathPaymentStrictSend{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + SendAsset: NativeAsset{}, + SendAmount: "10.0000000", + Destination: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + DestAsset: CreditAsset{"ABCD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}, + DestMin: "1.0000000", + Path: []Asset{CreditAsset{"ABCD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}}, + } + testOperationsMarshallingRoundtrip(t, []Operation{&pathPaymentStrictSend}, false) + + // with muxed accounts + pathPaymentStrictSend = PathPaymentStrictSend{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + SendAsset: NativeAsset{}, + SendAmount: "10.0000000", + Destination: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + DestAsset: CreditAsset{"ABCD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}, + DestMin: "1.0000000", + Path: []Asset{CreditAsset{"ABCD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}}, + } + testOperationsMarshallingRoundtrip(t, []Operation{&pathPaymentStrictSend}, true) +} diff --git a/txnbuild/path_payment_test.go b/txnbuild/path_payment_test.go index e8af782046..f5f78fafa7 100644 --- a/txnbuild/path_payment_test.go +++ b/txnbuild/path_payment_test.go @@ -156,3 +156,28 @@ func TestPathPaymentValidateDestAmount(t *testing.T) { assert.Contains(t, err.Error(), expected) } } + +func TestPathPaymentRoundtrip(t *testing.T) { + pathPayment := PathPayment{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + SendAsset: NativeAsset{}, + SendMax: "10.0000000", + Destination: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + DestAsset: CreditAsset{"ABCD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}, + DestAmount: "1.0000000", + Path: []Asset{CreditAsset{"ABCD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}}, + } + testOperationsMarshallingRoundtrip(t, []Operation{&pathPayment}, false) + + // with muxed accounts + pathPayment = PathPayment{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + SendAsset: NativeAsset{}, + SendMax: "10.0000000", + Destination: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + DestAsset: CreditAsset{"ABCD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}, + DestAmount: "1.0000000", + Path: []Asset{CreditAsset{"ABCD", "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H"}}, + } + testOperationsMarshallingRoundtrip(t, []Operation{&pathPayment}, true) +} diff --git a/txnbuild/payment_test.go b/txnbuild/payment_test.go index f8e94daa92..e82cd7b499 100644 --- a/txnbuild/payment_test.go +++ b/txnbuild/payment_test.go @@ -80,3 +80,22 @@ func TestPaymentValidateAsset(t *testing.T) { assert.Contains(t, err.Error(), expected) } } + +func TestPaymentRoundtrip(t *testing.T) { + payment := Payment{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Destination: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Amount: "10.0000000", + Asset: NativeAsset{}, + } + testOperationsMarshallingRoundtrip(t, []Operation{&payment}, false) + + // with muxed accounts + payment = Payment{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Destination: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + Amount: "10.0000000", + Asset: NativeAsset{}, + } + testOperationsMarshallingRoundtrip(t, []Operation{&payment}, true) +} diff --git a/txnbuild/revoke_sponsorship_test.go b/txnbuild/revoke_sponsorship_test.go index 2cf0202073..46f6cb9ab3 100644 --- a/txnbuild/revoke_sponsorship_test.go +++ b/txnbuild/revoke_sponsorship_test.go @@ -98,7 +98,23 @@ func TestRevokeSponsorship(t *testing.T) { var op2 RevokeSponsorship assert.NoError(t, op2.FromXDR(xdrOp2, false)) assert.Equal(t, op, op2) - testOperationsMarshallingRoundtrip(t, []Operation{&testcase.op}) + testOperationsMarshallingRoundtrip(t, []Operation{&testcase.op}, false) }) } + + // without muxed accounts + revokeOp := RevokeSponsorship{ + SourceAccount: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + SponsorshipType: RevokeSponsorshipTypeAccount, + Account: &accountAddress, + } + testOperationsMarshallingRoundtrip(t, []Operation{&revokeOp}, false) + + // with muxed accounts + revokeOp = RevokeSponsorship{ + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + SponsorshipType: RevokeSponsorshipTypeAccount, + Account: &accountAddress, + } + testOperationsMarshallingRoundtrip(t, []Operation{&revokeOp}, true) } diff --git a/txnbuild/set_trustline_flags_test.go b/txnbuild/set_trustline_flags_test.go index 516b1a8d2d..dcf51144aa 100644 --- a/txnbuild/set_trustline_flags_test.go +++ b/txnbuild/set_trustline_flags_test.go @@ -88,7 +88,17 @@ func TestSetTrustLineFlags(t *testing.T) { var op2 SetTrustLineFlags assert.NoError(t, op2.FromXDR(xdrOp2, false)) assert.Equal(t, op, op2) - testOperationsMarshallingRoundtrip(t, []Operation{&testcase.op}) + testOperationsMarshallingRoundtrip(t, []Operation{&testcase.op}, false) }) } + + // with muxed accounts + setTrustLineFlags := SetTrustLineFlags{ + Trustor: trustor, + Asset: asset, + SetFlags: []TrustLineFlag{TrustLineClawbackEnabled}, + ClearFlags: []TrustLineFlag{TrustLineAuthorized, TrustLineAuthorizedToMaintainLiabilities}, + SourceAccount: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + } + testOperationsMarshallingRoundtrip(t, []Operation{&setTrustLineFlags}, true) } From 4bad42802d9fb7038241ef724981ad8ac57a5874 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Wed, 14 Apr 2021 14:27:05 +0200 Subject: [PATCH 21/22] Rename option in the changelog --- txnbuild/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/txnbuild/CHANGELOG.md b/txnbuild/CHANGELOG.md index 1740ce0fa9..ebb3054a46 100644 --- a/txnbuild/CHANGELOG.md +++ b/txnbuild/CHANGELOG.md @@ -17,7 +17,7 @@ file. This project adheres to [Semantic Versioning](http://semver.org/). * Add opt-in support for [SEP23](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0023.md) M-strkeys for `MuxedAccount`s: * Some methods from the `Operation` interface (`BuildXDR()`,`FromXDR()` and `Validate()`) now take an additional `bool` parameter (`withMuxedAccounts`) * The parameters from `NewFeeBumpTransaction()` and `NewTransaction()` now include a new field (`EnableMuxedAccounts`) to enable M-strekeys. - * `TransactionFromXDR()` now allows passing a `ParseXDROptionEnableMuxedAccounts` option, to enable M-strkey parsing. + * `TransactionFromXDR()` now allows passing a `TransactionFromXDROptionEnableMuxedAccounts` option, to enable M-strkey parsing. ## [v6.0.0](https://github.com/stellar/go/releases/tag/horizonclient-v6.0.0) - 2021-02-22 ### Breaking changes From 8caa7819399c1157c8b966e6d7b622c16db850c6 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Wed, 14 Apr 2021 19:18:52 +0200 Subject: [PATCH 22/22] Address review feedback --- txnbuild/CHANGELOG.md | 2 +- xdr/muxed_account.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/txnbuild/CHANGELOG.md b/txnbuild/CHANGELOG.md index ebb3054a46..094765565e 100644 --- a/txnbuild/CHANGELOG.md +++ b/txnbuild/CHANGELOG.md @@ -13,7 +13,7 @@ file. This project adheres to [Semantic Versioning](http://semver.org/). ### New features -* Add support for Stellar Protocol 16 (CAP35): `Clawback`, `ClawbackClaimableBalance` and `SetTrustlineFlags` operations. +* Add support for Stellar Protocol 17 (CAP35): `Clawback`, `ClawbackClaimableBalance` and `SetTrustlineFlags` operations. * Add opt-in support for [SEP23](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0023.md) M-strkeys for `MuxedAccount`s: * Some methods from the `Operation` interface (`BuildXDR()`,`FromXDR()` and `Validate()`) now take an additional `bool` parameter (`withMuxedAccounts`) * The parameters from `NewFeeBumpTransaction()` and `NewTransaction()` now include a new field (`EnableMuxedAccounts`) to enable M-strekeys. diff --git a/xdr/muxed_account.go b/xdr/muxed_account.go index 7a4a057977..5b6500a50a 100644 --- a/xdr/muxed_account.go +++ b/xdr/muxed_account.go @@ -9,7 +9,7 @@ import ( func MustMuxedAddress(address string) MuxedAccount { muxed := MuxedAccount{} - err := muxed.SetEd25519Address(address) + err := muxed.SetAddress(address) if err != nil { panic(err) }