diff --git a/CHANGELOG.md b/CHANGELOG.md index a9e4dc63e6..b966a9d61b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,29 +1,22 @@ # Changelog - -All notable changes to this project will be documented in this -file. This project adheres to [Semantic Versioning](http://semver.org/). - -As this project is pre 1.0, breaking changes may happen for minor version -bumps. A breaking change will get clearly notified in this log. - -NOTE: this changelog represents the changes that are associated with the library code in this repo (rather than the tools or services in this repo). - -## [Unreleased] - -### Added - -- xdr: added support for new signer types -- build: `Signer` learned support for new signer types -- strkey: added support for new signer types -- network: Added the `HashTransaction` helper func to get the hash of a transaction targeted to a specific stellar network. -- trades: Added Server-Sent Events endpoint to support streaming of trades -- trades: add `base_offer_id` and `counter_offer_id` to trade resources. -- trade aggregation: Added an optional `offset` parameter that lets you offset the bucket timestamps in hour-long increments. Can only be used if the `resolution` parameter is greater than 1 hour. `offset` must also be in whole-hours and less than 24 hours. - - -### Changed: - -- build: _BREAKING CHANGE_: A transaction built and signed using the `build` package no longer default to the test network. -- trades for offer endpoint will query for trades that match the given offer on either side of trades, rather than just the "sell" offer. - -[Unreleased]: https://github.com/stellar/go/commits/master \ No newline at end of file +This repository adheres to [Semantic Versioning](http://semver.org/). + +This monorepo contains a number of projects, individually versioned and released. Please consult the relevant changelog: + +* `horizon server` ([changelog](./services/horizon/CHANGELOG.md)) +* `horizonclient` ([changelog](./clients/horizonclient/CHANGELOG.md)) +* `txnbuild` ([changelog](./txnbuild/CHANGELOG.md)) +* `bridge` ([changelog](./services/bridge/CHANGELOG.md)) +* `compliance` ([changelog](./services/compliance/CHANGELOG.md)) +* `federation` ([changelog](./services/federation/CHANGELOG.md)) +* `bifrost` ([changelog](./services/bifrost/CHANGELOG.md)) +* `ticker` (experimental) ([changelog](./exp/ticker/CHANGELOG.md)) +* `stellar-vanity-gen` ([changelog](./tools/stellar-vanity-gen/CHANGELOG.md)) +* `stellar-sign` ([changelog](./tools/stellar-sign/CHANGELOG.md)) +* `stellar-archivist` ([changelog](./tools/stellar-archivist/CHANGELOG.md)) +* `stellar-hd-wallet` ([changelog](./tools/stellar-hd-wallet/CHANGELOG.md)) + +If a project is pre-v1.0, breaking changes may happen for minor version +bumps. A breaking change will be clearly notified in the corresponding changelog. + +Official project releases may be found here: https://github.com/stellar/go/releases diff --git a/README.md b/README.md index a9d124a8e7..7d25b07ae0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ This repo is the home for all of the public go code produced by SDF. In additio ## Package Index * [Horizon Server](services/horizon): Full-featured API server for Stellar network -* [Go Clients (Horizon SDK)](clients): Go SDK for making requests to Horizon Server +* [Go Horizon SDK - horizonclient](clients/horizonclient): Client for Horizon server (queries and transaction submission) +* [Go Horizon SDK - txnbuild](txnbuild): Construct Stellar transactions and operations * [Bifrost](services/bifrost): Bitcoin/Ethereum -> Stellar bridge * Servers for Anchors & Financial Institutions * [Bridge Server](services/bridge): send payments and take action when payments are received diff --git a/clients/README.md b/clients/README.md index 59a6090418..216862fb8b 100644 --- a/clients/README.md +++ b/clients/README.md @@ -1,11 +1,16 @@ # Clients package -Packages contained by this package provide client libraries for accessing the ecosystem of stellar services. At present, it only contains a simple horizon client library, but in the future it will contain clients to interact with stellar-core, federation, the bridge server and more. +Packages here provide client libraries for accessing the ecosystem of Stellar services. -See [godoc](https://godoc.org/github.com/stellar/go/clients) for details about each package. +* `horizonclient` - programmatic client access to Horizon (use in conjunction with [txnbuild](../txnbuild)) +* `stellartoml` - parse Stellar.toml files from the internet +* `federation` - resolve federation addresses into stellar account IDs, suitable for use within a transaction +* `horizon` (DEPRECATED) - the original Horizon client, now superceded by `horizonclient` -## Adding new client packages +See [GoDoc](https://godoc.org/github.com/stellar/go/clients) for more details. + +## For developers: Adding new client packages Ideally, each one of our client packages will have commonalities in their API to ease the cost of learning each. It's recommended that we follow a pattern similar to the `net/http` package's client shape: -A type, `Client`, is the central type of any client package, and its methods should provide the bulk of the functionality for the package. A `DefaultClient` var is provided for consumers that don't need client-level customization of behavior. Each method on the `Client` type should have a corresponding func at the package level that proxies a call through to the default client. For example, `http.Get()` is the equivalent of `http.DefaultClient.Get()`. \ No newline at end of file +A type, `Client`, is the central type of any client package, and its methods should provide the bulk of the functionality for the package. A `DefaultClient` var is provided for consumers that don't need client-level customization of behavior. Each method on the `Client` type should have a corresponding func at the package level that proxies a call through to the default client. For example, `http.Get()` is the equivalent of `http.DefaultClient.Get()`. diff --git a/clients/horizon/main.go b/clients/horizon/main.go index 4b350626fc..81fe6d6c3c 100644 --- a/clients/horizon/main.go +++ b/clients/horizon/main.go @@ -1,9 +1,6 @@ -// Package horizon provides client access to a horizon server, allowing an -// application to post transactions and lookup ledger information. +// Package horizon is DEPRECATED in favour of clients/horizonclient! It used to provide client access to a horizon +// server, allowing an application to post transactions and lookup ledger information. // -// Create an instance of `Client` to customize the server used, or alternatively -// use `DefaultTestNetClient` or `DefaultPublicNetClient` to access the SDF run -// horizon servers. // Deprecated: clients/horizon package with all its exported methods and variables will no longer be // maintained. It will be removed in future versions of the SDK. // Use clients/horizonclient (https://godoc.org/github.com/stellar/go/clients/horizonclient) instead. diff --git a/clients/horizonclient/README.md b/clients/horizonclient/README.md index 413d60c10c..4cc481383d 100644 --- a/clients/horizonclient/README.md +++ b/clients/horizonclient/README.md @@ -26,20 +26,20 @@ This library is aimed at developers building Go applications that interact with import hClient "github.com/stellar/go/clients/horizonclient" ... - // use the default pubnet client + // Use the default pubnet client client := hClient.DefaultPublicNetClient - // create an account request + + // Create an account request accountRequest := hClient.AccountRequest{AccountID: "GCLWGQPMKXQSPF776IU33AH4PZNOOWNAWGGKVTBQMIC5IMKUNP3E6NVU"} - // load the account detail from the network + // Load the account detail from the network account, err := client.AccountDetail(accountRequest) if err != nil { fmt.Println(err) return } - // account contains information about the stellar account + // Account contains information about the stellar account fmt.Print(account) - ``` For more examples, refer to the [documentation](https://godoc.org/github.com/stellar/go/clients/horizonclient). @@ -47,7 +47,9 @@ For more examples, refer to the [documentation](https://godoc.org/github.com/ste Run the unit tests from the package directory: `go test` ## Contributing -Please read [CONTRIBUTING](../../CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. +Please read [Code of Conduct](https://github.com/stellar/.github/blob/master/CODE_OF_CONDUCT.md) to understand this project's communication rules. + +To submit improvements and fixes to this library, please see [CONTRIBUTING](../CONTRIBUTING.md). ## License This project is licensed under the Apache License - see the [LICENSE](../../LICENSE-APACHE.txt) file for details. diff --git a/services/keystore/attachments/2019-04-24-keystore-auth-flows.png b/exp/services/keystore/attachments/2019-04-24-keystore-auth-flows.png similarity index 100% rename from services/keystore/attachments/2019-04-24-keystore-auth-flows.png rename to exp/services/keystore/attachments/2019-04-24-keystore-auth-flows.png diff --git a/services/keystore/spec.md b/exp/services/keystore/spec.md similarity index 87% rename from services/keystore/spec.md rename to exp/services/keystore/spec.md index 0d3802b4f8..a6a738a513 100644 --- a/services/keystore/spec.md +++ b/exp/services/keystore/spec.md @@ -65,7 +65,7 @@ interface EncryptedKeyData { Store Keys Request: ```typescript -interface StoreKeyRequest { +interface StoreKeysRequest { encryptedKeys: EncryptedKey[]; } ``` @@ -73,7 +73,7 @@ interface StoreKeyRequest { Store Keys Response: ```typescript -interface StoreKeyResponse { +interface StoreKeysResponse { encryptedKeys: EncryptedKeyData[]; } ``` @@ -144,6 +144,35 @@ TBD ``` +### /update-keys + +Update Keys Request: + +```typescript +interface UpdateKeysRequest { + encryptedKeys: EncryptedKey[]; +} +``` + +Update Keys Response: + +```typescript +interface UpdateKeysResponse { + encryptedKeys: EncryptedKeyData[]; +} +``` +
Errors + +TBD +```json +{ + "code": "some error code", + "message": "some error message", + "retriable": false, +} +``` +
+ ### /remove-key Remove Key Request: diff --git a/tools/horizon-cmp/CHANGELOG.md b/tools/horizon-cmp/CHANGELOG.md new file mode 100644 index 0000000000..8c32c333b7 --- /dev/null +++ b/tools/horizon-cmp/CHANGELOG.md @@ -0,0 +1,3 @@ +## 2019-04-25 + +Initial version diff --git a/tools/horizon-cmp/README.md b/tools/horizon-cmp/README.md new file mode 100644 index 0000000000..83654a8ab5 --- /dev/null +++ b/tools/horizon-cmp/README.md @@ -0,0 +1,6 @@ +# Horizon cmp + +Tool that compares the responses of two Horizon servers and shows the diffs. +Useful for checking for regressions. + +TODO: add more info diff --git a/tools/horizon-verify/CHANGELOG.md b/tools/horizon-verify/CHANGELOG.md new file mode 100644 index 0000000000..8c32c333b7 --- /dev/null +++ b/tools/horizon-verify/CHANGELOG.md @@ -0,0 +1,3 @@ +## 2019-04-25 + +Initial version diff --git a/tools/horizon-verify/README.md b/tools/horizon-verify/README.md new file mode 100644 index 0000000000..a04bad962e --- /dev/null +++ b/tools/horizon-verify/README.md @@ -0,0 +1,9 @@ +# Horizon verify + +Tool that checks some invariants about Horizon responses: + +- successful response codes when getting transactions from ledgers +- successful transaction counts are correct +- failed transaction counts are correct + +TODO: add more info diff --git a/txnbuild/CHANGELOG.md b/txnbuild/CHANGELOG.md new file mode 100644 index 0000000000..0f1022183d --- /dev/null +++ b/txnbuild/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +All notable changes to this project will be documented in this +file. This project adheres to [Semantic Versioning](http://semver.org/). + +## [v1.1.0](https://github.com/stellar/go/releases/tag/horizonclient-v1.1.0) - 2019-02-02 + +* Support for multiple signatures ([#1198](https://github.com/stellar/go/pull/1198)) + +## [v1.0.0](https://github.com/stellar/go/releases/tag/horizonclient-v1.0) - 2019-04-26 + +* Initial release diff --git a/txnbuild/README.md b/txnbuild/README.md index 9543975159..73c96e9cb7 100644 --- a/txnbuild/README.md +++ b/txnbuild/README.md @@ -4,7 +4,7 @@ This project is maintained by the Stellar Development Foundation. -``` +```golang import ( "log" @@ -79,7 +79,9 @@ To see the SDK in action, build and run the demo: ## Contributing -Please read [CONTRIBUTING](../../CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. +Please read [Code of Conduct](https://github.com/stellar/.github/blob/master/CODE_OF_CONDUCT.md) to understand this project's communication rules. + +To submit improvements and fixes to this library, please see [CONTRIBUTING](../CONTRIBUTING.md). ## License This project is licensed under the Apache License - see the [LICENSE](../../LICENSE-APACHE.txt) file for details. diff --git a/txnbuild/account_merge.go b/txnbuild/account_merge.go index 895b3bce39..356c580dd1 100644 --- a/txnbuild/account_merge.go +++ b/txnbuild/account_merge.go @@ -8,7 +8,8 @@ import ( // AccountMerge represents the Stellar merge account operation. See // https://www.stellar.org/developers/guides/concepts/list-of-operations.html type AccountMerge struct { - Destination string + Destination string + SourceAccount Account } // BuildXDR for AccountMerge returns a fully configured XDR Operation. @@ -22,6 +23,10 @@ func (am *AccountMerge) BuildXDR() (xdr.Operation, error) { opType := xdr.OperationTypeAccountMerge body, err := xdr.NewOperationBody(opType, xdrOp) - - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") + } + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, am.SourceAccount) + return op, nil } diff --git a/txnbuild/allow_trust.go b/txnbuild/allow_trust.go index 7af18a582c..407d2dad34 100644 --- a/txnbuild/allow_trust.go +++ b/txnbuild/allow_trust.go @@ -8,9 +8,10 @@ import ( // AllowTrust represents the Stellar allow trust operation. See // https://www.stellar.org/developers/guides/concepts/list-of-operations.html type AllowTrust struct { - Trustor string - Type Asset - Authorize bool + Trustor string + Type Asset + Authorize bool + SourceAccount Account } // BuildXDR for AllowTrust returns a fully configured XDR Operation. @@ -44,6 +45,10 @@ func (at *AllowTrust) BuildXDR() (xdr.Operation, error) { opType := xdr.OperationTypeAllowTrust body, err := xdr.NewOperationBody(opType, xdrOp) - - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") + } + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, at.SourceAccount) + return op, nil } diff --git a/txnbuild/bump_sequence.go b/txnbuild/bump_sequence.go index 70bb11a529..cb900d73ab 100644 --- a/txnbuild/bump_sequence.go +++ b/txnbuild/bump_sequence.go @@ -8,7 +8,8 @@ import ( // BumpSequence represents the Stellar bump sequence operation. See // https://www.stellar.org/developers/guides/concepts/list-of-operations.html type BumpSequence struct { - BumpTo int64 + BumpTo int64 + SourceAccount Account } // BuildXDR for BumpSequence returns a fully configured XDR Operation. @@ -16,6 +17,10 @@ func (bs *BumpSequence) BuildXDR() (xdr.Operation, error) { opType := xdr.OperationTypeBumpSequence xdrOp := xdr.BumpSequenceOp{BumpTo: xdr.SequenceNumber(bs.BumpTo)} body, err := xdr.NewOperationBody(opType, xdrOp) - - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") + } + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, bs.SourceAccount) + return op, nil } diff --git a/txnbuild/change_trust.go b/txnbuild/change_trust.go index 5f37aec990..13ef94e554 100644 --- a/txnbuild/change_trust.go +++ b/txnbuild/change_trust.go @@ -9,8 +9,9 @@ import ( // ChangeTrust represents the Stellar change trust operation. See // https://www.stellar.org/developers/guides/concepts/list-of-operations.html type ChangeTrust struct { - Line Asset - Limit string + Line Asset + Limit string + SourceAccount Account } // RemoveTrustlineOp returns a ChangeTrust operation to remove the trustline of the described asset, @@ -43,6 +44,10 @@ func (ct *ChangeTrust) BuildXDR() (xdr.Operation, error) { Limit: xdrLimit, } body, err := xdr.NewOperationBody(opType, xdrOp) - - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") + } + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, ct.SourceAccount) + return xdr.Operation{Body: body}, nil } diff --git a/txnbuild/cmd/demo/operations/demo.go b/txnbuild/cmd/demo/operations/demo.go index ae1120b01c..7a3e1c58d7 100644 --- a/txnbuild/cmd/demo/operations/demo.go +++ b/txnbuild/cmd/demo/operations/demo.go @@ -301,7 +301,10 @@ func deleteTrustline(source *hProtocol.Account, asset txnbuild.Asset, signer Acc } func deleteOffer(source *hProtocol.Account, offerID int64, signer Account) (string, error) { - deleteOffer := txnbuild.DeleteOfferOp(offerID) + deleteOffer, err := txnbuild.DeleteOfferOp(offerID) + if err != nil { + return "", errors.Wrap(err, "building offer") + } tx := txnbuild.Transaction{ SourceAccount: source, diff --git a/txnbuild/create_account.go b/txnbuild/create_account.go index 558c43dfa4..0182a6ee53 100644 --- a/txnbuild/create_account.go +++ b/txnbuild/create_account.go @@ -9,8 +9,9 @@ import ( // CreateAccount represents the Stellar create account operation. See // https://www.stellar.org/developers/guides/concepts/list-of-operations.html type CreateAccount struct { - Destination string - Amount string + Destination string + Amount string + SourceAccount Account } // BuildXDR for CreateAccount returns a fully configured XDR Operation. @@ -29,6 +30,10 @@ func (ca *CreateAccount) BuildXDR() (xdr.Operation, error) { opType := xdr.OperationTypeCreateAccount body, err := xdr.NewOperationBody(opType, xdrOp) - - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") + } + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, ca.SourceAccount) + return op, nil } diff --git a/txnbuild/create_passive_offer.go b/txnbuild/create_passive_offer.go index 05b2c7ef05..bb68be0697 100644 --- a/txnbuild/create_passive_offer.go +++ b/txnbuild/create_passive_offer.go @@ -10,10 +10,11 @@ import ( // CreatePassiveSellOffer represents the Stellar create passive offer operation. See // https://www.stellar.org/developers/guides/concepts/list-of-operations.html type CreatePassiveSellOffer struct { - Selling Asset - Buying Asset - Amount string - Price string + Selling Asset + Buying Asset + Amount string + Price string + SourceAccount Account } // BuildXDR for CreatePassiveSellOffer returns a fully configured XDR Operation. @@ -47,6 +48,10 @@ func (cpo *CreatePassiveSellOffer) BuildXDR() (xdr.Operation, error) { opType := xdr.OperationTypeCreatePassiveSellOffer body, err := xdr.NewOperationBody(opType, xdrOp) - - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") + } + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, cpo.SourceAccount) + return op, nil } diff --git a/txnbuild/example_test.go b/txnbuild/example_test.go index 547f067445..0ce3c7ee16 100644 --- a/txnbuild/example_test.go +++ b/txnbuild/example_test.go @@ -305,7 +305,8 @@ func ExampleManageSellOffer() { buying := CreditAsset{"ABCD", "GAS4V4O2B7DW5T7IQRPEEVCRXMDZESKISR7DVIGKZQYYV3OSQ5SH5LVP"} sellAmount := "100" price := "0.01" - op := CreateOfferOp(selling, buying, sellAmount, price) + op, err := CreateOfferOp(selling, buying, sellAmount, price) + check(err) tx := Transaction{ SourceAccount: &sourceAccount, @@ -329,7 +330,8 @@ func ExampleManageSellOffer_deleteOffer() { check(err) offerID := int64(2921622) - op := DeleteOfferOp(offerID) + op, err := DeleteOfferOp(offerID) + check(err) tx := Transaction{ SourceAccount: &sourceAccount, @@ -357,7 +359,8 @@ func ExampleManageSellOffer_updateOffer() { sellAmount := "50" price := "0.02" offerID := int64(2497628) - op := UpdateOfferOp(selling, buying, sellAmount, price, offerID) + op, err := UpdateOfferOp(selling, buying, sellAmount, price, offerID) + check(err) tx := Transaction{ SourceAccount: &sourceAccount, diff --git a/txnbuild/helpers_test.go b/txnbuild/helpers_test.go index 77d7881286..841ea27288 100644 --- a/txnbuild/helpers_test.go +++ b/txnbuild/helpers_test.go @@ -27,18 +27,14 @@ func newKeypair(seed string) *keypair.Full { return myKeypair.(*keypair.Full) } -func buildSignEncode(tx Transaction, kp *keypair.Full, t *testing.T) (txeBase64 string) { - var err error - err = tx.Build() - assert.NoError(t, err) - - err = tx.Sign(kp) - assert.NoError(t, err) +func buildSignEncode(t *testing.T, tx Transaction, kps ...*keypair.Full) string { + assert.NoError(t, tx.Build()) + assert.NoError(t, tx.Sign(kps...)) - txeBase64, err = tx.Base64() + txeBase64, err := tx.Base64() assert.NoError(t, err) - return + return txeBase64 } func check(err error) { diff --git a/txnbuild/inflation.go b/txnbuild/inflation.go index 31e90d713d..c8f9ee8116 100644 --- a/txnbuild/inflation.go +++ b/txnbuild/inflation.go @@ -7,12 +7,18 @@ import ( // Inflation represents the Stellar inflation operation. See // https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type Inflation struct{} +type Inflation struct { + SourceAccount Account +} // BuildXDR for Inflation returns a fully configured XDR Operation. func (inf *Inflation) BuildXDR() (xdr.Operation, error) { opType := xdr.OperationTypeInflation body, err := xdr.NewOperationBody(opType, nil) - - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") + } + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, inf.SourceAccount) + return op, nil } diff --git a/txnbuild/manage_data.go b/txnbuild/manage_data.go index 43a3a194f8..81177e9509 100644 --- a/txnbuild/manage_data.go +++ b/txnbuild/manage_data.go @@ -8,8 +8,9 @@ import ( // ManageData represents the Stellar manage data operation. See // https://www.stellar.org/developers/guides/concepts/list-of-operations.html type ManageData struct { - Name string - Value []byte + Name string + Value []byte + SourceAccount Account } // BuildXDR for ManageData returns a fully configured XDR Operation. @@ -26,6 +27,10 @@ func (md *ManageData) BuildXDR() (xdr.Operation, error) { opType := xdr.OperationTypeManageData body, err := xdr.NewOperationBody(opType, xdrOp) - - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") + } + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, md.SourceAccount) + return op, nil } diff --git a/txnbuild/manage_offer.go b/txnbuild/manage_offer.go index 048eed7ecb..f65bfaf699 100644 --- a/txnbuild/manage_offer.go +++ b/txnbuild/manage_offer.go @@ -8,52 +8,78 @@ import ( ) //CreateOfferOp returns a ManageSellOffer operation to create a new offer, by -// setting the OfferID to "0". -func CreateOfferOp(selling, buying Asset, amount, price string) ManageSellOffer { - return ManageSellOffer{ +// 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 ...Account) (ManageSellOffer, error) { + if len(sourceAccount) > 1 { + return ManageSellOffer{}, errors.New("offer can't have multiple source accounts") + } + offer := ManageSellOffer{ Selling: selling, Buying: buying, Amount: amount, Price: price, OfferID: 0, } + if len(sourceAccount) == 1 { + offer.SourceAccount = sourceAccount[0] + } + return offer, nil } -//UpdateOfferOp returns a ManageSellOffer operation to update an offer. -func UpdateOfferOp(selling, buying Asset, amount, price string, offerID int64) ManageSellOffer { - return ManageSellOffer{ +// UpdateOfferOp returns a ManageSellOffer operation to update an offer. +// The sourceAccount is optional, and if not provided, will be that of +// the surrounding transaction. +func UpdateOfferOp(selling, buying Asset, amount, price string, offerID int64, sourceAccount ...Account) (ManageSellOffer, error) { + if len(sourceAccount) > 1 { + return ManageSellOffer{}, errors.New("offer can't have multiple source accounts") + } + offer := ManageSellOffer{ Selling: selling, Buying: buying, Amount: amount, Price: price, OfferID: offerID, } + if len(sourceAccount) == 1 { + offer.SourceAccount = sourceAccount[0] + } + return offer, nil } //DeleteOfferOp returns a ManageSellOffer operation to delete an offer, by -// setting the Amount to "0". -func DeleteOfferOp(offerID int64) ManageSellOffer { +// 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 ...Account) (ManageSellOffer, error) { // It turns out Stellar core doesn't care about any of these fields except the amount. // However, Horizon will reject ManageSellOffer if it is missing fields. // Horizon will also reject if Buying == Selling. // Therefore unfortunately we have to make up some dummy values here. - return ManageSellOffer{ + if len(sourceAccount) > 1 { + return ManageSellOffer{}, errors.New("offer can't have multiple source accounts") + } + offer := ManageSellOffer{ Selling: NativeAsset{}, Buying: CreditAsset{Code: "FAKE", Issuer: "GBAQPADEYSKYMYXTMASBUIS5JI3LMOAWSTM2CHGDBJ3QDDPNCSO3DVAA"}, Amount: "0", Price: "1", OfferID: offerID, } + if len(sourceAccount) == 1 { + offer.SourceAccount = sourceAccount[0] + } + return offer, nil } // ManageSellOffer represents the Stellar manage offer operation. See // https://www.stellar.org/developers/guides/concepts/list-of-operations.html type ManageSellOffer struct { - Selling Asset - Buying Asset - Amount string - Price string - OfferID int64 + Selling Asset + Buying Asset + Amount string + Price string + OfferID int64 + SourceAccount Account } // BuildXDR for ManageSellOffer returns a fully configured XDR Operation. @@ -87,6 +113,11 @@ func (mo *ManageSellOffer) BuildXDR() (xdr.Operation, error) { OfferId: xdr.Int64(mo.OfferID), } body, err := xdr.NewOperationBody(opType, xdrOp) + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") + } - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, mo.SourceAccount) + return op, nil } diff --git a/txnbuild/operation.go b/txnbuild/operation.go index 56af68102a..8f835351cd 100644 --- a/txnbuild/operation.go +++ b/txnbuild/operation.go @@ -8,3 +8,13 @@ import ( type Operation interface { BuildXDR() (xdr.Operation, error) } + +// SetOpSourceAccount sets the source account ID on an Operation. +func SetOpSourceAccount(op *xdr.Operation, sourceAccount Account) { + if sourceAccount == nil { + return + } + var opSourceAccountID xdr.AccountId + opSourceAccountID.SetAddress(sourceAccount.GetAccountID()) + op.SourceAccount = &opSourceAccountID +} diff --git a/txnbuild/path_payment.go b/txnbuild/path_payment.go index eab706731f..f330e0b223 100644 --- a/txnbuild/path_payment.go +++ b/txnbuild/path_payment.go @@ -9,12 +9,13 @@ import ( // PathPayment represents the Stellar path payment operation. See // https://www.stellar.org/developers/guides/concepts/list-of-operations.html type PathPayment struct { - SendAsset Asset - SendMax string - Destination string - DestAsset Asset - DestAmount string - Path []Asset + SendAsset Asset + SendMax string + Destination string + DestAsset Asset + DestAmount string + Path []Asset + SourceAccount Account } // BuildXDR for Payment returns a fully configured XDR Operation. @@ -77,6 +78,10 @@ func (pp *PathPayment) BuildXDR() (xdr.Operation, error) { Path: xdrPath, } body, err := xdr.NewOperationBody(opType, xdrOp) - - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") + } + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, pp.SourceAccount) + return op, nil } diff --git a/txnbuild/payment.go b/txnbuild/payment.go index f77417028f..2efd8edcf5 100644 --- a/txnbuild/payment.go +++ b/txnbuild/payment.go @@ -9,9 +9,10 @@ import ( // Payment represents the Stellar payment operation. See // https://www.stellar.org/developers/guides/concepts/list-of-operations.html type Payment struct { - Destination string - Amount string - Asset Asset + Destination string + Amount string + Asset Asset + SourceAccount Account } // BuildXDR for Payment returns a fully configured XDR Operation. @@ -43,6 +44,10 @@ func (p *Payment) BuildXDR() (xdr.Operation, error) { Asset: xdrAsset, } body, err := xdr.NewOperationBody(opType, xdrOp) - - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR Operation") + } + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, p.SourceAccount) + return op, nil } diff --git a/txnbuild/set_options.go b/txnbuild/set_options.go index e20839bdbe..d4ca81b214 100644 --- a/txnbuild/set_options.go +++ b/txnbuild/set_options.go @@ -60,6 +60,7 @@ type SetOptions struct { HomeDomain *string Signer *Signer xdrOp xdr.SetOptionsOp + SourceAccount Account } // BuildXDR for SetOptions returns a fully configured XDR Operation. @@ -86,8 +87,13 @@ func (so *SetOptions) BuildXDR() (xdr.Operation, error) { opType := xdr.OperationTypeSetOptions body, err := xdr.NewOperationBody(opType, so.xdrOp) + if err != nil { + return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") + } - return xdr.Operation{Body: body}, errors.Wrap(err, "failed to build XDR OperationBody") + op := xdr.Operation{Body: body} + SetOpSourceAccount(&op, so.SourceAccount) + return op, nil } // handleInflation for SetOptions sets the XDR inflation destination. diff --git a/txnbuild/signers_test.go b/txnbuild/signers_test.go new file mode 100644 index 0000000000..a99634e6de --- /dev/null +++ b/txnbuild/signers_test.go @@ -0,0 +1,364 @@ +package txnbuild + +import ( + "testing" + + "github.com/stellar/go/network" + "github.com/stretchr/testify/assert" +) + +func TestAccountMergeMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + accountMerge := AccountMerge{ + Destination: "GAS4V4O2B7DW5T7IQRPEEVCRXMDZESKISR7DVIGKZQYYV3OSQ5SH5LVP", + SourceAccount: &opSourceAccount, + } + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&accountMerge}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAIAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAAAAAALqLnLFAAAAQAES8MwTufP7l2Rlbg4+1klxAeGgSyTb+vdGI7Or/Lp5xHGZwQ/KvWo0W1ot4hy+WkdJBCD1VF53skB4ZYTPFAnSh2R+AAAAQGPvZk8T2GDp2BpYGeS85VAV2UGKzyjGowt+YOfJwKbW5fjo+GLe47obXEEYxCQDZIsmwG4u5tJ9FUbjuvqi/g0=" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestAllowTrustMultSigners(t *testing.T) { + kp0 := newKeypair0() + opSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + txSourceAccount := makeTestAccount(kp1, "9606132444168199") + + issuedAsset := CreditAsset{"ABCD", kp1.Address()} + allowTrust := AllowTrust{ + Trustor: kp1.Address(), + Type: issuedAsset, + Authorize: true, + SourceAccount: &opSourceAccount, + } + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&allowTrust}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAiILoAAAAIAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAA4Nxt4XJcrGZRYrUvrOc1sooiQ+QdEk1suS1wo+oucsUAAAAHAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAUFCQ0QAAAABAAAAAAAAAALqLnLFAAAAQHm+8kcSuOMVfthbNRu5ItzonA0ACvL58h4lC6K0JG6OCSR5gRbLUOMqVu1xpQZu+6t9pHwKN9QoEPoXviT3rgDSh2R+AAAAQCr0qzbX9xroeFOzliJgb7+dZJEjyZMpmF3b90NwlEWtm4KPu+U2Lvr91ImeOYtt1/UGksDlGC+3aFq3FsbKBg8=" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestBumpSequenceMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + bumpSequence := BumpSequence{ + BumpTo: 9606132444168300, + SourceAccount: &opSourceAccount, + } + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&bumpSequence}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAALACIgugAAAGwAAAAAAAAAAuoucsUAAABA5wbLXDFQdTkJ0Oo3mkW6VrcFeylOag0urj6lKXaQV3mGdFQA4J9OezChx5DynW+FxQtuyXbSBYTcgXUADapSCdKHZH4AAABAuuYmmuuwkMBGC3oX4RA6ZkM5PfYrdUuuAhEvOnuanfyynrOgD/RPs0ROOpd7PAOuZiSkWlJZPUCaJTCo8QZdDg==" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestChangeTrustMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + changeTrust := ChangeTrust{ + Line: CreditAsset{"ABCD", kp0.Address()}, + Limit: "10", + SourceAccount: &opSourceAccount, + } + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&changeTrust}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAUFCQ0QAAAAA4Nxt4XJcrGZRYrUvrOc1sooiQ+QdEk1suS1wo+oucsUAAAAABfXhAAAAAAAAAAAC6i5yxQAAAEBGh9ocFFyY4gH19pd+mVn6cUbOxlp6K4e3zNHfYd/WJ22nqpD89FBDa+iuSQGGpeqEGWELdiMqY6lYWsN2sisD0odkfgAAAEDXG99N0TmrMCyg7OixF0COempqsfraKGbRnQBtYHeH4ZauJzm81kshSRnHGenlrWM0KzMtevfeLGBFnA1Y/s4I" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestCreateAccountMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + createAccount := CreateAccount{ + Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", + Amount: "10", + SourceAccount: &opSourceAccount, + } + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&createAccount}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAuoucsUAAABANXxsuIht++BXo21iiKkj0lrhVCYNdbD/uBPbL7AXKoleT1cynaR7luA74npsMfzE9AVFr+VclOY+dzQBqIWZDNKHZH4AAABA0EQ/a/U49VgXN6kAHnxMIfy/7rATGCk+stqym2Pa6fcbIKIFyoTRVi+uPTkIcS0u1wL1FvkWuU4YbfbtUPJ5Aw==" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestCreatePassiveSellOfferMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + createPassiveOffer := CreatePassiveSellOffer{ + Selling: NativeAsset{}, + Buying: CreditAsset{"ABCD", kp0.Address()}, + Amount: "10", + Price: "1.0", + SourceAccount: &opSourceAccount, + } + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&createPassiveOffer}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAEAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAX14QAAAAABAAAAAQAAAAAAAAAC6i5yxQAAAEDQA9vz3Yvc1g/xjJdkyiegE5gw4y1RmGmM6d9Kd+i7FD+i0WdGyzkxf9GjrDprUQj1/iDFGE2HpYOb5Zd5UUcP0odkfgAAAECq+9bggD7neBxaDYO4kxR/ltLjqBucqqAbYcIY7bwnGy32Ca/jvsglwnU2UgX3qhCEHSshN21bsGI/h5f+xcUH" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestInflationMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + inflation := Inflation{ + SourceAccount: &opSourceAccount, + } + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&inflation}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAJAAAAAAAAAALqLnLFAAAAQA10jZSBnnrqNR6RH6tbivfJropBxXa2KcH2yN9J3mBGoIxgBSyBCYEc9qKWnkrmZUGvmbTO+LNOd+PdQ4Y8CAPSh2R+AAAAQJ/b1BbMNzsXMGqGELrXH8bcEM5nXa+jq06xrMnIbgMRNRsj+NhSyKifzWP0PQQvHQ4C2yw6y+HBfWvwzAFPhQE=" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestManageDataMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + manageData := ManageData{ + Name: "Fruit preference", + Value: []byte("Apple"), + SourceAccount: &opSourceAccount, + } + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&manageData}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAKAAAAEEZydWl0IHByZWZlcmVuY2UAAAABAAAABUFwcGxlAAAAAAAAAAAAAALqLnLFAAAAQLCtVrhLtpfRGrFhiiGJq891ewFu6ju63po2fgjnYHgA2MgSFJ5rRo3H09uIyWpcmEeeBwXihnx6Ahh9R/pDng7Sh2R+AAAAQOSrmSI0GjSDSgp0+RFwrZYBgqjVtEE5aeCebZ3KR0JCEYXwzIG/q9t1WGz5/zyd8BmmqI1+rP0R6QSp1kDyhwo=" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestManageOfferCreateMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + selling := NativeAsset{} + buying := CreditAsset{"ABCD", kp0.Address()} + sellAmount := "100" + price := "0.01" + createOffer, err := CreateOfferOp(selling, buying, sellAmount, price, &opSourceAccount) + check(err) + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&createOffer}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAADAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAADuaygAAAAABAAAAZAAAAAAAAAAAAAAAAAAAAALqLnLFAAAAQBo6hZfPKEWgmpWi/TCZonjsQz/w3TCCg2Qcn218b/vCq6OjTezTukCJzJZuhEI7k/STp1/dEptolP9ysGsqegjSh2R+AAAAQLAcnq3rskk4p7shyvfRLuNnK1XgOnVtvho24UW6pqflv+wRaVWJg7Vp848Gi5bBFB8mPJRYMa3lbL78n4wYJA0=" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestManageOfferDeleteMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + offerID := int64(2921622) + deleteOffer, err := DeleteOfferOp(offerID, &opSourceAccount) + check(err) + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&deleteOffer}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAADAAAAAAAAAAFGQUtFAAAAAEEHgGTElYZi82AkGiJdSja2OBaU2aEcwwp3AY3tFJ2xAAAAAAAAAAAAAAABAAAAAQAAAAAALJSWAAAAAAAAAALqLnLFAAAAQFp2K2fnu4AY2GsFb5BL8b8uDwCoHvcdHi+e7oedEp524sJCws4nPwEWLFu5DJcMqLCvFkr5UgwtQwFrNJmMqwbSh2R+AAAAQLtQ2CiOJOG5OsmWydFWFUads6QJj51RcJbJb0mCyDewWBpZRLmh45IMyMRlMJoxk8wKJK4UR2Sfolz7aMmjrw4=" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestManageOfferUpdateMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + selling := NativeAsset{} + buying := CreditAsset{"ABCD", kp0.Address()} + sellAmount := "50" + price := "0.02" + offerID := int64(2497628) + updateOffer, err := UpdateOfferOp(selling, buying, sellAmount, price, offerID, &opSourceAccount) + check(err) + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&updateOffer}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAADAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAB3NZQAAAAABAAAAMgAAAAAAJhxcAAAAAAAAAALqLnLFAAAAQOEvekP35V6i3XzbXLdxC5BHFg1pQkhC35KXHJKDYdXwGb5YjHh5amYL78JLtrmswu7NbpWz3MY/rbxFn+8I3gHSh2R+AAAAQLU+5Xee25nSTbRJnWx5zCtkIQ7KzDnb/V/r9nizHsizneito26JQqeEKBH/qz88d3kQxWWC4Lf053tPD6d+SA0=" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestPathPaymentMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + abcdAsset := CreditAsset{"ABCD", kp0.Address()} + pathPayment := PathPayment{ + SendAsset: NativeAsset{}, + SendMax: "10", + Destination: kp0.Address(), + DestAsset: NativeAsset{}, + DestAmount: "1", + Path: []Asset{abcdAsset}, + SourceAccount: &opSourceAccount, + } + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&pathPayment}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAACAAAAAAAAAAAF9eEAAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAAAAAAAmJaAAAAAAQAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAAAAALqLnLFAAAAQKYqPzdMAYo7NrrOOE2HnXRzCIBixIT9jWteNysju07WVcGpJhoLJW597UrMlsRVWLB/QJk6e6jw6SzLRDXw5wLSh2R+AAAAQMz9ZqejVTk9KiWZCv0e/hoW+F4ua2mM6tHV/kuzCB9HqVGglbK9xN0aOGnrQwvwlp824cVOYnUkV8+HfwsnQgM=" + + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestPaymentMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + payment := Payment{ + Destination: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", + Amount: "10", + Asset: NativeAsset{}, + SourceAccount: &opSourceAccount, + } + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&payment}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAABAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAAF9eEAAAAAAAAAAALqLnLFAAAAQHzYkZeogiHztanqRvrXXxiNShH/Zf5EUjgabrb6wwgX1eOUBRjp5J92qq8s/o1B1sxrMNiPpViAq40tD/yGfwjSh2R+AAAAQNVC6YLIbAnFs3G/rdf7IxrWYFOxjOKUSZsN0q1Bm/MXk+7ydhcCbYBgq+VGa6eZf8BckgIdAtDI8VNWPoTyhAM=" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} + +func TestSetOptionsMultSigners(t *testing.T) { + kp0 := newKeypair0() + txSourceAccount := makeTestAccount(kp0, "9605939170639898") + + kp1 := newKeypair1() + opSourceAccount := makeTestAccount(kp1, "9606132444168199") + + setOptions := SetOptions{ + SetFlags: []AccountFlag{AuthRequired, AuthRevocable}, + SourceAccount: &opSourceAccount, + } + + tx := Transaction{ + SourceAccount: &txSourceAccount, + Operations: []Operation{&setOptions}, + Timebounds: NewInfiniteTimeout(), + Network: network.TestNetworkPassphrase, + } + + received := buildSignEncode(t, tx, kp0, kp1) + expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAFAAAAAAAAAAAAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6i5yxQAAAEA7Wgrkr6q1o1Cf9rzfopqkIUQWD9Se3TagU2GhMn9OjGT75flGAaOdQ+kHLDGQjThDKMMdB8jCJGe8IGc/dIQP0odkfgAAAEDni8seENXmyh0QgHkLjM4EmhHmBr5NvU6VpJaVBfv631yaaHP7lONfg9x8DyHjz8uh03S7ipShHIrQDFN+L+cM" + assert.Equal(t, expected, received, "Base 64 XDR should match") +} diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index b2537de9f2..0f44162a6e 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -128,7 +128,7 @@ func (tx *Transaction) Build() error { // Sign for Transaction signs a previously built transaction. A signed transaction may be // submitted to the network. -func (tx *Transaction) Sign(kp *keypair.Full) error { +func (tx *Transaction) Sign(kps ...*keypair.Full) error { // TODO: Only sign if Transaction has been previously built // TODO: Validate network set before sign // Initialise transaction envelope @@ -144,27 +144,27 @@ func (tx *Transaction) Sign(kp *keypair.Full) error { } // Sign the hash - // TODO: Allow multiple signers - sig, err := kp.SignDecorated(hash[:]) - if err != nil { - return errors.Wrap(err, "failed to sign transaction") + for _, kp := range kps { + sig, err := kp.SignDecorated(hash[:]) + if err != nil { + return errors.Wrap(err, "failed to sign transaction") + } + // Append the signature to the envelope + tx.xdrEnvelope.Signatures = append(tx.xdrEnvelope.Signatures, sig) } - // Append the signature to the envelope - tx.xdrEnvelope.Signatures = append(tx.xdrEnvelope.Signatures, sig) - return nil } // BuildSignEncode performs all the steps to produce a final transaction suitable // for submitting to the network. -func (tx *Transaction) BuildSignEncode(keypair *keypair.Full) (string, error) { +func (tx *Transaction) BuildSignEncode(keypairs ...*keypair.Full) (string, error) { err := tx.Build() if err != nil { return "", errors.Wrap(err, "couldn't build transaction") } - err = tx.Sign(keypair) + err = tx.Sign(keypairs...) if err != nil { return "", errors.Wrap(err, "couldn't sign transaction") } diff --git a/txnbuild/transaction_test.go b/txnbuild/transaction_test.go index f07eb5039c..479312546a 100644 --- a/txnbuild/transaction_test.go +++ b/txnbuild/transaction_test.go @@ -32,7 +32,7 @@ func TestInflation(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) // https://www.stellar.org/laboratory/#xdr-viewer?input=AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAHqLnLFAAAAQP3NHWXvzKIHB3%2BjjhHITdc%2FtBPntWYj3SoTjpON%2BdxjKqU5ohFamSHeqi5ONXkhE9Uajr5sVZXjQfUcTTzsWAA%3D&type=TransactionEnvelope expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAHqLnLFAAAAQP3NHWXvzKIHB3+jjhHITdc/tBPntWYj3SoTjpON+dxjKqU5ohFamSHeqi5ONXkhE9Uajr5sVZXjQfUcTTzsWAA=" assert.Equal(t, expected, received, "Base 64 XDR should match") @@ -54,7 +54,7 @@ func TestCreateAccount(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAaAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAeoucsUAAABAHsyMojA0Q5MiNsR5X5AiNpCn9mlXmqluRsNpTniCR91M4U5TFmrrqVNLkU58/l+Y8hUPwidDTRSzLZKbMUL/Bw==" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -76,7 +76,7 @@ func TestPayment(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAAF9eEAAAAAAAAAAAHqLnLFAAAAQNcGQpjNOFCLf9eEmobN+H8SNoDH/jMrfEFPX8kM212ST+TGfirEdXH77GJXvaWplfGKmE3B+UDwLuYLwO+KbQQ=" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -117,7 +117,7 @@ func TestBumpSequence(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp1, t) + received := buildSignEncode(t, tx, kp1) expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAiILoAAAAIAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAALACIgugAAAGwAAAAAAAAAAdKHZH4AAABAndjSSWeACpbr0ROAEK6jw5CzHiL/rCDpa6AO05+raHDowSUJBckkwlEuCjbBoO/A06tZNRT1Per3liTQrc8fCg==" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -137,7 +137,7 @@ func TestAccountMerge(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAALAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAIAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAAAAAAHqLnLFAAAAQJ/UcOgE64+GQpwv0uXXa2jrKtFdmDsyZ6ZZ/udxryPS8cNCm2L784ixPYM4XRgkoQCdxC3YK8n5x5+CXLzrrwA=" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -158,7 +158,7 @@ func TestManageData(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) // https://www.stellar.org/laboratory/#txsigner?xdr=AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAKAAAAEEZydWl0IHByZWZlcmVuY2UAAAABAAAABUFwcGxlAAAAAAAAAAAAAAA%3D expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAKAAAAEEZydWl0IHByZWZlcmVuY2UAAAABAAAABUFwcGxlAAAAAAAAAAAAAAHqLnLFAAAAQO1ELJBEoqBDyIsS7uSJwe1LOimV/E+09MyF1G/+yrxSggFVPEjD5LXcm/6POze3IsMuIYJU1et5Q2Vt9f73zQo=" assert.Equal(t, expected, received, "Base 64 XDR should match") @@ -179,7 +179,7 @@ func TestManageDataRemoveDataEntry(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAWAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAKAAAAEEZydWl0IHByZWZlcmVuY2UAAAAAAAAAAAAAAAHqLnLFAAAAQB8rkFZgtffUTdCASzwJ3jRcMCzHpVbbuFbye7Ki2dLao6u5d2aSzz3M2ugNJjNFMfSu3io9adCqwVKKjk0UJQA=" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -200,7 +200,7 @@ func TestSetOptionsInflationDestination(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAcAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAQAAAAAlyvHaD8duz+iEXkJUUbsHkklIlH46oMrMMYrt0odkfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHqLnLFAAAAQB0RLe9DjdHzLM22whFja3ZT97L/818lvWpk5EOTETr9lmDH7/A0/EAzeCkTBzZMCi3C6pV1PrGBr0NJdRrPowg=" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -220,7 +220,7 @@ func TestSetOptionsSetFlags(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAfAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB6i5yxQAAAECfYTppxtp1A2zSbb6VzkOkyk9D/7xjaXRxR+ZIqgdK3lWkHQRkjyVBj2yaI61J3trdp7CswImptjkjLprt0WIO" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -240,7 +240,7 @@ func TestSetOptionsClearFlags(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAgAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB6i5yxQAAAEANXPAN+RgvqjGF0kJ6MyNTiMnWaELw5vYNwxhv8+mi3KmGWMzojCxcmMAqni0zBMsEjl9z7H8JT9x05OlQ9nsD" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -260,7 +260,7 @@ func TestSetOptionsMasterWeight(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAhAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAQAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB6i5yxQAAAECIxH2W4XZ5fMsG658hdIEys2nlVSAK1FEjT5GADF6sWEThGFc+Wrmlw6GwKn6ZNAmxVULEgircjQx48aYSgFYD" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -282,7 +282,7 @@ func TestSetOptionsThresholds(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAjAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAIAAAABAAAAAgAAAAAAAAAAAAAAAAAAAAHqLnLFAAAAQFwRcFbzEtxoxZOtWlOQld3nURHZugNj5faEncpv0X/dcrfiQVU7k3fkTYDskiVExFiq78CBsYAr0uuvfH61IQs=" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -302,7 +302,7 @@ func TestSetOptionsHomeDomain(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAmAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAcTG92ZWx5THVtZW5zTG9va0x1bWlub3VzLmNvbQAAAAAAAAAAAAAAAeoucsUAAABAtC4HZzvRfyphRg5jjmz5jzBn86SANXCZS59GejRE8L1uCOxgXSEVoh1b+UetUEi7JN/n1ECBEVJrXgj0c34eBg==" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -342,7 +342,7 @@ func TestSetOptionsSigner(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAmAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAABAAAAAAAAAAB6i5yxQAAAEBfgmUK+wNj8ROz78Sg0rQ2s7lmtvA4r5epHkqc9yoxLDr/GSkmgWneVqoKNxWF0JB9L+Gql1+f8M8p1McF4MsB" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -363,7 +363,7 @@ func TestMultipleOperations(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp1, t) + received := buildSignEncode(t, tx, kp1) expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAyAAiILoAAAAIAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAAsAIiC6AAAAbAAAAAAAAAAB0odkfgAAAEDmf3Ag2Hw5NdlvzJpph4Km+aNKy8kfzS1EAhIVdKJwUnMVWhOpfdXSh/aekEVdoxXh2+ioocrxdtkWAZfS3sMF" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -385,7 +385,7 @@ func TestChangeTrust(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAA9AAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAUFCQ0QAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAABfXhAAAAAAAAAAAB6i5yxQAAAED7YSd1VdewEdtEURAYuyCy8dWbzALEf1vJn88/gCER4CNdIvojOEafJEhYhzZJhdG7oa+95UjfI9vMJO8qdWMK" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -426,7 +426,7 @@ func TestChangeTrustDeleteTrustline(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAABDAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAUFCQ0QAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAAAAAAAAAAAAAAAAAB6i5yxQAAAECgd2wkK35civvf6NKpsSFDyKpdyo/cs7wL+RYfZ2BCP7eGrUUpu2GfQFtf/Hm6aBwT6nJ+dONTSPXnyp7Dq18L" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -450,7 +450,7 @@ func TestAllowTrust(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp0, t) + received := buildSignEncode(t, tx, kp0) expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAABPAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAHAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAUFCQ0QAAAABAAAAAAAAAAHqLnLFAAAAQGGBSKitYxpHNMaVVOE2CIylWFJgwqxjhwnIvWauSSkLapntD18G1pMahLbs8Lqcr3+cEs5WjLI4eBhy6WiJhAk=" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -464,7 +464,8 @@ func TestManageSellOfferNewOffer(t *testing.T) { buying := CreditAsset{"ABCD", kp0.Address()} sellAmount := "100" price := "0.01" - createOffer := CreateOfferOp(selling, buying, sellAmount, price) + createOffer, err := CreateOfferOp(selling, buying, sellAmount, price) + check(err) tx := Transaction{ SourceAccount: &sourceAccount, @@ -473,7 +474,7 @@ func TestManageSellOfferNewOffer(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp1, t) + received := buildSignEncode(t, tx, kp1) expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAAFAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAADuaygAAAAABAAAAZAAAAAAAAAAAAAAAAAAAAAHSh2R+AAAAQAmXf4BnH8bWhy+Tnxf+7zgsij7pV0b7XC4rqfYWi9ZIVUaidWPbrFhaWjiQbXYB1NKdx0XjidzkcAgMInLqDgs=" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -483,7 +484,8 @@ func TestManageSellOfferDeleteOffer(t *testing.T) { sourceAccount := makeTestAccount(kp1, "41137196761105") offerID := int64(2921622) - deleteOffer := DeleteOfferOp(offerID) + deleteOffer, err := DeleteOfferOp(offerID) + check(err) tx := Transaction{ SourceAccount: &sourceAccount, @@ -492,7 +494,7 @@ func TestManageSellOfferDeleteOffer(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp1, t) + received := buildSignEncode(t, tx, kp1) expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAASAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAFGQUtFAAAAAEEHgGTElYZi82AkGiJdSja2OBaU2aEcwwp3AY3tFJ2xAAAAAAAAAAAAAAABAAAAAQAAAAAALJSWAAAAAAAAAAHSh2R+AAAAQBSjRfpyEAIMnRQOPf1BBOx8HFC6Lm6bxxdljaegnUts8SmWJGQbZN5a8PQGzOTwGdBKBk9X9d+BIrBVc3kyyQ4=" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -507,7 +509,8 @@ func TestManageSellOfferUpdateOffer(t *testing.T) { sellAmount := "50" price := "0.02" offerID := int64(2497628) - updateOffer := UpdateOfferOp(selling, buying, sellAmount, price, offerID) + updateOffer, err := UpdateOfferOp(selling, buying, sellAmount, price, offerID) + check(err) tx := Transaction{ SourceAccount: &sourceAccount, @@ -516,7 +519,7 @@ func TestManageSellOfferUpdateOffer(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp1, t) + received := buildSignEncode(t, tx, kp1) expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAAKAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAB3NZQAAAAABAAAAMgAAAAAAJhxcAAAAAAAAAAHSh2R+AAAAQAwqWg2C/oe/zH4D3Y7/yg5SlHqFvF6A3j6GQZ9NPh3ROqutovLyAE62+rvXxM7hqSNz1Rtx4frJaOhOabh6DAg=" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -540,7 +543,7 @@ func TestCreatePassiveSellOffer(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp1, t) + received := buildSignEncode(t, tx, kp1) expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAANAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAEAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAX14QAAAAABAAAAAQAAAAAAAAAB0odkfgAAAEAgUD7M1UL7x2m2m26ySzcSHxIneOT7/r+s/HLsgWDj6CmpSi1GZrlvtBH+CNuegCwvW09TRZJhp7bLywkaFCoK" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -567,7 +570,7 @@ func TestPathPayment(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp2, t) + received := buildSignEncode(t, tx, kp2) expected := "AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAAql0AAAADAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAAF9eEAAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAAAmJaAAAAAAQAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAAAAAEuFVmYAAAAQF2kLUL/RoFIy1cmt+GXdWn2tDUjJYV3YwF4A82zIBhqYSO6ogOoLPNRt3w+IGCAgfR4Q9lpax+wCXWoQERHSw4=" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -584,7 +587,7 @@ func TestMemoText(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp2, t) + received := buildSignEncode(t, tx, kp2) // https://www.stellar.org/laboratory/#txsigner?xdr=AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMokEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAADFR3YXMgYnJpbGxpZwAAAAEAAAAAAAAACwAAAAAAAAABAAAAAAAAAAA%3D&network=test expected := "AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMokEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAADFR3YXMgYnJpbGxpZwAAAAEAAAAAAAAACwAAAAAAAAABAAAAAAAAAAEuFVmYAAAAQILT8/7MGTmWkfjMi6Y23n2cVWs+IMY67xOskTivSZehp7wWaDXLIdCbdijmG64+Nz+fPBT9HYMqSRDcLiZYDQ0=" assert.Equal(t, expected, received, "Base 64 XDR should match") @@ -602,7 +605,7 @@ func TestMemoID(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp2, t) + received := buildSignEncode(t, tx, kp2) expected := "AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMLgoAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAEyy8AAAABAAAAAAAAAAsAAAAAAAAAAQAAAAAAAAABLhVZmAAAAEA5P/V/Veh6pjXj7CnqtWDATh8II+ci1z3/zmNk374XLuVLzx7jRve59AKnPMwIPwDJ8cXwEKz8+fYOIkfEI9AJ" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -619,7 +622,7 @@ func TestMemoHash(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp2, t) + received := buildSignEncode(t, tx, kp2) expected := "AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMLgoAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAsAAAAAAAAAAQAAAAAAAAABLhVZmAAAAEAgauaUpqEGF1VeXYtkYg0I19QC3GJVrCPOqDHPIdXvGkQ9N+3Vt6yfKIN0sE/X5NuD6FhArQ3adwvZeaNDilwN" assert.Equal(t, expected, received, "Base 64 XDR should match") } @@ -636,7 +639,7 @@ func TestMemoReturn(t *testing.T) { Network: network.TestNetworkPassphrase, } - received := buildSignEncode(tx, kp2, t) + received := buildSignEncode(t, tx, kp2) expected := "AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMLgoAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAsAAAAAAAAAAQAAAAAAAAABLhVZmAAAAEAuLFTunY08pbWKompoepHdazLmr7uePUSOzA4P33+SVRKWiu+h2tngOsP8hga+wpLJXT9l/0uMQ3iziRVUrh0K" assert.Equal(t, expected, received, "Base 64 XDR should match") }