Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

services/horizon/integration: Precondition edge cases and V18->19 upgrade boundary. #4354

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions services/horizon/internal/integration/protocol_19_upgrade_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package integration

import (
"fmt"
"strconv"
"testing"

"github.com/stellar/go/clients/horizonclient"
"github.com/stellar/go/protocols/horizon"
"github.com/stellar/go/services/horizon/internal/test/integration"
"github.com/stellar/go/txnbuild"
"github.com/stretchr/testify/assert"
)

// TestProtocol19Upgrade tests that crossing the upgrade boundary results in the
// correct behavior and no crashes.
func TestProtocol19Upgrade(t *testing.T) {
itest := integration.NewTest(t, integration.Config{ProtocolVersion: 18})

master := itest.Master()
masterAccount := itest.MasterAccount()

if integration.GetCoreMaxSupportedProtocol() < 19 {
t.Skip("This test run does not support Protocol 19")
}

// Note: These tests are combined to avoid the extra setup/teardown.

// TestTransactionPreconditionsPremature ensures that submitting
// transactions that use Protocol 19 features fail correctly.
t.Run("TestTransactionPreconditionsPremature", func(t *testing.T) {
tt := assert.New(t)

// Submit a transaction with extra preconditions set too early.
txParams := txnbuild.TransactionParams{
BaseFee: txnbuild.MinBaseFee,
SourceAccount: masterAccount,
IncrementSequenceNum: true,
// Phony operation to run
Operations: []txnbuild.Operation{
&txnbuild.Payment{
Destination: masterAccount.GetAccountID(),
Amount: "10",
Asset: txnbuild.NativeAsset{},
},
},
Preconditions: txnbuild.Preconditions{
TimeBounds: txnbuild.NewInfiniteTimeout(),
LedgerBounds: &txnbuild.LedgerBounds{0, 100},
},
}
_, err := itest.SubmitTransaction(master, txParams)

tt.Error(err)
if prob := horizonclient.GetError(err); prob != nil {
if results, ok := prob.Problem.Extras["result_codes"].(map[string]interface{}); ok {
fmt.Println("RESULTS ARE HERE")
fmt.Println(results)
tt.Equal("tx_not_supported", results["transaction"])
} else {
tt.FailNow("result_codes couldn't be parsed: %+v", results)
}
} else {
tt.Error(prob)
}
})

// TestTransactionAccountV3Upgrade ensures that upgrading over the
// Protocol 19 boundary correctly adds the V3 fields.
t.Run("TestTransactionAccountV3Upgrade", func(t *testing.T) {
var account horizon.Account
tt := assert.New(t)

// Submit phony operation which should bump the sequence number but not
// actually track it in the extension.
tx := submitPhonyOp(itest)
account = itest.MasterAccountDetails()

// Check that the account response has V3 fields omitted.
tt.EqualValues(0, account.SequenceLedger)
tt.Equal("0", account.SequenceTime)

itest.UpgradeProtocol(19)

// Submit phony operation which should trigger the new fields.
tx = submitPhonyOp(itest)

// Refresh master account and check that the account response has the new
// AccountV3 fields
account = itest.MasterAccountDetails()
tt.Equal(uint32(tx.Ledger), account.SequenceLedger)
tt.Equal(strconv.FormatInt(tx.LedgerCloseTime.Unix(), 10), account.SequenceTime)
})
}

func submitPhonyOp(itest *integration.Test) horizon.Transaction {
master := itest.Master()
account := itest.MasterAccount()

return itest.MustSubmitOperations(account, master,
&txnbuild.Payment{
Destination: master.Address(),
Amount: "10",
Asset: txnbuild.NativeAsset{},
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/base64"
"fmt"
"math"
"strconv"
"sync"
"testing"
Expand Down Expand Up @@ -289,55 +290,6 @@ func TestTransactionPreconditionsMinSequenceNumberLedgerGap(t *testing.T) {
assert.Equal(t, txHistory.Preconditions.MinAccountSequenceLedgerGap, txParams.Preconditions.MinSequenceNumberLedgerGap)
}

func buildTXParams(master *keypair.Full, masterAccount txnbuild.Account, txSequence int64) txnbuild.TransactionParams {
return txnbuild.TransactionParams{
SourceAccount: &txnbuild.SimpleAccount{
AccountID: masterAccount.GetAccountID(),
Sequence: txSequence,
},
// Phony operation to run
Operations: []txnbuild.Operation{
&txnbuild.Payment{
Destination: master.Address(),
Amount: "10",
Asset: txnbuild.NativeAsset{},
},
},
BaseFee: txnbuild.MinBaseFee,
Memo: nil,
Preconditions: txnbuild.Preconditions{
TimeBounds: txnbuild.NewInfiniteTimeout(),
},
}
}

func TestTransactionPreconditionsAccountV3Fields(t *testing.T) {
tt := assert.New(t)
itest := integration.NewTest(t, integration.Config{})
if itest.GetEffectiveProtocolVersion() < 19 {
t.Skip("Can't run with protocol < 19")
}
master := itest.Master()
masterAccount := itest.MasterAccount()

// Submit phony operation
tx := itest.MustSubmitOperations(masterAccount, master,
&txnbuild.Payment{
Destination: master.Address(),
Amount: "10",
Asset: txnbuild.NativeAsset{},
},
)

// refresh master account
account, err := itest.Client().AccountDetail(sdk.AccountRequest{AccountID: master.Address()})
assert.NoError(t, err)

// Check that the account response has the new AccountV3 fields
tt.Equal(uint32(tx.Ledger), account.SequenceLedger)
tt.Equal(strconv.FormatInt(tx.LedgerCloseTime.Unix(), 10), account.SequenceTime)
}

// TestTransactionWithoutPreconditions ensures that Horizon doesn't break when
// we have a PRECOND_NONE type transaction (which is not possible to submit
// through SDKs, but is absolutely still possible).
Expand Down Expand Up @@ -398,15 +350,69 @@ func TestTransactionWithoutPreconditions(t *testing.T) {
txResp, err := itest.Client().SubmitTransactionXDR(b64)
tt.NoError(err)

fmt.Println(
"envelopeXDR", txResp.EnvelopeXdr,
"resultXDR", txResp.ResultXdr,
// "feeChangesXDR", txResp.feeChangesXDR,
"metaXDR", txResp.FeeMetaXdr,
"hash", txResp.Hash,
)

txResp2, err := itest.Client().TransactionDetail(txResp.Hash)
tt.NoError(err)
tt.Nil(txResp2.Preconditions)
}

func TestTransactionPreconditionsEdgeCases(t *testing.T) {
tt := assert.New(t)
itest := integration.NewTest(t, integration.Config{})
if itest.GetEffectiveProtocolVersion() < 19 {
t.Skip("Can't run with protocol < 19")
}
master := itest.Master()
masterAccount := itest.MasterAccount()

maxMinSeq := int64(math.MaxInt64)
preconditionTests := []txnbuild.Preconditions{
{LedgerBounds: &txnbuild.LedgerBounds{1, 0}},
{LedgerBounds: &txnbuild.LedgerBounds{0, math.MaxUint32}},
{LedgerBounds: &txnbuild.LedgerBounds{math.MaxUint32, 1}},
{
LedgerBounds: &txnbuild.LedgerBounds{math.MaxUint32, 1},
ExtraSigners: []string{},
},
{
MinSequenceNumber: &maxMinSeq,
MinSequenceNumberLedgerGap: math.MaxUint32,
MinSequenceNumberAge: math.MaxUint64,
ExtraSigners: nil,
},
}

for _, precondition := range preconditionTests {
seqNum, err := masterAccount.IncrementSequenceNumber()
tt.NoError(err)

params := buildTXParams(master, masterAccount, seqNum)
precondition.TimeBounds = txnbuild.NewInfiniteTimeout()
params.Preconditions = precondition

// The goal here is not to check for validation or errors or responses,
// but rather to just make sure the edge case doesn't crash Horizon.
itest.SubmitTransaction(master, params)
}
}

func buildTXParams(master *keypair.Full, masterAccount txnbuild.Account, txSequence int64) txnbuild.TransactionParams {
return txnbuild.TransactionParams{
SourceAccount: &txnbuild.SimpleAccount{
AccountID: masterAccount.GetAccountID(),
Sequence: txSequence,
},
// Phony operation to run
Operations: []txnbuild.Operation{
&txnbuild.Payment{
Destination: master.Address(),
Amount: "10",
Asset: txnbuild.NativeAsset{},
},
},
BaseFee: txnbuild.MinBaseFee,
Memo: nil,
Preconditions: txnbuild.Preconditions{
TimeBounds: txnbuild.NewInfiniteTimeout(),
},
}
}
14 changes: 10 additions & 4 deletions services/horizon/internal/test/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func NewTest(t *testing.T, config Config) *Test {
config.ProtocolVersion = ingest.MaxSupportedProtocolVersion
// If the environment tells us that Core only supports up to certain version,
// use that.
maxSupportedCoreProtocolFromEnv := getCoreMaxSupportedProtocol()
maxSupportedCoreProtocolFromEnv := GetCoreMaxSupportedProtocol()
if maxSupportedCoreProtocolFromEnv != 0 && maxSupportedCoreProtocolFromEnv < ingest.MaxSupportedProtocolVersion {
config.ProtocolVersion = maxSupportedCoreProtocolFromEnv
}
Expand Down Expand Up @@ -468,7 +468,8 @@ func (i *Test) WaitForHorizon() {
}

if uint32(root.CurrentProtocolVersion) == i.config.ProtocolVersion {
i.t.Logf("Horizon protocol version matches... %v", root)
i.t.Logf("Horizon protocol version matches %d: %+v",
root.CurrentProtocolVersion, root)
return
}
}
Expand Down Expand Up @@ -516,11 +517,16 @@ func (i *Test) Master() *keypair.Full {
}

func (i *Test) MasterAccount() txnbuild.Account {
account := i.MasterAccountDetails()
return &account
}

func (i *Test) MasterAccountDetails() proto.Account {
master, client := i.Master(), i.Client()
request := sdk.AccountRequest{AccountID: master.Address()}
account, err := client.AccountDetail(request)
panicIf(err)
return &account
return account
}

func (i *Test) CurrentTest() *testing.T {
Expand Down Expand Up @@ -874,7 +880,7 @@ func mapToFlags(params map[string]string) []string {
return args
}

func getCoreMaxSupportedProtocol() uint32 {
func GetCoreMaxSupportedProtocol() uint32 {
str := os.Getenv("HORIZON_INTEGRATION_TESTS_CORE_MAX_SUPPORTED_PROTOCOL")
if str == "" {
return 0
Expand Down