Skip to content
This repository has been archived by the owner on Apr 23, 2024. It is now read-only.

sdk/state: new state StateClosedWithOutdatedState #324

Merged
merged 10 commits into from
Sep 14, 2021
2 changes: 1 addition & 1 deletion sdk/state/ingest.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (c *Channel) IngestTx(txOrderID int64, txXDR, resultXDR, resultMetaXDR stri
if err != nil {
return fmt.Errorf("getting channel state: %w", err)
}
if cs == StateClosed {
if cs == StateClosed || cs == StateClosedWithOutdatedState {
return fmt.Errorf("channel has been closed")
}

Expand Down
108 changes: 108 additions & 0 deletions sdk/state/ingest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1322,3 +1322,111 @@ func TestChannel_IngestTx_OpenClose(t *testing.T) {
err = initiatorChannel.IngestTx(4, "", "", "")
require.EqualError(t, err, "channel has been closed")
}

func TestChannel_IngestTx_ingestOldTransactions(t *testing.T) {
initiatorSigner := keypair.MustRandom()
responderSigner := keypair.MustRandom()
initiatorEscrow := keypair.MustRandom().FromAddress()
responderEscrow := keypair.MustRandom().FromAddress()

// Given a channel with observation periods set to 1.
initiatorChannel := NewChannel(Config{
NetworkPassphrase: network.TestNetworkPassphrase,
Initiator: true,
LocalSigner: initiatorSigner,
RemoteSigner: responderSigner.FromAddress(),
LocalEscrowAccount: initiatorEscrow,
RemoteEscrowAccount: responderEscrow,
MaxOpenExpiry: 2 * time.Hour,
})
responderChannel := NewChannel(Config{
NetworkPassphrase: network.TestNetworkPassphrase,
Initiator: false,
LocalSigner: responderSigner,
RemoteSigner: initiatorSigner.FromAddress(),
LocalEscrowAccount: responderEscrow,
RemoteEscrowAccount: initiatorEscrow,
MaxOpenExpiry: 2 * time.Hour,
})

// Put channel into the Open state.
{
m, err := initiatorChannel.ProposeOpen(OpenParams{
Asset: NativeAsset,
ExpiresAt: time.Now().Add(5 * time.Minute),
StartingSequence: 101,
ObservationPeriodTime: 10,
ObservationPeriodLedgerGap: 10,
})
require.NoError(t, err)
m, err = responderChannel.ConfirmOpen(m.Envelope)
require.NoError(t, err)
_, err = initiatorChannel.ConfirmOpen(m.Envelope)
require.NoError(t, err)

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: initiatorSigner.Address(),
ResponderSigner: responderSigner.Address(),
InitiatorEscrow: initiatorEscrow.Address(),
ResponderEscrow: responderEscrow.Address(),
StartSequence: 101,
Asset: txnbuild.NativeAsset{},
})
require.NoError(t, err)

err = initiatorChannel.IngestTx(2, ftxXDR, successResultXDR, resultMetaXDR)
require.NoError(t, err)
err = responderChannel.IngestTx(2, ftxXDR, successResultXDR, resultMetaXDR)
require.NoError(t, err)
}
initiatorChannel.UpdateLocalEscrowAccountBalance(100)
responderChannel.UpdateRemoteEscrowAccountBalance(100)

placeholderXDR := "AAAAAgAAAAIAAAADABArWwAAAAAAAAAAWPnYf+6kQN3t44vgesQdWh4JOOPj7aer852I7RJhtzAAAAAWg8TZOwANrPwAAAAKAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABABArWwAAAAAAAAAAWPnYf+6kQN3t44vgesQdWh4JOOPj7aer852I7RJhtzAAAAAWg8TZOwANrPwAAAALAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAABAAAAAMAD/39AAAAAAAAAAD49aUpVx7fhJPK6wDdlPJgkA1HkAi85qUL1tii8YSZzQAAABdjSVwcAA/8sgAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAECtbAAAAAAAAAAD49aUpVx7fhJPK6wDdlPJgkA1HkAi85qUL1tii8YSZzQAAABee5CYcAA/8sgAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAMAECtbAAAAAAAAAABY+dh/7qRA3e3ji+B6xB1aHgk44+Ptp6vznYjtEmG3MAAAABaDxNk7AA2s/AAAAAsAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAECtbAAAAAAAAAABY+dh/7qRA3e3ji+B6xB1aHgk44+Ptp6vznYjtEmG3MAAAABZIKg87AA2s/AAAAAsAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA="
validResultXDR, err := txbuildtest.BuildResultXDR(true)
require.NoError(t, err)

oldDeclTx, oldCloseTx, err := responderChannel.CloseTxs()
require.NoError(t, err)
oldDeclXDR, err := oldDeclTx.Base64()
require.NoError(t, err)
oldCloseXDR, err := oldCloseTx.Base64()
require.NoError(t, err)

// New payment.
{
close, err := initiatorChannel.ProposePayment(8)
require.NoError(t, err)
close, err = responderChannel.ConfirmPayment(close.Envelope)
require.NoError(t, err)
_, err = initiatorChannel.ConfirmPayment(close.Envelope)
require.NoError(t, err)
}

// Close channel with old transactions.
{
err = initiatorChannel.IngestTx(1, oldDeclXDR, validResultXDR, placeholderXDR)
require.NoError(t, err)
cs, err := initiatorChannel.State()
require.NoError(t, err)
assert.Equal(t, StateClosingWithOutdatedState, cs)

err = initiatorChannel.IngestTx(1, oldCloseXDR, validResultXDR, placeholderXDR)
require.NoError(t, err)
cs, err = initiatorChannel.State()
require.NoError(t, err)
assert.Equal(t, StateClosedWithOutdatedState, cs)
}

// Once closed with old closeTx, ingesting new transactions should error.
err = initiatorChannel.IngestTx(1, oldCloseXDR, validResultXDR, placeholderXDR)
require.EqualError(t, err, "channel has been closed")
}
11 changes: 9 additions & 2 deletions sdk/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"time"

"github.com/stellar/experimental-payment-channels/sdk/txbuild"
"github.com/stellar/go/keypair"
"github.com/stellar/go/network"
"github.com/stellar/go/txnbuild"
Expand Down Expand Up @@ -135,6 +136,7 @@ const (
StateNone
StateOpen
StateClosingWithOutdatedState
StateClosedWithOutdatedState
StateClosing
StateClosed
)
Expand All @@ -161,11 +163,16 @@ func (c *Channel) State() (State, error) {
latestCloseSequence := txs.Close.SequenceNumber()

initiatorEscrowSeqNum := c.initiatorEscrowAccount().SequenceNumber
s := c.openAgreement.Envelope.Details.StartingSequence

if initiatorEscrowSeqNum == c.openAgreement.Envelope.Details.StartingSequence {
if initiatorEscrowSeqNum == s {
return StateOpen, nil
} else if initiatorEscrowSeqNum < latestDeclSequence {
} else if initiatorEscrowSeqNum < latestDeclSequence &&
txbuild.SequenceNumberToTransactionType(s, initiatorEscrowSeqNum) == txbuild.TransactionTypeDeclaration {
return StateClosingWithOutdatedState, nil
} else if initiatorEscrowSeqNum < latestDeclSequence &&
txbuild.SequenceNumberToTransactionType(s, initiatorEscrowSeqNum) == txbuild.TransactionTypeClose {
return StateClosedWithOutdatedState, nil
} else if initiatorEscrowSeqNum == latestDeclSequence {
return StateClosing, nil
} else if initiatorEscrowSeqNum >= latestCloseSequence {
Expand Down
14 changes: 14 additions & 0 deletions sdk/txbuild/sequence.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,17 @@ const m = 2
func startSequenceOfIteration(startSequence int64, iterationNumber int64) int64 {
return startSequence + iterationNumber*m
}

type TransactionType string

const (
TransactionTypeDeclaration TransactionType = "declaration"
TransactionTypeClose TransactionType = "close"
)

func SequenceNumberToTransactionType(startingSeqNum, seqNum int64) TransactionType {
if startingSeqNum%m == seqNum%m {
return TransactionTypeDeclaration
}
return TransactionTypeClose
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some tests that demonstrate even and odd starting sequences, and then both tx types? 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a good idea c1426f1

writing the test made me think that we should add a TransactionTypeOpen (if they're the same)

also there's 1 unique case of the startingSequence + 1, which isn't a transaction type. Maybe we should add a type for that as well (it would be something like TransactionTypeNone)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added them here
d5e1329

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can focus on the transaction types being for close agreements then, because I don't think we get much bang out of adding other types.

I wouldn't worry about adding the none case either, we don't sign txs with those sequence numbers. I'd just panic in that case, it's code that should never be hit.

Copy link
Contributor Author

@acharb acharb Sep 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the panic 0b1d1df

imo I think the TransactionTypeFormation makes sense, isn't used now but may save a bug down the line if someone means to get the formation tx seq and not the declaration tx seq 🤷

up to you though I can remove if it's added code for no reason

edit: "if they meant to be referring to a formation tx and not a declaration tx" I mean