Skip to content
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
10 changes: 5 additions & 5 deletions .github/workflows/horizon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ jobs:
env:
HORIZON_INTEGRATION_TESTS_ENABLED: true
HORIZON_INTEGRATION_TESTS_CORE_MAX_SUPPORTED_PROTOCOL: ${{ matrix.protocol-version }}
PROTOCOL_22_CORE_DEBIAN_PKG_VERSION: 23.0.0-2587.rc4.dcf366957.focal
PROTOCOL_22_CORE_DOCKER_IMG: stellar/unsafe-stellar-core:23.0.0-2587.rc4.dcf366957.focal
PROTOCOL_22_CORE_DEBIAN_PKG_VERSION: 22.4.2-2603.136aea451.focal
PROTOCOL_22_CORE_DOCKER_IMG: stellar/unsafe-stellar-core:22.4.2-2603.136aea451.focal
PROTOCOL_22_STELLAR_RPC_DOCKER_IMG: stellar/stellar-rpc:23.0.0-rc2-126
PROTOCOL_23_CORE_DEBIAN_PKG_VERSION: 23.0.0-2587.rc4.dcf366957.focal
PROTOCOL_23_CORE_DOCKER_IMG: stellar/unsafe-stellar-core:23.0.0-2587.rc4.dcf366957.focal
PROTOCOL_23_CORE_DEBIAN_PKG_VERSION: 22.4.2-2603.136aea451.focal
PROTOCOL_23_CORE_DOCKER_IMG: stellar/unsafe-stellar-core:22.4.2-2603.136aea451.focal
PROTOCOL_23_STELLAR_RPC_DOCKER_IMG: stellar/stellar-rpc:23.0.0-rc2-126
PGHOST: localhost
PGPORT: 5432
Expand Down Expand Up @@ -126,7 +126,7 @@ jobs:
runs-on: ubuntu-22.04
env:
GO_VERSION: 1.23.4
STELLAR_CORE_VERSION: 23.0.0-2587.rc4.dcf366957.jammy
STELLAR_CORE_VERSION: 22.4.2-2603.136aea451.jammy
steps:
- uses: actions/checkout@v3
with:
Expand Down
203 changes: 203 additions & 0 deletions services/horizon/internal/integration/sac_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"crypto/sha256"
"math"
"math/big"
"strconv"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -812,6 +813,129 @@
})
}

func TestParallelSACTransfer(t *testing.T) {

Check failure on line 816 in services/horizon/internal/integration/sac_test.go

View workflow job for this annotation

GitHub Actions / golangci

Function 'TestParallelSACTransfer' has too many statements (54 > 50) (funlen)
if integration.GetCoreMaxSupportedProtocol() < 23 {
t.Skip("This test run does not support less than Protocol 23")
}

itest := integration.NewTest(t, integration.Config{
EnableStellarRPC: true,
HorizonIngestParameters: map[string]string{
// disable state verification because we will insert
// a fake asset contract in the horizon db and we don't
// want state verification to detect this
"ingest-disable-state-verification": "true",
},
QuickEviction: true,
})

issuer := itest.Master().Address()
code := "USD"
asset := xdr.MustNewCreditAsset(code, issuer)

createSAC(itest, asset)

accountKeys, accounts := itest.CreateAccounts(2, "100")
recipientKp, recipient := accountKeys[0], accounts[0]
senderKp, sender := accountKeys[1], accounts[1]
itest.MustEstablishTrustline(recipientKp, recipient, txnbuild.MustAssetFromXDR(asset))
itest.MustEstablishTrustline(senderKp, sender, txnbuild.MustAssetFromXDR(asset))

itest.MustSubmitOperations(
itest.MasterAccount(),
itest.Master(),
&txnbuild.Payment{
SourceAccount: issuer,
Destination: senderKp.Address(),
Asset: txnbuild.CreditAsset{
Code: code,
Issuer: issuer,
},
Amount: "2000",
},
)

assertAssetStats(itest, assetStats{
code: code,
issuer: issuer,
numAccounts: 2,
balanceAccounts: 0,
numContracts: 0,
balanceContracts: big.NewInt(0),
contractID: stellarAssetContractID(itest, asset),
})

i := 0
for {
// tx to remove evicted balance
transferOp := itest.PreflightHostFunctions(sender,
*transfer(itest, senderKp.Address(), asset, "3", accountAddressParam(recipient.GetAccountID())),
)
mintOp := itest.PreflightHostFunctions(itest.MasterAccount(),
*transfer(itest, itest.Master().Address(), asset, "6", accountAddressParam(recipient.GetAccountID())),
)

mintTx, err := itest.CreateSignedTransactionFromOps(itest.MasterAccount(), []*keypair.Full{itest.Master()}, &mintOp)
require.NoError(t, err)

account := itest.MustGetAccount(senderKp)
transferTx, err := itest.CreateSignedTransactionFromOps(&account, []*keypair.Full{senderKp}, &transferOp)
require.NoError(t, err)

responses, err := itest.SubmitTransactions([]*txnbuild.Transaction{mintTx, transferTx})
require.NoError(t, err)
require.True(t, responses[0].Successful)
require.True(t, responses[1].Successful)

masterAccount := itest.MustGetAccount(itest.Master())
recipientAccount := itest.MustGetAccount(recipientKp)
senderAccount := itest.MustGetAccount(senderKp)

require.Equal(t, masterAccount.LastModifiedLedger, uint32(responses[0].Ledger))

Check failure on line 894 in services/horizon/internal/integration/sac_test.go

View workflow job for this annotation

GitHub Actions / golangci

G115: integer overflow conversion int32 -> uint32 (gosec)
require.Equal(t, senderAccount.LastModifiedLedger, uint32(responses[1].Ledger))

Check failure on line 895 in services/horizon/internal/integration/sac_test.go

View workflow job for this annotation

GitHub Actions / golangci

G115: integer overflow conversion int32 -> uint32 (gosec)

var found bool
for _, balance := range senderAccount.Balances {
if balance.Issuer != issuer || balance.Code != code {
continue
}
found = true
require.Equal(t, balance.LastModifiedLedger, uint32(responses[1].Ledger))

Check failure on line 903 in services/horizon/internal/integration/sac_test.go

View workflow job for this annotation

GitHub Actions / golangci

G115: integer overflow conversion int32 -> uint32 (gosec)
}
require.True(t, found)

found = false
for _, balance := range recipientAccount.Balances {
if balance.Issuer != issuer || balance.Code != code {
continue
}
found = true
require.Equal(t, balance.LastModifiedLedger, max(uint32(responses[1].Ledger), uint32(responses[0].Ledger)))
}
require.True(t, found)

if responses[0].Ledger == responses[1].Ledger {
break
}
if i >= 100 {
require.Fail(t, "could not produce ledger with both transfers")
}
i++
time.Sleep(time.Second)
}

expectedAmount := strconv.Itoa(90 * (i + 1))
assertAssetStats(itest, assetStats{
code: code,
issuer: issuer,
numAccounts: 2,
balanceAccounts: amount.MustParse(expectedAmount),
numContracts: 0,
balanceContracts: big.NewInt(0),
contractID: stellarAssetContractID(itest, asset),
})
}

func TestEvictionOfSACWithActiveBalance(t *testing.T) {
if integration.GetCoreMaxSupportedProtocol() < 23 {
t.Skip("This test run does not support less than Protocol 23")
Expand Down Expand Up @@ -1052,6 +1176,85 @@
})
}

func TestEvictionOfSACAndAutoRestore(t *testing.T) {
if integration.GetCoreMaxSupportedProtocol() < 23 {
t.Skip("This test run does not support less than Protocol 23")
}

itest := integration.NewTest(t, integration.Config{
EnableStellarRPC: true,
HorizonIngestParameters: map[string]string{
// disable state verification because we will insert
// a fake asset contract in the horizon db and we don't
// want state verification to detect this
"ingest-disable-state-verification": "true",
},
QuickEviction: true,
})

issuer := itest.Master().Address()
code := "USD"
asset := xdr.MustNewCreditAsset(code, issuer)

recipientKp, recipient := itest.CreateAccount("100")
itest.MustEstablishTrustline(recipientKp, recipient, txnbuild.MustAssetFromXDR(asset))

createSACWithTTL(itest, asset, 30)
contractID := stellarAssetContractID(itest, asset)

assertAssetStats(itest, assetStats{
code: code,
issuer: issuer,
numAccounts: 1,
balanceAccounts: 0,
numContracts: 0,
balanceContracts: big.NewInt(0),
contractID: contractID,
})

contractToEvictLedgerKey := xdr.LedgerKey{
Type: xdr.LedgerEntryTypeContractData,
ContractData: &xdr.LedgerKeyContractData{
Contract: xdr.ScAddress{
Type: xdr.ScAddressTypeScAddressTypeContract,
AccountId: nil,
ContractId: &contractID,
},
Key: xdr.ScVal{
Type: xdr.ScValTypeScvLedgerKeyContractInstance,
},
Durability: xdr.ContractDataDurabilityPersistent,
},
}
itest.WaitUntilLedgerEntryIsEvicted(contractToEvictLedgerKey, time.Minute)

assertAssetStats(itest, assetStats{
code: code,
issuer: issuer,
numAccounts: 1,
balanceAccounts: amount.MustParse("0"),
numContracts: 0,
balanceContracts: big.NewInt(0),
})

// auto restore SAC via transfer()
assertInvokeHostFnSucceeds(
itest,
itest.Master(),
transfer(itest, itest.Master().Address(), asset, "6", accountAddressParam(recipient.GetAccountID())),
)

assertAssetStats(itest, assetStats{
code: code,
issuer: issuer,
numAccounts: 1,
balanceAccounts: amount.MustParse("6"),
numContracts: 0,
balanceContracts: big.NewInt(0),
contractID: contractID,
})
}

func invokeStoreSet(
itest *integration.Test,
storeContractID xdr.ContractId,
Expand Down
50 changes: 49 additions & 1 deletion services/horizon/internal/test/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"github.com/stellar/go/clients/stellarcore"
"github.com/stellar/go/keypair"
proto "github.com/stellar/go/protocols/horizon"
coreproto "github.com/stellar/go/protocols/stellarcore"

Check failure on line 39 in services/horizon/internal/test/integration/integration.go

View workflow job for this annotation

GitHub Actions / golangci

import 'github.com/stellar/go/protocols/stellarcore' is not allowed from list 'Main' (depguard)
horizoncmd "github.com/stellar/go/services/horizon/cmd"
horizon "github.com/stellar/go/services/horizon/internal"
"github.com/stellar/go/services/horizon/internal/ingest"
Expand Down Expand Up @@ -173,7 +174,7 @@
}
if config.QuickEviction {
validatorParams.OverrideEvictionParamsForTesting = true
validatorParams.TestingStartingEvictionScanLevel = 2
validatorParams.TestingStartingEvictionScanLevel = 1
validatorParams.TestingMaxEntriesToArchive = 100
// QuickEviction implies QuickExpiration
config.QuickExpiration = true
Expand Down Expand Up @@ -1450,6 +1451,53 @@
return i.Client().AsyncSubmitTransaction(tx)
}

func (i *Test) SubmitTransactions(transactions []*txnbuild.Transaction) ([]proto.Transaction, error) {
var results []proto.Transaction
byHash := make(map[string]proto.Transaction)
for _, tx := range transactions {
response, err := i.Client().AsyncSubmitTransaction(tx)
if err != nil {
return nil, err
}
if response.TxStatus != coreproto.TXStatusPending {
return nil, fmt.Errorf("transaction status is %s", response.TxStatus)
}
}
require.Eventually(i.t, func() bool {
for _, tx := range transactions {
hash, err := tx.HashHex(i.passPhrase)
if err != nil {
continue
}
if _, ok := byHash[hash]; ok {
continue
}
response, err := i.Client().TransactionDetail(hash)
if err != nil {
continue
}
byHash[hash] = response
}
return len(byHash) == len(transactions)
}, time.Minute, time.Second)

if len(byHash) != len(transactions) {
return nil, fmt.Errorf("expected %d responses, got %d", len(transactions), len(byHash))
}
for _, tx := range transactions {
hash, err := tx.HashHex(i.passPhrase)
if err != nil {
return nil, err
}
response, ok := byHash[hash]
if !ok {
return nil, fmt.Errorf("transaction %s not found", hash)
}
results = append(results, response)
}
return results, nil
}

func (i *Test) MustSubmitMultiSigTransaction(
signers []*keypair.Full, txParams txnbuild.TransactionParams,
) proto.Transaction {
Expand Down
Loading