From 105eed00522b83e723b42be6a7246ff082c7590f Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Mon, 30 Aug 2021 15:35:42 -0700 Subject: [PATCH 01/13] first cut --- sdk/state/open.go | 15 ++++-- sdk/state/open_test.go | 105 ++++++++++++++++++++++---------------- sdk/state/payment.go | 12 ++++- sdk/state/payment_test.go | 1 + 4 files changed, 85 insertions(+), 48 deletions(-) diff --git a/sdk/state/open.go b/sdk/state/open.go index 767059ec..5a769173 100644 --- a/sdk/state/open.go +++ b/sdk/state/open.go @@ -192,9 +192,14 @@ func (c *Channel) OpenTx() (formationTx *txnbuild.Transaction, err error) { // initiating the channel. func (c *Channel) ProposeOpen(p OpenParams) (OpenAgreement, error) { // if the channel is already open, error. - if c.openAgreement.isFull() { - return OpenAgreement{}, fmt.Errorf("cannot propose a new open if channel is already opened") + cs, err := c.State() + if err != nil { + return OpenAgreement{}, fmt.Errorf("getting channel state: %w", err) + } + if cs >= StateOpen { + return OpenAgreement{}, fmt.Errorf("cannot propose a new open if channel has already opened") } + c.startingSequence = c.initiatorEscrowAccount().SequenceNumber + 1 d := OpenAgreementDetails{ @@ -224,7 +229,11 @@ func (c *Channel) ProposeOpen(p OpenParams) (OpenAgreement, error) { func (c *Channel) validateOpen(m OpenAgreement) error { // if the channel is already open, error. - if c.openAgreement.isFull() { + cs, err := c.State() + if err != nil { + return fmt.Errorf("getting channel state: %w", err) + } + if cs >= StateOpen { return fmt.Errorf("cannot confirm a new open if channel is already opened") } diff --git a/sdk/state/open_test.go b/sdk/state/open_test.go index 3ff9f2fe..ac622a95 100644 --- a/sdk/state/open_test.go +++ b/sdk/state/open_test.go @@ -373,57 +373,76 @@ func TestChannel_OpenAgreementIsFull(t *testing.T) { } func TestChannel_ProposeAndConfirmOpen_rejectIfChannelAlreadyOpen(t *testing.T) { - localSigner := keypair.MustRandom() - remoteSigner := keypair.MustRandom() - localEscrowAccount := &EscrowAccount{ - Address: keypair.MustRandom().FromAddress(), - SequenceNumber: int64(101), - } - remoteEscrowAccount := &EscrowAccount{ - Address: keypair.MustRandom().FromAddress(), - SequenceNumber: int64(202), + initiatorSigner, err := keypair.ParseFull("SCBMAMOPWKL2YHWELK63VLAY2R74A6GTLLD4ON223B7K5KZ37MUR6IDF") + require.NoError(t, err) + responderSigner, err := keypair.ParseFull("SBM7D2IIDSRX5Y3VMTMTXXPB6AIB4WYGZBC2M64U742BNOK32X6SW4NF") + require.NoError(t, err) + + initiatorEscrow, err := keypair.ParseAddress("GAU4CFXQI6HLK5PPY2JWU3GMRJIIQNLF24XRAHX235F7QTG6BEKLGQ36") + require.NoError(t, err) + responderEscrow, err := keypair.ParseAddress("GBQNGSEHTFC4YGQ3EXHIL7JQBA6265LFANKFFAYKHM7JFGU5CORROEGO") + require.NoError(t, err) + + initiatorEscrowAccount := &EscrowAccount{ + Address: initiatorEscrow.FromAddress(), + SequenceNumber: int64(28037546508288), } - channel := NewChannel(Config{ + responderEscrowAccount := &EscrowAccount{ + Address: responderEscrow.FromAddress(), + SequenceNumber: int64(28054726377472), + } + initiatorChannel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, + MaxOpenExpiry: time.Hour, Initiator: true, - LocalSigner: localSigner, - RemoteSigner: remoteSigner.FromAddress(), - LocalEscrowAccount: localEscrowAccount, - RemoteEscrowAccount: remoteEscrowAccount, + LocalSigner: initiatorSigner, + RemoteSigner: responderSigner.FromAddress(), + LocalEscrowAccount: initiatorEscrowAccount, + RemoteEscrowAccount: responderEscrowAccount, + }) + responderChannel := NewChannel(Config{ + NetworkPassphrase: network.TestNetworkPassphrase, + MaxOpenExpiry: time.Hour, + Initiator: false, + LocalSigner: responderSigner, + RemoteSigner: initiatorSigner.FromAddress(), + LocalEscrowAccount: responderEscrowAccount, + RemoteEscrowAccount: initiatorEscrowAccount, }) - channel.openAgreement = OpenAgreement{ - Details: OpenAgreementDetails{ - ObservationPeriodTime: 1, - ObservationPeriodLedgerGap: 1, - Asset: NativeAsset, - ExpiresAt: time.Now(), - ProposingSigner: localSigner.FromAddress(), - ConfirmingSigner: remoteSigner.FromAddress(), - }, - ProposerSignatures: OpenAgreementSignatures{ - Declaration: xdr.Signature{0}, - Close: xdr.Signature{1}, - Formation: xdr.Signature{2}, - }, - ConfirmerSignatures: OpenAgreementSignatures{ - Declaration: xdr.Signature{3}, - Close: xdr.Signature{4}, - Formation: xdr.Signature{5}, - }, - } - _, err := channel.ProposeOpen(OpenParams{}) - require.EqualError(t, err, "cannot propose a new open if channel is already opened") + open, err := initiatorChannel.ProposeOpen((OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + })) + require.NoError(t, err) + open, err = responderChannel.ConfirmOpen(open) + require.NoError(t, err) + _, err = initiatorChannel.ConfirmOpen(open) + require.NoError(t, err) - _, err = channel.ConfirmOpen(OpenAgreement{}) - require.EqualError(t, err, "validating open agreement: cannot confirm a new open if channel is already opened") + formationTx, err := initiatorChannel.OpenTx() + require.NoError(t, err) + formationTxXDR, err := formationTx.Base64() + require.NoError(t, err) + + validResultXDR := "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAA=" + resultMetaXDR := "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" + err = initiatorChannel.IngestTx(formationTxXDR, validResultXDR, resultMetaXDR) + require.NoError(t, err) + + cs, err := initiatorChannel.State() + require.NoError(t, err) + assert.Equal(t, StateOpen, cs) - // A channel without a full open agreement should be able to propose an open - channel.openAgreement.ConfirmerSignatures = OpenAgreementSignatures{} - _, err = channel.ProposeOpen(OpenParams{ + // local channel trying to open channel again should error. + _, err = initiatorChannel.ProposeOpen((OpenParams{ Asset: NativeAsset, ExpiresAt: time.Now().Add(5 * time.Minute), - }) - require.NoError(t, err) + })) + require.EqualError(t, err, "cannot propose a new open if channel has already opened") + + // local channel trying to confirm an open again should error. + _, err = initiatorChannel.ConfirmOpen(open) + require.EqualError(t, err, "validating open agreement: cannot confirm a new open if channel is already opened") } diff --git a/sdk/state/payment.go b/sdk/state/payment.go index b0f24b09..8a0600d8 100644 --- a/sdk/state/payment.go +++ b/sdk/state/payment.go @@ -96,7 +96,11 @@ func (c *Channel) ProposePayment(amount int64) (CloseAgreement, error) { } // If the channel is not open yet, error. - if c.latestAuthorizedCloseAgreement.isEmpty() { + cs, err := c.State() + if err != nil { + return CloseAgreement{}, fmt.Errorf("getting channel state: %w", err) + } + if cs < StateOpen { return CloseAgreement{}, fmt.Errorf("cannot propose a payment before channel is opened") } @@ -158,7 +162,11 @@ var ErrUnderfunded = fmt.Errorf("account is underfunded to make payment") // on the state of the close agreement signatures. func (c *Channel) validatePayment(ca CloseAgreement) (err error) { // If the channel is not open yet, error. - if c.latestAuthorizedCloseAgreement.isEmpty() { + cs, err := c.State() + if err != nil { + return fmt.Errorf("getting channel state: %w", err) + } + if cs < StateOpen { return fmt.Errorf("cannot confirm a payment before channel is opened") } diff --git a/sdk/state/payment_test.go b/sdk/state/payment_test.go index 0e7d47a8..bc482117 100644 --- a/sdk/state/payment_test.go +++ b/sdk/state/payment_test.go @@ -617,6 +617,7 @@ func TestChannel_ConfirmPayment_initiatorCannotProposePaymentThatIsUnderfunded(t ObservationPeriodLedgerGap: 10, }, } + _, err := channel.ProposePayment(110) assert.EqualError(t, err, "amount over commits: account is underfunded to make payment") assert.ErrorIs(t, err, ErrUnderfunded) From 809433ef4cf4c5859629b18cd45ddddd9f53b956 Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 15:13:42 -0700 Subject: [PATCH 02/13] fix unit tests --- sdk/state/ingest_test.go | 3 +++ sdk/state/payment.go | 2 +- sdk/state/payment_test.go | 53 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/sdk/state/ingest_test.go b/sdk/state/ingest_test.go index a0325fa5..4856fe81 100644 --- a/sdk/state/ingest_test.go +++ b/sdk/state/ingest_test.go @@ -65,6 +65,7 @@ func TestChannel_IngestTx_latestUnauthorizedDeclTxViaFeeBump(t *testing.T) { // Mock initiatorChannel ingested formation tx successfully. initiatorChannel.openExecutedAndValidated = true + responderChannel.openExecutedAndValidated = true // To prevent xdr parsing error. placeholderXDR := "AAAAAgAAAAIAAAADABArWwAAAAAAAAAAWPnYf+6kQN3t44vgesQdWh4JOOPj7aer852I7RJhtzAAAAAWg8TZOwANrPwAAAAKAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABABArWwAAAAAAAAAAWPnYf+6kQN3t44vgesQdWh4JOOPj7aer852I7RJhtzAAAAAWg8TZOwANrPwAAAALAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAABAAAAAMAD/39AAAAAAAAAAD49aUpVx7fhJPK6wDdlPJgkA1HkAi85qUL1tii8YSZzQAAABdjSVwcAA/8sgAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAECtbAAAAAAAAAAD49aUpVx7fhJPK6wDdlPJgkA1HkAi85qUL1tii8YSZzQAAABee5CYcAA/8sgAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAMAECtbAAAAAAAAAABY+dh/7qRA3e3ji+B6xB1aHgk44+Ptp6vznYjtEmG3MAAAABaDxNk7AA2s/AAAAAsAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAECtbAAAAAAAAAABY+dh/7qRA3e3ji+B6xB1aHgk44+Ptp6vznYjtEmG3MAAAABZIKg87AA2s/AAAAAsAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA=" @@ -153,6 +154,7 @@ func TestChannel_IngestTx_latestUnauthorizedDeclTx(t *testing.T) { // Mock initiatorChannel ingested formation tx successfully. initiatorChannel.openExecutedAndValidated = true + responderChannel.openExecutedAndValidated = true // To prevent xdr parsing error. placeholderXDR := "AAAAAgAAAAIAAAADABArWwAAAAAAAAAAWPnYf+6kQN3t44vgesQdWh4JOOPj7aer852I7RJhtzAAAAAWg8TZOwANrPwAAAAKAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABABArWwAAAAAAAAAAWPnYf+6kQN3t44vgesQdWh4JOOPj7aer852I7RJhtzAAAAAWg8TZOwANrPwAAAALAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAABAAAAAMAD/39AAAAAAAAAAD49aUpVx7fhJPK6wDdlPJgkA1HkAi85qUL1tii8YSZzQAAABdjSVwcAA/8sgAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAECtbAAAAAAAAAAD49aUpVx7fhJPK6wDdlPJgkA1HkAi85qUL1tii8YSZzQAAABee5CYcAA/8sgAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAMAECtbAAAAAAAAAABY+dh/7qRA3e3ji+B6xB1aHgk44+Ptp6vznYjtEmG3MAAAABaDxNk7AA2s/AAAAAsAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAECtbAAAAAAAAAABY+dh/7qRA3e3ji+B6xB1aHgk44+Ptp6vznYjtEmG3MAAAABZIKg87AA2s/AAAAAsAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA=" @@ -294,6 +296,7 @@ func TestChannel_IngestTx_oldDeclTx(t *testing.T) { // Mock initiatorChannel ingested formation tx successfully. initiatorChannel.openExecutedAndValidated = true + responderChannel.openExecutedAndValidated = true // To prevent xdr parsing error. placeholderXDR := "AAAAAgAAAAIAAAADABArWwAAAAAAAAAAWPnYf+6kQN3t44vgesQdWh4JOOPj7aer852I7RJhtzAAAAAWg8TZOwANrPwAAAAKAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABABArWwAAAAAAAAAAWPnYf+6kQN3t44vgesQdWh4JOOPj7aer852I7RJhtzAAAAAWg8TZOwANrPwAAAALAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAABAAAAAMAD/39AAAAAAAAAAD49aUpVx7fhJPK6wDdlPJgkA1HkAi85qUL1tii8YSZzQAAABdjSVwcAA/8sgAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAECtbAAAAAAAAAAD49aUpVx7fhJPK6wDdlPJgkA1HkAi85qUL1tii8YSZzQAAABee5CYcAA/8sgAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAMAECtbAAAAAAAAAABY+dh/7qRA3e3ji+B6xB1aHgk44+Ptp6vznYjtEmG3MAAAABaDxNk7AA2s/AAAAAsAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAECtbAAAAAAAAAABY+dh/7qRA3e3ji+B6xB1aHgk44+Ptp6vznYjtEmG3MAAAABZIKg87AA2s/AAAAAsAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA=" diff --git a/sdk/state/payment.go b/sdk/state/payment.go index 8a0600d8..73adf20f 100644 --- a/sdk/state/payment.go +++ b/sdk/state/payment.go @@ -173,7 +173,7 @@ func (c *Channel) validatePayment(ca CloseAgreement) (err error) { // If a coordinated close has been proposed by this channel already, error. if !c.latestUnauthorizedCloseAgreement.isEmpty() && c.latestUnauthorizedCloseAgreement.Details.ObservationPeriodTime == 0 && c.latestUnauthorizedCloseAgreement.Details.ObservationPeriodLedgerGap == 0 { - return fmt.Errorf("cannot propose payment after proposing a coordinated close") + return fmt.Errorf("cannot confirm payment after proposing a coordinated close") } // If a coordinated close has been accepted already, error. diff --git a/sdk/state/payment_test.go b/sdk/state/payment_test.go index bc482117..20e2ade6 100644 --- a/sdk/state/payment_test.go +++ b/sdk/state/payment_test.go @@ -229,9 +229,13 @@ func TestChannel_ConfirmPayment_acceptsSameObservationPeriod(t *testing.T) { Details: CloseAgreementDetails{ ObservationPeriodTime: 1, ObservationPeriodLedgerGap: 1, + ConfirmingSigner: localSigner.FromAddress(), }, } + // Put channel into the Open state. + channel.openExecutedAndValidated = true + // A close agreement from the remote participant should be accepted if the // observation period matches the channels observation period. { @@ -289,9 +293,13 @@ func TestChannel_ConfirmPayment_rejectsDifferentObservationPeriod(t *testing.T) Details: CloseAgreementDetails{ ObservationPeriodTime: 1, ObservationPeriodLedgerGap: 1, + ConfirmingSigner: localSigner.FromAddress(), }, } + // Put channel into the Open state. + channel.openExecutedAndValidated = true + // A close agreement from the remote participant should be rejected if the // observation period doesn't match the channels observation period. { @@ -356,8 +364,13 @@ func TestChannel_ConfirmPayment_localWhoIsInitiatorRejectsPaymentToRemoteWhoIsRe Balance: 100, // Local (initiator) owes remote (responder) 100. ObservationPeriodTime: 10, ObservationPeriodLedgerGap: 10, + ConfirmingSigner: localSigner.FromAddress(), }, } + + // Put channel into the Open state. + channel.openExecutedAndValidated = true + ca := CloseAgreementDetails{ IterationNumber: 2, Balance: 110, // Local (initiator) owes remote (responder) 110, payment of 10 from ❌ local to remote. @@ -417,6 +430,7 @@ func TestChannel_ConfirmPayment_localWhoIsResponderRejectsPaymentToRemoteWhoIsIn Balance: 100, // Remote (initiator) owes local (responder) 100. ObservationPeriodTime: 10, ObservationPeriodLedgerGap: 10, + ConfirmingSigner: localSigner.FromAddress(), }, } ca := CloseAgreementDetails{ @@ -427,6 +441,10 @@ func TestChannel_ConfirmPayment_localWhoIsResponderRejectsPaymentToRemoteWhoIsIn ObservationPeriodTime: 10, ObservationPeriodLedgerGap: 10, } + + // Put channel into the Open state. + channel.openExecutedAndValidated = true + txDecl, txClose, err := channel.closeTxs(channel.openAgreement.Details, ca) require.NoError(t, err) txDecl, err = txDecl.Sign(network.TestNetworkPassphrase, remoteSigner) @@ -475,8 +493,13 @@ func TestChannel_ConfirmPayment_initiatorRejectsPaymentThatIsUnderfunded(t *test Balance: -60, // Remote (responder) owes local (initiator) 60. ObservationPeriodTime: 10, ObservationPeriodLedgerGap: 10, + ConfirmingSigner: localSigner.FromAddress(), }, } + + // Put channel into the Open state. + channel.openExecutedAndValidated = true + ca := CloseAgreementDetails{ IterationNumber: 2, Balance: -110, // Remote (responder) owes local (initiator) 110, which responder ❌ cannot pay. @@ -545,8 +568,13 @@ func TestChannel_ConfirmPayment_responderRejectsPaymentThatIsUnderfunded(t *test Balance: 60, // Remote (initiator) owes local (responder) 60. ObservationPeriodTime: 10, ObservationPeriodLedgerGap: 10, + ConfirmingSigner: localSigner.FromAddress(), }, } + + // Put channel into the Open state. + channel.openExecutedAndValidated = true + ca := CloseAgreementDetails{ IterationNumber: 2, Balance: 110, // Remote (initiator) owes local (responder) 110, which initiator ❌ cannot pay. @@ -615,9 +643,13 @@ func TestChannel_ConfirmPayment_initiatorCannotProposePaymentThatIsUnderfunded(t Balance: 60, // Local (initiator) owes remote (responder) 60. ObservationPeriodTime: 10, ObservationPeriodLedgerGap: 10, + ConfirmingSigner: localSigner.FromAddress(), }, } + // Put channel into the Open state. + channel.openExecutedAndValidated = true + _, err := channel.ProposePayment(110) assert.EqualError(t, err, "amount over commits: account is underfunded to make payment") assert.ErrorIs(t, err, ErrUnderfunded) @@ -660,8 +692,13 @@ func TestChannel_ConfirmPayment_responderCannotProposePaymentThatIsUnderfunded(t Balance: -60, // Local (responder) owes remote (initiator) 60. ObservationPeriodTime: 10, ObservationPeriodLedgerGap: 10, + ConfirmingSigner: localSigner.FromAddress(), }, } + + // Put channel into the Open state. + channel.openExecutedAndValidated = true + _, err := channel.ProposePayment(110) assert.EqualError(t, err, "amount over commits: account is underfunded to make payment") assert.ErrorIs(t, err, ErrUnderfunded) @@ -709,6 +746,7 @@ func TestLastConfirmedPayment(t *testing.T) { Balance: 0, ObservationPeriodTime: 10, ObservationPeriodLedgerGap: 10, + ConfirmingSigner: localSigner.FromAddress(), }, } receiverChannel.latestAuthorizedCloseAgreement = CloseAgreement{ @@ -717,9 +755,14 @@ func TestLastConfirmedPayment(t *testing.T) { Balance: 0, ObservationPeriodTime: 10, ObservationPeriodLedgerGap: 10, + ConfirmingSigner: remoteSigner.FromAddress(), }, } + // Put channels into the Open state. + sendingChannel.openExecutedAndValidated = true + receiverChannel.openExecutedAndValidated = true + ca, err := sendingChannel.ProposePayment(200) require.NoError(t, err) assert.Equal(t, ca, sendingChannel.latestUnauthorizedCloseAgreement) @@ -805,6 +848,10 @@ func TestChannel_ProposeAndConfirmPayment_rejectIfChannelNotOpen(t *testing.T) { _, err = senderChannel.ConfirmOpen(m) require.NoError(t, err) + // Put channel into the Open state. + senderChannel.openExecutedAndValidated = true + receiverChannel.openExecutedAndValidated = true + // Sender proposes coordinated close. ca, err := senderChannel.ProposeClose() require.NoError(t, err) @@ -824,7 +871,7 @@ func TestChannel_ProposeAndConfirmPayment_rejectIfChannelNotOpen(t *testing.T) { }, } _, err = senderChannel.ConfirmPayment(p) - require.EqualError(t, err, "validating payment: cannot propose payment after proposing a coordinated close") + require.EqualError(t, err, "validating payment: cannot confirm payment after proposing a coordinated close") // Finish close. ca, err = receiverChannel.ConfirmClose(ca) @@ -902,6 +949,10 @@ func TestChannel_enforceOnlyOneCloseAgreementAllowed(t *testing.T) { _, err = senderChannel.ConfirmOpen(m) require.NoError(t, err) + // Put channel into the Open state. + senderChannel.openExecutedAndValidated = true + receiverChannel.openExecutedAndValidated = true + caOriginal := senderChannel.latestAuthorizedCloseAgreement // sender proposes payment From 4c1636a1281aa2bc115147ca19e38be90696100d Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 15:21:31 -0700 Subject: [PATCH 03/13] add to close functions, and fix test --- sdk/state/close.go | 13 +++++++++++-- sdk/state/close_test.go | 4 ++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/sdk/state/close.go b/sdk/state/close.go index 60b8c8f3..46c02145 100644 --- a/sdk/state/close.go +++ b/sdk/state/close.go @@ -93,7 +93,11 @@ func (c *Channel) ProposeClose() (CloseAgreement, error) { } // If the channel is not open yet, error. - if c.latestAuthorizedCloseAgreement.isEmpty() { + cs, err := c.State() + if err != nil { + return CloseAgreement{}, fmt.Errorf("getting channel state: %w", err) + } + if cs < StateOpen { return CloseAgreement{}, fmt.Errorf("cannot propose a coordinated close before channel is opened") } @@ -122,9 +126,14 @@ func (c *Channel) ProposeClose() (CloseAgreement, error) { func (c *Channel) validateClose(ca CloseAgreement) error { // If the channel is not open yet, error. - if c.latestAuthorizedCloseAgreement.isEmpty() { + cs, err := c.State() + if err != nil { + return fmt.Errorf("getting channel state: %w", err) + } + if cs < StateOpen { return fmt.Errorf("cannot confirm a coordinated close before channel is opened") } + if ca.Details.IterationNumber != c.latestAuthorizedCloseAgreement.Details.IterationNumber { return fmt.Errorf("close agreement iteration number does not match saved latest authorized close agreement") } diff --git a/sdk/state/close_test.go b/sdk/state/close_test.go index 6c7f86b6..ab2902a6 100644 --- a/sdk/state/close_test.go +++ b/sdk/state/close_test.go @@ -118,6 +118,10 @@ func TestChannel_ProposeClose(t *testing.T) { _, err = localChannel.ConfirmOpen(open2) require.NoError(t, err) + // Put channels into the Open state. + localChannel.openExecutedAndValidated = true + remoteChannel.openExecutedAndValidated = true + // If the local proposes a close, the agreement will have them as the proposer. closeByLocal, err := localChannel.ProposeClose() require.NoError(t, err) From 860dcf7ae1f2fd891d9e126383cf7892f6b064f7 Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 17:46:35 -0700 Subject: [PATCH 04/13] add formation tx xdr helper, fix integration tests --- sdk/state/integrationtests/state_test.go | 126 +++++++++++++++++++++++ sdk/txbuildtest/txbuildtest.go | 76 ++++++++++++++ 2 files changed, 202 insertions(+) diff --git a/sdk/state/integrationtests/state_test.go b/sdk/state/integrationtests/state_test.go index 74b01356..2a3f1fc7 100644 --- a/sdk/state/integrationtests/state_test.go +++ b/sdk/state/integrationtests/state_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/stellar/experimental-payment-channels/sdk/state" + "github.com/stellar/experimental-payment-channels/sdk/txbuildtest" "github.com/stellar/go/amount" "github.com/stellar/go/clients/horizonclient" "github.com/stellar/go/keypair" @@ -108,6 +109,31 @@ func TestOpenUpdatesUncoordinatedClose(t *testing.T) { require.NoError(t, err) } + { + t.Log("Initiator and Responder channels ingest the formation tx ...") + ftx, err := initiatorChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: initiator.KP.Address(), + ResponderSigner: responder.KP.Address(), + InitiatorEscrow: initiator.Escrow.Address(), + ResponderEscrow: responder.Escrow.Address(), + StartSequence: s, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = initiatorChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = responderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + t.Log("Iteration", i, "Declarations:", txSeqs(declarationTxs)) t.Log("Iteration", i, "Closes:", txSeqs(closeTxs)) @@ -343,6 +369,31 @@ func TestOpenUpdatesCoordinatedCloseStartCloseThenCoordinate(t *testing.T) { require.NoError(t, err) } + { + t.Log("Initiator and Responder channels ingest the formation tx ...") + ftx, err := initiatorChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: initiator.KP.Address(), + ResponderSigner: responder.KP.Address(), + InitiatorEscrow: initiator.Escrow.Address(), + ResponderEscrow: responder.Escrow.Address(), + StartSequence: s, + Asset: txnbuild.CreditAsset{Code: asset.Code(), Issuer: asset.Issuer()}, + }) + require.NoError(t, err) + + err = initiatorChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = responderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + // Update balances known for each other. initiatorChannel.UpdateRemoteEscrowAccountBalance(responder.Contribution) responderChannel.UpdateRemoteEscrowAccountBalance(initiator.Contribution) @@ -524,6 +575,31 @@ func TestOpenUpdatesCoordinatedCloseCoordinateThenStartClose(t *testing.T) { require.NoError(t, err) } + { + t.Log("Initiator and Responder channels ingest the formation tx ...") + ftx, err := initiatorChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: initiator.KP.Address(), + ResponderSigner: responder.KP.Address(), + InitiatorEscrow: initiator.Escrow.Address(), + ResponderEscrow: responder.Escrow.Address(), + StartSequence: s, + Asset: txnbuild.CreditAsset{Code: asset.Code(), Issuer: asset.Issuer()}, + }) + require.NoError(t, err) + + err = initiatorChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = responderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + // Update balances known for each other. initiatorChannel.UpdateRemoteEscrowAccountBalance(responder.Contribution) responderChannel.UpdateRemoteEscrowAccountBalance(initiator.Contribution) @@ -705,6 +781,31 @@ func TestOpenUpdatesCoordinatedCloseCoordinateThenStartCloseByRemote(t *testing. require.NoError(t, err) } + { + t.Log("Initiator and Responder channels ingest the formation tx ...") + ftx, err := initiatorChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: initiator.KP.Address(), + ResponderSigner: responder.KP.Address(), + InitiatorEscrow: initiator.Escrow.Address(), + ResponderEscrow: responder.Escrow.Address(), + StartSequence: s, + Asset: txnbuild.CreditAsset{Code: asset.Code(), Issuer: asset.Issuer()}, + }) + require.NoError(t, err) + + err = initiatorChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = responderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + // Update balances known for each other. initiatorChannel.UpdateRemoteEscrowAccountBalance(responder.Contribution) responderChannel.UpdateRemoteEscrowAccountBalance(initiator.Contribution) @@ -865,6 +966,31 @@ func TestOpenUpdatesUncoordinatedClose_recieverNotReturningSigs(t *testing.T) { require.NoError(t, err) } + { + t.Log("Initiator and Responder channels ingest the formation tx ...") + ftx, err := initiatorChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: initiator.KP.Address(), + ResponderSigner: responder.KP.Address(), + InitiatorEscrow: initiator.Escrow.Address(), + ResponderEscrow: responder.Escrow.Address(), + StartSequence: s, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = initiatorChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = responderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + // Update balances known for each other. initiatorChannel.UpdateRemoteEscrowAccountBalance(responder.Contribution) responderChannel.UpdateRemoteEscrowAccountBalance(initiator.Contribution) diff --git a/sdk/txbuildtest/txbuildtest.go b/sdk/txbuildtest/txbuildtest.go index 101080f5..9b2c6299 100644 --- a/sdk/txbuildtest/txbuildtest.go +++ b/sdk/txbuildtest/txbuildtest.go @@ -3,6 +3,7 @@ package txbuildtest import ( "fmt" + "github.com/stellar/go/txnbuild" "github.com/stellar/go/xdr" ) @@ -57,3 +58,78 @@ func BuildResultMetaXDR(ledgerEntryResults []xdr.LedgerEntryData) (string, error } return tmXDR, nil } + +type FormationResultMetaParams struct { + InitiatorSigner string + ResponderSigner string + InitiatorEscrow string + ResponderEscrow string + StartSequence int64 + Asset txnbuild.Asset +} + +func BuildFormationResultMetaXDR(params FormationResultMetaParams) (string, error) { + led := []xdr.LedgerEntryData{ + { + Type: xdr.LedgerEntryTypeAccount, + Account: &xdr.AccountEntry{ + AccountId: xdr.MustAddress(params.InitiatorEscrow), + SeqNum: xdr.SequenceNumber(params.StartSequence), + Signers: []xdr.Signer{ + { + Key: xdr.MustSigner(params.InitiatorSigner), + Weight: 1, + }, + { + Key: xdr.MustSigner(params.ResponderSigner), + Weight: 1, + }, + }, + Thresholds: xdr.Thresholds{0, 2, 2, 2}, + }, + }, + { + Type: xdr.LedgerEntryTypeAccount, + Account: &xdr.AccountEntry{ + AccountId: xdr.MustAddress(params.ResponderEscrow), + SeqNum: xdr.SequenceNumber(1), + Signers: []xdr.Signer{ + { + Key: xdr.MustSigner(params.InitiatorSigner), + Weight: 1, + }, + { + Key: xdr.MustSigner(params.ResponderSigner), + Weight: 1, + }, + }, + Thresholds: xdr.Thresholds{0, 2, 2, 2}, + }, + }, + } + + if !params.Asset.IsNative() { + led = append(led, []xdr.LedgerEntryData{ + { + Type: xdr.LedgerEntryTypeTrustline, + TrustLine: &xdr.TrustLineEntry{ + AccountId: xdr.MustAddress(params.InitiatorEscrow), + Balance: 0, + Asset: xdr.MustNewCreditAsset(params.Asset.GetCode(), params.Asset.GetIssuer()), + Flags: xdr.Uint32(xdr.TrustLineFlagsAuthorizedFlag), + }, + }, + { + Type: xdr.LedgerEntryTypeTrustline, + TrustLine: &xdr.TrustLineEntry{ + AccountId: xdr.MustAddress(params.ResponderEscrow), + Balance: 0, + Asset: xdr.MustNewCreditAsset(params.Asset.GetCode(), params.Asset.GetIssuer()), + Flags: xdr.Uint32(xdr.TrustLineFlagsAuthorizedFlag), + }, + }, + }...) + } + + return BuildResultMetaXDR(led) +} From bcd9fc415ff94bc831187d2e926b3931d65e82ef Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 18:37:59 -0700 Subject: [PATCH 05/13] check if state is statenone --- sdk/state/open.go | 4 +- sdk/state/open_test.go | 100 ++++++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/sdk/state/open.go b/sdk/state/open.go index 5a769173..db7b6179 100644 --- a/sdk/state/open.go +++ b/sdk/state/open.go @@ -196,7 +196,7 @@ func (c *Channel) ProposeOpen(p OpenParams) (OpenAgreement, error) { if err != nil { return OpenAgreement{}, fmt.Errorf("getting channel state: %w", err) } - if cs >= StateOpen { + if cs != StateNone { return OpenAgreement{}, fmt.Errorf("cannot propose a new open if channel has already opened") } @@ -233,7 +233,7 @@ func (c *Channel) validateOpen(m OpenAgreement) error { if err != nil { return fmt.Errorf("getting channel state: %w", err) } - if cs >= StateOpen { + if cs != StateNone { return fmt.Errorf("cannot confirm a new open if channel is already opened") } diff --git a/sdk/state/open_test.go b/sdk/state/open_test.go index ac622a95..701bb3c3 100644 --- a/sdk/state/open_test.go +++ b/sdk/state/open_test.go @@ -5,8 +5,10 @@ import ( "testing" "time" + "github.com/stellar/experimental-payment-channels/sdk/txbuildtest" "github.com/stellar/go/keypair" "github.com/stellar/go/network" + "github.com/stellar/go/txnbuild" "github.com/stellar/go/xdr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -373,76 +375,80 @@ func TestChannel_OpenAgreementIsFull(t *testing.T) { } func TestChannel_ProposeAndConfirmOpen_rejectIfChannelAlreadyOpen(t *testing.T) { - initiatorSigner, err := keypair.ParseFull("SCBMAMOPWKL2YHWELK63VLAY2R74A6GTLLD4ON223B7K5KZ37MUR6IDF") - require.NoError(t, err) - responderSigner, err := keypair.ParseFull("SBM7D2IIDSRX5Y3VMTMTXXPB6AIB4WYGZBC2M64U742BNOK32X6SW4NF") - require.NoError(t, err) - - initiatorEscrow, err := keypair.ParseAddress("GAU4CFXQI6HLK5PPY2JWU3GMRJIIQNLF24XRAHX235F7QTG6BEKLGQ36") - require.NoError(t, err) - responderEscrow, err := keypair.ParseAddress("GBQNGSEHTFC4YGQ3EXHIL7JQBA6265LFANKFFAYKHM7JFGU5CORROEGO") - require.NoError(t, err) - - initiatorEscrowAccount := &EscrowAccount{ - Address: initiatorEscrow.FromAddress(), - SequenceNumber: int64(28037546508288), + localSigner := keypair.MustRandom() + remoteSigner := keypair.MustRandom() + localEscrowAccount := &EscrowAccount{ + Address: keypair.MustRandom().FromAddress(), + SequenceNumber: int64(101), } - - responderEscrowAccount := &EscrowAccount{ - Address: responderEscrow.FromAddress(), - SequenceNumber: int64(28054726377472), + remoteEscrowAccount := &EscrowAccount{ + Address: keypair.MustRandom().FromAddress(), + SequenceNumber: int64(202), } + initiatorChannel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, - MaxOpenExpiry: time.Hour, Initiator: true, - LocalSigner: initiatorSigner, - RemoteSigner: responderSigner.FromAddress(), - LocalEscrowAccount: initiatorEscrowAccount, - RemoteEscrowAccount: responderEscrowAccount, + LocalSigner: localSigner, + RemoteSigner: remoteSigner.FromAddress(), + LocalEscrowAccount: localEscrowAccount, + RemoteEscrowAccount: remoteEscrowAccount, + MaxOpenExpiry: 2 * time.Hour, }) responderChannel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, - MaxOpenExpiry: time.Hour, Initiator: false, - LocalSigner: responderSigner, - RemoteSigner: initiatorSigner.FromAddress(), - LocalEscrowAccount: responderEscrowAccount, - RemoteEscrowAccount: initiatorEscrowAccount, + LocalSigner: remoteSigner, + RemoteSigner: localSigner.FromAddress(), + LocalEscrowAccount: remoteEscrowAccount, + RemoteEscrowAccount: localEscrowAccount, + MaxOpenExpiry: 2 * time.Hour, }) - open, err := initiatorChannel.ProposeOpen((OpenParams{ - Asset: NativeAsset, - ExpiresAt: time.Now().Add(5 * time.Minute), - })) + // Open channel. + m, err := initiatorChannel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Second), + ObservationPeriodTime: 10, + ObservationPeriodLedgerGap: 10, + }) require.NoError(t, err) - open, err = responderChannel.ConfirmOpen(open) + m, err = responderChannel.ConfirmOpen(m) require.NoError(t, err) - _, err = initiatorChannel.ConfirmOpen(open) + _, err = initiatorChannel.ConfirmOpen(m) require.NoError(t, err) - formationTx, err := initiatorChannel.OpenTx() + // Ingest the formationTx successfully to enter the Open state. + ftx, err := initiatorChannel.OpenTx() require.NoError(t, err) - formationTxXDR, err := formationTx.Base64() + ftxXDR, err := ftx.Base64() require.NoError(t, err) - validResultXDR := "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAA=" - resultMetaXDR := "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" - err = initiatorChannel.IngestTx(formationTxXDR, validResultXDR, resultMetaXDR) + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) require.NoError(t, err) - cs, err := initiatorChannel.State() + err = initiatorChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = responderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) require.NoError(t, err) - assert.Equal(t, StateOpen, cs) - // local channel trying to open channel again should error. - _, err = initiatorChannel.ProposeOpen((OpenParams{ - Asset: NativeAsset, - ExpiresAt: time.Now().Add(5 * time.Minute), - })) + _, err = initiatorChannel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Second), + ObservationPeriodTime: 10, + ObservationPeriodLedgerGap: 10, + }) require.EqualError(t, err, "cannot propose a new open if channel has already opened") - // local channel trying to confirm an open again should error. - _, err = initiatorChannel.ConfirmOpen(open) + _, err = responderChannel.ConfirmOpen(m) require.EqualError(t, err, "validating open agreement: cannot confirm a new open if channel is already opened") } From 3e69491210a236d2224b405d8f616314047858d7 Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 18:38:43 -0700 Subject: [PATCH 06/13] change back --- sdk/state/open.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/state/open.go b/sdk/state/open.go index db7b6179..5a769173 100644 --- a/sdk/state/open.go +++ b/sdk/state/open.go @@ -196,7 +196,7 @@ func (c *Channel) ProposeOpen(p OpenParams) (OpenAgreement, error) { if err != nil { return OpenAgreement{}, fmt.Errorf("getting channel state: %w", err) } - if cs != StateNone { + if cs >= StateOpen { return OpenAgreement{}, fmt.Errorf("cannot propose a new open if channel has already opened") } @@ -233,7 +233,7 @@ func (c *Channel) validateOpen(m OpenAgreement) error { if err != nil { return fmt.Errorf("getting channel state: %w", err) } - if cs != StateNone { + if cs >= StateOpen { return fmt.Errorf("cannot confirm a new open if channel is already opened") } From 9fd645b67c716ff50f5c4363073f3235d8e4d668 Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 19:31:11 -0700 Subject: [PATCH 07/13] fix unit test --- sdk/state/ingest_test.go | 3 + sdk/state/payment.go | 2 +- sdk/state/payment_test.go | 180 +++++++++++++++++++++++++++++++------- 3 files changed, 150 insertions(+), 35 deletions(-) diff --git a/sdk/state/ingest_test.go b/sdk/state/ingest_test.go index 4856fe81..f0bcec01 100644 --- a/sdk/state/ingest_test.go +++ b/sdk/state/ingest_test.go @@ -65,6 +65,7 @@ func TestChannel_IngestTx_latestUnauthorizedDeclTxViaFeeBump(t *testing.T) { // Mock initiatorChannel ingested formation tx successfully. initiatorChannel.openExecutedAndValidated = true + initiatorChannel.setInitiatorEscrowAccountSequence(initiatorEscrowAccount.SequenceNumber + 1) responderChannel.openExecutedAndValidated = true // To prevent xdr parsing error. @@ -154,6 +155,7 @@ func TestChannel_IngestTx_latestUnauthorizedDeclTx(t *testing.T) { // Mock initiatorChannel ingested formation tx successfully. initiatorChannel.openExecutedAndValidated = true + initiatorChannel.setInitiatorEscrowAccountSequence(initiatorEscrowAccount.SequenceNumber + 1) responderChannel.openExecutedAndValidated = true // To prevent xdr parsing error. @@ -296,6 +298,7 @@ func TestChannel_IngestTx_oldDeclTx(t *testing.T) { // Mock initiatorChannel ingested formation tx successfully. initiatorChannel.openExecutedAndValidated = true + initiatorChannel.setInitiatorEscrowAccountSequence(initiatorEscrowAccount.SequenceNumber + 1) responderChannel.openExecutedAndValidated = true // To prevent xdr parsing error. diff --git a/sdk/state/payment.go b/sdk/state/payment.go index 73adf20f..eab1027a 100644 --- a/sdk/state/payment.go +++ b/sdk/state/payment.go @@ -100,7 +100,7 @@ func (c *Channel) ProposePayment(amount int64) (CloseAgreement, error) { if err != nil { return CloseAgreement{}, fmt.Errorf("getting channel state: %w", err) } - if cs < StateOpen { + if cs != StateOpen { return CloseAgreement{}, fmt.Errorf("cannot propose a payment before channel is opened") } diff --git a/sdk/state/payment_test.go b/sdk/state/payment_test.go index 20e2ade6..45fba067 100644 --- a/sdk/state/payment_test.go +++ b/sdk/state/payment_test.go @@ -1,12 +1,15 @@ package state import ( + "fmt" "strconv" "testing" "time" + "github.com/stellar/experimental-payment-channels/sdk/txbuildtest" "github.com/stellar/go/keypair" "github.com/stellar/go/network" + "github.com/stellar/go/txnbuild" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -647,10 +650,34 @@ func TestChannel_ConfirmPayment_initiatorCannotProposePaymentThatIsUnderfunded(t }, } - // Put channel into the Open state. - channel.openExecutedAndValidated = true + // Put the channel into the Open state. + _, err := channel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + }) + require.NoError(t, err) + + ftx, err := channel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) - _, err := channel.ProposePayment(110) + err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + + _, err = channel.ProposePayment(110) assert.EqualError(t, err, "amount over commits: account is underfunded to make payment") assert.ErrorIs(t, err, ErrUnderfunded) @@ -696,10 +723,37 @@ func TestChannel_ConfirmPayment_responderCannotProposePaymentThatIsUnderfunded(t }, } - // Put channel into the Open state. - channel.openExecutedAndValidated = true + // Put the channel into the Open state. + _, err := channel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + }) + require.NoError(t, err) + + ftx, err := channel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) - _, err := channel.ProposePayment(110) + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: remoteSigner.Address(), + ResponderSigner: localSigner.Address(), + InitiatorEscrow: remoteEscrowAccount.Address.Address(), + ResponderEscrow: localEscrowAccount.Address.Address(), + StartSequence: remoteEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + + // TODO - remove + fmt.Println(channel.openExecutedWithError) + + _, err = channel.ProposePayment(110) assert.EqualError(t, err, "amount over commits: account is underfunded to make payment") assert.ErrorIs(t, err, ErrUnderfunded) @@ -715,12 +769,12 @@ func TestLastConfirmedPayment(t *testing.T) { localEscrowAccount := &EscrowAccount{ Address: keypair.MustRandom().FromAddress(), SequenceNumber: int64(101), - Balance: 1000, + Balance: 0, } remoteEscrowAccount := &EscrowAccount{ Address: keypair.MustRandom().FromAddress(), SequenceNumber: int64(202), - Balance: 1000, + Balance: 0, } sendingChannel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, @@ -729,6 +783,7 @@ func TestLastConfirmedPayment(t *testing.T) { RemoteSigner: remoteSigner.FromAddress(), LocalEscrowAccount: localEscrowAccount, RemoteEscrowAccount: remoteEscrowAccount, + MaxOpenExpiry: 2 * time.Hour, }) receiverChannel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, @@ -737,32 +792,48 @@ func TestLastConfirmedPayment(t *testing.T) { RemoteSigner: localSigner.FromAddress(), LocalEscrowAccount: remoteEscrowAccount, RemoteEscrowAccount: localEscrowAccount, + MaxOpenExpiry: 2 * time.Hour, }) - // // latest close agreement should be set during open steps - sendingChannel.latestAuthorizedCloseAgreement = CloseAgreement{ - Details: CloseAgreementDetails{ - IterationNumber: 1, - Balance: 0, - ObservationPeriodTime: 10, - ObservationPeriodLedgerGap: 10, - ConfirmingSigner: localSigner.FromAddress(), - }, - } - receiverChannel.latestAuthorizedCloseAgreement = CloseAgreement{ - Details: CloseAgreementDetails{ - IterationNumber: 1, - Balance: 0, - ObservationPeriodTime: 10, - ObservationPeriodLedgerGap: 10, - ConfirmingSigner: remoteSigner.FromAddress(), - }, - } + // Open steps. + m, err := sendingChannel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + ObservationPeriodTime: 10, + ObservationPeriodLedgerGap: 10, + }) + require.NoError(t, err) + m, err = receiverChannel.ConfirmOpen(m) + require.NoError(t, err) + _, err = sendingChannel.ConfirmOpen(m) + require.NoError(t, err) + + ftx, err := sendingChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = sendingChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = receiverChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) - // Put channels into the Open state. - sendingChannel.openExecutedAndValidated = true - receiverChannel.openExecutedAndValidated = true + sendingChannel.UpdateLocalEscrowAccountBalance(1000) + sendingChannel.UpdateRemoteEscrowAccountBalance(1000) + // Test the returned close agreemenets are as expected. ca, err := sendingChannel.ProposePayment(200) require.NoError(t, err) assert.Equal(t, ca, sendingChannel.latestUnauthorizedCloseAgreement) @@ -849,8 +920,27 @@ func TestChannel_ProposeAndConfirmPayment_rejectIfChannelNotOpen(t *testing.T) { require.NoError(t, err) // Put channel into the Open state. - senderChannel.openExecutedAndValidated = true - receiverChannel.openExecutedAndValidated = true + ftx, err := senderChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = senderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = receiverChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) // Sender proposes coordinated close. ca, err := senderChannel.ProposeClose() @@ -950,8 +1040,30 @@ func TestChannel_enforceOnlyOneCloseAgreementAllowed(t *testing.T) { require.NoError(t, err) // Put channel into the Open state. - senderChannel.openExecutedAndValidated = true - receiverChannel.openExecutedAndValidated = true + ftx, err := senderChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = senderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = receiverChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + + senderChannel.UpdateLocalEscrowAccountBalance(1000) + senderChannel.UpdateRemoteEscrowAccountBalance(1000) caOriginal := senderChannel.latestAuthorizedCloseAgreement From 1a33b01fa1c3b12b235c2751fdea2de882e41df0 Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 21:22:50 -0700 Subject: [PATCH 08/13] make putting the channel into the Open state in tests consistent --- sdk/state/payment_test.go | 300 +++++++++++++++++++++++++++----------- 1 file changed, 212 insertions(+), 88 deletions(-) diff --git a/sdk/state/payment_test.go b/sdk/state/payment_test.go index 45fba067..21fdcb06 100644 --- a/sdk/state/payment_test.go +++ b/sdk/state/payment_test.go @@ -1,7 +1,6 @@ package state import ( - "fmt" "strconv" "testing" "time" @@ -283,7 +282,7 @@ func TestChannel_ConfirmPayment_rejectsDifferentObservationPeriod(t *testing.T) SequenceNumber: int64(202), } - // Given a channel with observation periods set to 1, that is already open. + // Given a channel with observation periods set to 1. channel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, Initiator: true, @@ -292,6 +291,36 @@ func TestChannel_ConfirmPayment_rejectsDifferentObservationPeriod(t *testing.T) LocalEscrowAccount: localEscrowAccount, RemoteEscrowAccount: remoteEscrowAccount, }) + + // Put the channel into the Open state. + { + _, err := channel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + }) + require.NoError(t, err) + + ftx, err := channel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + channel.latestAuthorizedCloseAgreement = CloseAgreement{ Details: CloseAgreementDetails{ ObservationPeriodTime: 1, @@ -300,9 +329,6 @@ func TestChannel_ConfirmPayment_rejectsDifferentObservationPeriod(t *testing.T) }, } - // Put channel into the Open state. - channel.openExecutedAndValidated = true - // A close agreement from the remote participant should be rejected if the // observation period doesn't match the channels observation period. { @@ -354,13 +380,37 @@ func TestChannel_ConfirmPayment_localWhoIsInitiatorRejectsPaymentToRemoteWhoIsRe RemoteEscrowAccount: remoteEscrowAccount, }) + // Put the channel into the Open state. + { + _, err := channel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + }) + require.NoError(t, err) + + ftx, err := channel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + // A close agreement from the remote participant should be rejected if the // payment changes the balance in the favor of the remote. - channel.openAgreement = OpenAgreement{ - Details: OpenAgreementDetails{ - Asset: NativeAsset, - }, - } channel.latestAuthorizedCloseAgreement = CloseAgreement{ Details: CloseAgreementDetails{ IterationNumber: 1, @@ -371,9 +421,6 @@ func TestChannel_ConfirmPayment_localWhoIsInitiatorRejectsPaymentToRemoteWhoIsRe }, } - // Put channel into the Open state. - channel.openExecutedAndValidated = true - ca := CloseAgreementDetails{ IterationNumber: 2, Balance: 110, // Local (initiator) owes remote (responder) 110, payment of 10 from ❌ local to remote. @@ -410,7 +457,7 @@ func TestChannel_ConfirmPayment_localWhoIsResponderRejectsPaymentToRemoteWhoIsIn SequenceNumber: int64(202), } - // Given a channel with observation periods set to 1, that is already open. + // Given a channel with observation periods set to 1. channel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, Initiator: false, @@ -420,13 +467,37 @@ func TestChannel_ConfirmPayment_localWhoIsResponderRejectsPaymentToRemoteWhoIsIn RemoteEscrowAccount: remoteEscrowAccount, }) + // Put the channel into the Open state. + { + _, err := channel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + }) + require.NoError(t, err) + + ftx, err := channel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: remoteSigner.Address(), + ResponderSigner: localSigner.Address(), + InitiatorEscrow: remoteEscrowAccount.Address.Address(), + ResponderEscrow: localEscrowAccount.Address.Address(), + StartSequence: remoteEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + // A close agreement from the remote participant should be rejected if the // payment changes the balance in the favor of the remote. - channel.openAgreement = OpenAgreement{ - Details: OpenAgreementDetails{ - Asset: NativeAsset, - }, - } channel.latestAuthorizedCloseAgreement = CloseAgreement{ Details: CloseAgreementDetails{ IterationNumber: 1, @@ -478,7 +549,7 @@ func TestChannel_ConfirmPayment_initiatorRejectsPaymentThatIsUnderfunded(t *test Balance: 100, } - // Given a channel with observation periods set to 1, that is already open. + // Given a channel with observation periods set to 1. channel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, Initiator: true, @@ -488,6 +559,35 @@ func TestChannel_ConfirmPayment_initiatorRejectsPaymentThatIsUnderfunded(t *test RemoteEscrowAccount: remoteEscrowAccount, }) + // Put the channel into the Open state. + { + _, err := channel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + }) + require.NoError(t, err) + + ftx, err := channel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + // A close agreement from the remote participant should be rejected if the // payment changes the balance in the favor of the remote. channel.latestAuthorizedCloseAgreement = CloseAgreement{ @@ -500,9 +600,6 @@ func TestChannel_ConfirmPayment_initiatorRejectsPaymentThatIsUnderfunded(t *test }, } - // Put channel into the Open state. - channel.openExecutedAndValidated = true - ca := CloseAgreementDetails{ IterationNumber: 2, Balance: -110, // Remote (responder) owes local (initiator) 110, which responder ❌ cannot pay. @@ -553,7 +650,7 @@ func TestChannel_ConfirmPayment_responderRejectsPaymentThatIsUnderfunded(t *test Balance: 100, } - // Given a channel with observation periods set to 1, that is already open. + // Given a channel with observation periods set to 1. channel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, Initiator: false, @@ -563,6 +660,35 @@ func TestChannel_ConfirmPayment_responderRejectsPaymentThatIsUnderfunded(t *test RemoteEscrowAccount: remoteEscrowAccount, }) + // Put the channel into the Open state. + { + _, err := channel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + }) + require.NoError(t, err) + + ftx, err := channel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: remoteSigner.Address(), + ResponderSigner: localSigner.Address(), + InitiatorEscrow: remoteEscrowAccount.Address.Address(), + ResponderEscrow: localEscrowAccount.Address.Address(), + StartSequence: remoteEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + // A close agreement from the remote participant should be rejected if the // payment changes the balance in the favor of the remote. channel.latestAuthorizedCloseAgreement = CloseAgreement{ @@ -575,9 +701,6 @@ func TestChannel_ConfirmPayment_responderRejectsPaymentThatIsUnderfunded(t *test }, } - // Put channel into the Open state. - channel.openExecutedAndValidated = true - ca := CloseAgreementDetails{ IterationNumber: 2, Balance: 110, // Remote (initiator) owes local (responder) 110, which initiator ❌ cannot pay. @@ -628,7 +751,7 @@ func TestChannel_ConfirmPayment_initiatorCannotProposePaymentThatIsUnderfunded(t Balance: 100, } - // Given a channel with observation periods set to 1, that is already open. + // Given a channel with observation periods set to 1. channel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, Initiator: true, @@ -638,6 +761,35 @@ func TestChannel_ConfirmPayment_initiatorCannotProposePaymentThatIsUnderfunded(t RemoteEscrowAccount: remoteEscrowAccount, }) + // Put the channel into the Open state. + { + _, err := channel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + }) + require.NoError(t, err) + + ftx, err := channel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + // A close agreement from the remote participant should be rejected if the // payment changes the balance in the favor of the remote. channel.latestAuthorizedCloseAgreement = CloseAgreement{ @@ -650,34 +802,7 @@ func TestChannel_ConfirmPayment_initiatorCannotProposePaymentThatIsUnderfunded(t }, } - // Put the channel into the Open state. - _, err := channel.ProposeOpen(OpenParams{ - Asset: NativeAsset, - ExpiresAt: time.Now().Add(5 * time.Minute), - }) - require.NoError(t, err) - - ftx, err := channel.OpenTx() - require.NoError(t, err) - ftxXDR, err := ftx.Base64() - require.NoError(t, err) - - successResultXDR, err := txbuildtest.BuildResultXDR(true) - require.NoError(t, err) - resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ - InitiatorSigner: localSigner.Address(), - ResponderSigner: remoteSigner.Address(), - InitiatorEscrow: localEscrowAccount.Address.Address(), - ResponderEscrow: remoteEscrowAccount.Address.Address(), - StartSequence: localEscrowAccount.SequenceNumber + 1, - Asset: txnbuild.NativeAsset{}, - }) - require.NoError(t, err) - - err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) - require.NoError(t, err) - - _, err = channel.ProposePayment(110) + _, err := channel.ProposePayment(110) assert.EqualError(t, err, "amount over commits: account is underfunded to make payment") assert.ErrorIs(t, err, ErrUnderfunded) @@ -701,7 +826,7 @@ func TestChannel_ConfirmPayment_responderCannotProposePaymentThatIsUnderfunded(t Balance: 100, } - // Given a channel with observation periods set to 1, that is already open. + // Given a channel with observation periods set to 1. channel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, Initiator: false, @@ -711,6 +836,35 @@ func TestChannel_ConfirmPayment_responderCannotProposePaymentThatIsUnderfunded(t RemoteEscrowAccount: remoteEscrowAccount, }) + // Put the channel into the Open state. + { + _, err := channel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + }) + require.NoError(t, err) + + ftx, err := channel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: remoteSigner.Address(), + ResponderSigner: localSigner.Address(), + InitiatorEscrow: remoteEscrowAccount.Address.Address(), + ResponderEscrow: localEscrowAccount.Address.Address(), + StartSequence: remoteEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } + // A close agreement from the remote participant should be rejected if the // payment changes the balance in the favor of the remote. channel.latestAuthorizedCloseAgreement = CloseAgreement{ @@ -723,37 +877,7 @@ func TestChannel_ConfirmPayment_responderCannotProposePaymentThatIsUnderfunded(t }, } - // Put the channel into the Open state. - _, err := channel.ProposeOpen(OpenParams{ - Asset: NativeAsset, - ExpiresAt: time.Now().Add(5 * time.Minute), - }) - require.NoError(t, err) - - ftx, err := channel.OpenTx() - require.NoError(t, err) - ftxXDR, err := ftx.Base64() - require.NoError(t, err) - - successResultXDR, err := txbuildtest.BuildResultXDR(true) - require.NoError(t, err) - resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ - InitiatorSigner: remoteSigner.Address(), - ResponderSigner: localSigner.Address(), - InitiatorEscrow: remoteEscrowAccount.Address.Address(), - ResponderEscrow: localEscrowAccount.Address.Address(), - StartSequence: remoteEscrowAccount.SequenceNumber + 1, - Asset: txnbuild.NativeAsset{}, - }) - require.NoError(t, err) - - err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) - require.NoError(t, err) - - // TODO - remove - fmt.Println(channel.openExecutedWithError) - - _, err = channel.ProposePayment(110) + _, err := channel.ProposePayment(110) assert.EqualError(t, err, "amount over commits: account is underfunded to make payment") assert.ErrorIs(t, err, ErrUnderfunded) From b1e52be6c6ed9c114a8ebbcc9bc9c808955ef809 Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 21:26:37 -0700 Subject: [PATCH 09/13] missed some --- sdk/state/payment_test.go | 121 +++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 49 deletions(-) diff --git a/sdk/state/payment_test.go b/sdk/state/payment_test.go index 21fdcb06..6d4059b4 100644 --- a/sdk/state/payment_test.go +++ b/sdk/state/payment_test.go @@ -236,7 +236,29 @@ func TestChannel_ConfirmPayment_acceptsSameObservationPeriod(t *testing.T) { } // Put channel into the Open state. - channel.openExecutedAndValidated = true + { + ftx, err := senderChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) + + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = senderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = receiverChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } // A close agreement from the remote participant should be accepted if the // observation period matches the channels observation period. @@ -292,7 +314,7 @@ func TestChannel_ConfirmPayment_rejectsDifferentObservationPeriod(t *testing.T) RemoteEscrowAccount: remoteEscrowAccount, }) - // Put the channel into the Open state. + // Put channel into the Open state. { _, err := channel.ProposeOpen(OpenParams{ Asset: NativeAsset, @@ -380,7 +402,7 @@ func TestChannel_ConfirmPayment_localWhoIsInitiatorRejectsPaymentToRemoteWhoIsRe RemoteEscrowAccount: remoteEscrowAccount, }) - // Put the channel into the Open state. + // Put channel into the Open state. { _, err := channel.ProposeOpen(OpenParams{ Asset: NativeAsset, @@ -467,7 +489,7 @@ func TestChannel_ConfirmPayment_localWhoIsResponderRejectsPaymentToRemoteWhoIsIn RemoteEscrowAccount: remoteEscrowAccount, }) - // Put the channel into the Open state. + // Put channel into the Open state. { _, err := channel.ProposeOpen(OpenParams{ Asset: NativeAsset, @@ -516,9 +538,6 @@ func TestChannel_ConfirmPayment_localWhoIsResponderRejectsPaymentToRemoteWhoIsIn ObservationPeriodLedgerGap: 10, } - // Put channel into the Open state. - channel.openExecutedAndValidated = true - txDecl, txClose, err := channel.closeTxs(channel.openAgreement.Details, ca) require.NoError(t, err) txDecl, err = txDecl.Sign(network.TestNetworkPassphrase, remoteSigner) @@ -559,7 +578,7 @@ func TestChannel_ConfirmPayment_initiatorRejectsPaymentThatIsUnderfunded(t *test RemoteEscrowAccount: remoteEscrowAccount, }) - // Put the channel into the Open state. + // Put channel into the Open state. { _, err := channel.ProposeOpen(OpenParams{ Asset: NativeAsset, @@ -660,7 +679,7 @@ func TestChannel_ConfirmPayment_responderRejectsPaymentThatIsUnderfunded(t *test RemoteEscrowAccount: remoteEscrowAccount, }) - // Put the channel into the Open state. + // Put channel into the Open state. { _, err := channel.ProposeOpen(OpenParams{ Asset: NativeAsset, @@ -761,7 +780,7 @@ func TestChannel_ConfirmPayment_initiatorCannotProposePaymentThatIsUnderfunded(t RemoteEscrowAccount: remoteEscrowAccount, }) - // Put the channel into the Open state. + // Put channel into the Open state. { _, err := channel.ProposeOpen(OpenParams{ Asset: NativeAsset, @@ -836,7 +855,7 @@ func TestChannel_ConfirmPayment_responderCannotProposePaymentThatIsUnderfunded(t RemoteEscrowAccount: remoteEscrowAccount, }) - // Put the channel into the Open state. + // Put channel into the Open state. { _, err := channel.ProposeOpen(OpenParams{ Asset: NativeAsset, @@ -1044,27 +1063,29 @@ func TestChannel_ProposeAndConfirmPayment_rejectIfChannelNotOpen(t *testing.T) { require.NoError(t, err) // Put channel into the Open state. - ftx, err := senderChannel.OpenTx() - require.NoError(t, err) - ftxXDR, err := ftx.Base64() - require.NoError(t, err) + { + ftx, err := senderChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) - successResultXDR, err := txbuildtest.BuildResultXDR(true) - require.NoError(t, err) - resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ - InitiatorSigner: localSigner.Address(), - ResponderSigner: remoteSigner.Address(), - InitiatorEscrow: localEscrowAccount.Address.Address(), - ResponderEscrow: remoteEscrowAccount.Address.Address(), - StartSequence: localEscrowAccount.SequenceNumber + 1, - Asset: txnbuild.NativeAsset{}, - }) - require.NoError(t, err) + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) - err = senderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) - require.NoError(t, err) - err = receiverChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) - require.NoError(t, err) + err = senderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = receiverChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } // Sender proposes coordinated close. ca, err := senderChannel.ProposeClose() @@ -1164,27 +1185,29 @@ func TestChannel_enforceOnlyOneCloseAgreementAllowed(t *testing.T) { require.NoError(t, err) // Put channel into the Open state. - ftx, err := senderChannel.OpenTx() - require.NoError(t, err) - ftxXDR, err := ftx.Base64() - require.NoError(t, err) + { + ftx, err := senderChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) - successResultXDR, err := txbuildtest.BuildResultXDR(true) - require.NoError(t, err) - resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ - InitiatorSigner: localSigner.Address(), - ResponderSigner: remoteSigner.Address(), - InitiatorEscrow: localEscrowAccount.Address.Address(), - ResponderEscrow: remoteEscrowAccount.Address.Address(), - StartSequence: localEscrowAccount.SequenceNumber + 1, - Asset: txnbuild.NativeAsset{}, - }) - require.NoError(t, err) + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) - err = senderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) - require.NoError(t, err) - err = receiverChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) - require.NoError(t, err) + err = senderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = receiverChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } senderChannel.UpdateLocalEscrowAccountBalance(1000) senderChannel.UpdateRemoteEscrowAccountBalance(1000) From 687c88a9ca0a1a8e648303b0c70af84a4c288413 Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 21:27:25 -0700 Subject: [PATCH 10/13] cleanup --- sdk/state/payment_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/state/payment_test.go b/sdk/state/payment_test.go index 6d4059b4..ab59cb6d 100644 --- a/sdk/state/payment_test.go +++ b/sdk/state/payment_test.go @@ -218,7 +218,7 @@ func TestChannel_ConfirmPayment_acceptsSameObservationPeriod(t *testing.T) { SequenceNumber: int64(202), } - // Given a channel with observation periods set to 1, that is already open. + // Given a channel with observation periods set to 1. channel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, Initiator: true, @@ -392,7 +392,7 @@ func TestChannel_ConfirmPayment_localWhoIsInitiatorRejectsPaymentToRemoteWhoIsRe SequenceNumber: int64(202), } - // Given a channel with observation periods set to 1, that is already open. + // Given a channel with observation periods set to 1. channel := NewChannel(Config{ NetworkPassphrase: network.TestNetworkPassphrase, Initiator: true, From 96652002aec0025f8614a5c20c92b6c1a175b649 Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 21:29:48 -0700 Subject: [PATCH 11/13] cleanup --- sdk/state/payment_test.go | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/sdk/state/payment_test.go b/sdk/state/payment_test.go index ab59cb6d..24b7a7f9 100644 --- a/sdk/state/payment_test.go +++ b/sdk/state/payment_test.go @@ -227,17 +227,16 @@ func TestChannel_ConfirmPayment_acceptsSameObservationPeriod(t *testing.T) { LocalEscrowAccount: localEscrowAccount, RemoteEscrowAccount: remoteEscrowAccount, }) - channel.latestAuthorizedCloseAgreement = CloseAgreement{ - Details: CloseAgreementDetails{ - ObservationPeriodTime: 1, - ObservationPeriodLedgerGap: 1, - ConfirmingSigner: localSigner.FromAddress(), - }, - } // Put channel into the Open state. { - ftx, err := senderChannel.OpenTx() + _, err := channel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + }) + require.NoError(t, err) + + ftx, err := channel.OpenTx() require.NoError(t, err) ftxXDR, err := ftx.Base64() require.NoError(t, err) @@ -254,15 +253,21 @@ func TestChannel_ConfirmPayment_acceptsSameObservationPeriod(t *testing.T) { }) require.NoError(t, err) - err = senderChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) - require.NoError(t, err) - err = receiverChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + err = channel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) require.NoError(t, err) } // A close agreement from the remote participant should be accepted if the // observation period matches the channels observation period. { + channel.latestAuthorizedCloseAgreement = CloseAgreement{ + Details: CloseAgreementDetails{ + ObservationPeriodTime: 1, + ObservationPeriodLedgerGap: 1, + ConfirmingSigner: localSigner.FromAddress(), + }, + } + txDecl, txClose, err := channel.closeTxs(channel.openAgreement.Details, CloseAgreementDetails{ IterationNumber: 1, ObservationPeriodTime: 1, From 27032bb59014982f97baf2020f991bcd3600671b Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 21:33:45 -0700 Subject: [PATCH 12/13] cleanup --- sdk/state/payment_test.go | 64 ++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/sdk/state/payment_test.go b/sdk/state/payment_test.go index 24b7a7f9..693e9fe4 100644 --- a/sdk/state/payment_test.go +++ b/sdk/state/payment_test.go @@ -943,40 +943,42 @@ func TestLastConfirmedPayment(t *testing.T) { MaxOpenExpiry: 2 * time.Hour, }) - // Open steps. - m, err := sendingChannel.ProposeOpen(OpenParams{ - Asset: NativeAsset, - ExpiresAt: time.Now().Add(5 * time.Minute), - ObservationPeriodTime: 10, - ObservationPeriodLedgerGap: 10, - }) - require.NoError(t, err) - m, err = receiverChannel.ConfirmOpen(m) - require.NoError(t, err) - _, err = sendingChannel.ConfirmOpen(m) - require.NoError(t, err) + // Put channel into the Open state. + { + m, err := sendingChannel.ProposeOpen(OpenParams{ + Asset: NativeAsset, + ExpiresAt: time.Now().Add(5 * time.Minute), + ObservationPeriodTime: 10, + ObservationPeriodLedgerGap: 10, + }) + require.NoError(t, err) + m, err = receiverChannel.ConfirmOpen(m) + require.NoError(t, err) + _, err = sendingChannel.ConfirmOpen(m) + require.NoError(t, err) - ftx, err := sendingChannel.OpenTx() - require.NoError(t, err) - ftxXDR, err := ftx.Base64() - require.NoError(t, err) + ftx, err := sendingChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) - successResultXDR, err := txbuildtest.BuildResultXDR(true) - require.NoError(t, err) - resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ - InitiatorSigner: localSigner.Address(), - ResponderSigner: remoteSigner.Address(), - InitiatorEscrow: localEscrowAccount.Address.Address(), - ResponderEscrow: remoteEscrowAccount.Address.Address(), - StartSequence: localEscrowAccount.SequenceNumber + 1, - Asset: txnbuild.NativeAsset{}, - }) - require.NoError(t, err) + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) - err = sendingChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) - require.NoError(t, err) - err = receiverChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) - require.NoError(t, err) + err = sendingChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = receiverChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } sendingChannel.UpdateLocalEscrowAccountBalance(1000) sendingChannel.UpdateRemoteEscrowAccountBalance(1000) From 25bb7846249dde839ff519cf5850010e6e6b609d Mon Sep 17 00:00:00 2001 From: Alec Charbonneau Date: Tue, 31 Aug 2021 21:38:57 -0700 Subject: [PATCH 13/13] fix --- sdk/state/close_test.go | 49 ++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/sdk/state/close_test.go b/sdk/state/close_test.go index ab2902a6..f5a7a4b3 100644 --- a/sdk/state/close_test.go +++ b/sdk/state/close_test.go @@ -4,8 +4,10 @@ import ( "testing" "time" + "github.com/stellar/experimental-payment-channels/sdk/txbuildtest" "github.com/stellar/go/keypair" "github.com/stellar/go/network" + "github.com/stellar/go/txnbuild" "github.com/stellar/go/xdr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -107,20 +109,41 @@ func TestChannel_ProposeClose(t *testing.T) { MaxOpenExpiry: 2 * time.Hour, }) - open1, err := localChannel.ProposeOpen(OpenParams{ - ObservationPeriodTime: 1, - ObservationPeriodLedgerGap: 1, - ExpiresAt: time.Now().Add(time.Hour), - }) - require.NoError(t, err) - open2, err := remoteChannel.ConfirmOpen(open1) - require.NoError(t, err) - _, err = localChannel.ConfirmOpen(open2) - require.NoError(t, err) + // Put channel into the Open state. + { + open1, err := localChannel.ProposeOpen(OpenParams{ + ObservationPeriodTime: 1, + ObservationPeriodLedgerGap: 1, + ExpiresAt: time.Now().Add(time.Hour), + }) + require.NoError(t, err) + open2, err := remoteChannel.ConfirmOpen(open1) + require.NoError(t, err) + _, err = localChannel.ConfirmOpen(open2) + require.NoError(t, err) + + ftx, err := localChannel.OpenTx() + require.NoError(t, err) + ftxXDR, err := ftx.Base64() + require.NoError(t, err) - // Put channels into the Open state. - localChannel.openExecutedAndValidated = true - remoteChannel.openExecutedAndValidated = true + successResultXDR, err := txbuildtest.BuildResultXDR(true) + require.NoError(t, err) + resultMetaXDR, err := txbuildtest.BuildFormationResultMetaXDR(txbuildtest.FormationResultMetaParams{ + InitiatorSigner: localSigner.Address(), + ResponderSigner: remoteSigner.Address(), + InitiatorEscrow: localEscrowAccount.Address.Address(), + ResponderEscrow: remoteEscrowAccount.Address.Address(), + StartSequence: localEscrowAccount.SequenceNumber + 1, + Asset: txnbuild.NativeAsset{}, + }) + require.NoError(t, err) + + err = localChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + err = remoteChannel.IngestTx(ftxXDR, successResultXDR, resultMetaXDR) + require.NoError(t, err) + } // If the local proposes a close, the agreement will have them as the proposer. closeByLocal, err := localChannel.ProposeClose()