diff --git a/services/horizon/internal/ingest/processors/contract_data.go b/ingest/sac/contract_data.go similarity index 98% rename from services/horizon/internal/ingest/processors/contract_data.go rename to ingest/sac/contract_data.go index 7b15bd9c5b..72cbd91b8a 100644 --- a/services/horizon/internal/ingest/processors/contract_data.go +++ b/ingest/sac/contract_data.go @@ -1,4 +1,4 @@ -package processors +package sac import ( "math/big" @@ -452,7 +452,7 @@ func AssetToContractData(isNative bool, code, issuer string, contractID [32]byte // // Warning: Only for use in tests. This does not create the accompanied TTLEntry which would typically be created by core. func BalanceToContractData(assetContractId, holderID [32]byte, amt uint64) xdr.LedgerEntryData { - return balanceToContractData(assetContractId, holderID, xdr.Int128Parts{ + return BalanceInt128ToContractData(assetContractId, holderID, xdr.Int128Parts{ Lo: xdr.Uint64(amt), Hi: 0, }) @@ -488,7 +488,7 @@ func ContractBalanceLedgerKey(assetContractId, holderID [32]byte) xdr.LedgerKey } } -func balanceToContractData(assetContractId, holderID [32]byte, amt xdr.Int128Parts) xdr.LedgerEntryData { +func BalanceInt128ToContractData(assetContractId, holderID [32]byte, amt xdr.Int128Parts) xdr.LedgerEntryData { ledgerKey := ContractBalanceLedgerKey(assetContractId, holderID) amountSym := xdr.ScSymbol("amount") diff --git a/services/horizon/internal/ingest/processors/asset_stats_processor.go b/services/horizon/internal/ingest/processors/asset_stats_processor.go index 7e76b509c2..81e0f5ca06 100644 --- a/services/horizon/internal/ingest/processors/asset_stats_processor.go +++ b/services/horizon/internal/ingest/processors/asset_stats_processor.go @@ -8,6 +8,7 @@ import ( "math/big" "github.com/stellar/go/ingest" + "github.com/stellar/go/ingest/sac" "github.com/stellar/go/services/horizon/internal/db2/history" "github.com/stellar/go/support/errors" "github.com/stellar/go/xdr" @@ -74,8 +75,8 @@ func (p *AssetStatsProcessor) ProcessChange(ctx context.Context, change ingest.C if ledgerEntry == nil { ledgerEntry = change.Pre } - asset := AssetFromContractData(*ledgerEntry, p.networkPassphrase) - _, _, balanceFound := ContractBalanceFromContractData(*ledgerEntry, p.networkPassphrase) + asset := sac.AssetFromContractData(*ledgerEntry, p.networkPassphrase) + _, _, balanceFound := sac.ContractBalanceFromContractData(*ledgerEntry, p.networkPassphrase) if asset == nil && !balanceFound { return nil } diff --git a/services/horizon/internal/ingest/processors/asset_stats_processor_test.go b/services/horizon/internal/ingest/processors/asset_stats_processor_test.go index ea267f6d3e..f6d1d03f81 100644 --- a/services/horizon/internal/ingest/processors/asset_stats_processor_test.go +++ b/services/horizon/internal/ingest/processors/asset_stats_processor_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stellar/go/ingest" + "github.com/stellar/go/ingest/sac" "github.com/stellar/go/services/horizon/internal/db2/history" "github.com/stellar/go/xdr" @@ -524,12 +525,12 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertContractID() { } eurID, err := trustLine.Asset.ToAsset().ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) usdID, err := xdr.MustNewCreditAsset("USD", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - usdContractData, err := AssetToContractData(false, "USD", trustLineIssuer.Address(), usdID) + usdContractData, err := sac.AssetToContractData(false, "USD", trustLineIssuer.Address(), usdID) s.Assert().NoError(err) lastModifiedLedgerSeq := xdr.Uint32(1234) @@ -638,7 +639,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertContractBalance() { Type: xdr.LedgerEntryTypeContractData, Post: &xdr.LedgerEntry{ LastModifiedLedgerSeq: lastModifiedLedgerSeq, - Data: BalanceToContractData(usdID, [32]byte{1}, 200), + Data: sac.BalanceToContractData(usdID, [32]byte{1}, 200), }, })) @@ -704,11 +705,11 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateContractBalance() { Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ LastModifiedLedgerSeq: lastModifiedLedgerSeq, - Data: BalanceToContractData(usdID, [32]byte{1}, 100), + Data: sac.BalanceToContractData(usdID, [32]byte{1}, 100), }, Post: &xdr.LedgerEntry{ LastModifiedLedgerSeq: lastModifiedLedgerSeq, - Data: BalanceToContractData(usdID, [32]byte{1}, 300), + Data: sac.BalanceToContractData(usdID, [32]byte{1}, 300), }, })) @@ -763,7 +764,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractBalance() { Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ LastModifiedLedgerSeq: lastModifiedLedgerSeq, - Data: BalanceToContractData(usdID, [32]byte{1}, 200), + Data: sac.BalanceToContractData(usdID, [32]byte{1}, 200), }, })) @@ -821,12 +822,12 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertContractIDWithBalance() { } eurID, err := trustLine.Asset.ToAsset().ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) usdID, err := xdr.MustNewCreditAsset("USD", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - usdContractData, err := AssetToContractData(false, "USD", trustLineIssuer.Address(), usdID) + usdContractData, err := sac.AssetToContractData(false, "USD", trustLineIssuer.Address(), usdID) s.Assert().NoError(err) lastModifiedLedgerSeq := xdr.Uint32(1234) @@ -867,7 +868,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertContractIDWithBalance() { Type: xdr.LedgerEntryTypeContractData, Post: &xdr.LedgerEntry{ LastModifiedLedgerSeq: lastModifiedLedgerSeq, - Data: BalanceToContractData(usdID, [32]byte{1}, 150), + Data: sac.BalanceToContractData(usdID, [32]byte{1}, 150), }, })) @@ -876,7 +877,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertContractIDWithBalance() { Type: xdr.LedgerEntryTypeContractData, Post: &xdr.LedgerEntry{ LastModifiedLedgerSeq: lastModifiedLedgerSeq, - Data: BalanceToContractData(btcID, [32]byte{1}, 20), + Data: sac.BalanceToContractData(btcID, [32]byte{1}, 20), }, })) @@ -1096,7 +1097,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateContractID() { eurID, err := xdr.MustNewCreditAsset("EUR", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) err = s.processor.ProcessChange(s.ctx, ingest.Change{ @@ -1231,7 +1232,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateContractIDWithBalance() { eurID, err := xdr.MustNewCreditAsset("EUR", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) err = s.processor.ProcessChange(s.ctx, ingest.Change{ @@ -1247,7 +1248,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateContractIDWithBalance() { Type: xdr.LedgerEntryTypeContractData, Post: &xdr.LedgerEntry{ LastModifiedLedgerSeq: lastModifiedLedgerSeq, - Data: BalanceToContractData(eurID, [32]byte{1}, 150), + Data: sac.BalanceToContractData(eurID, [32]byte{1}, 150), }, })) keyHash := getKeyHashForBalance(s.T(), eurID, [32]byte{1}) @@ -1350,7 +1351,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateContractIDError() { s.Assert().NoError(err) eurID, err := xdr.MustNewCreditAsset("EUR", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) err = s.processor.ProcessChange(s.ctx, ingest.Change{ @@ -1395,7 +1396,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateTrustlineAndContractIDErr s.Assert().NoError(err) eurID, err := xdr.MustNewCreditAsset("EUR", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) err = s.processor.ProcessChange(s.ctx, ingest.Change{ @@ -1470,7 +1471,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractIDError() { eurID, err := xdr.MustNewCreditAsset("EUR", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) err = s.processor.ProcessChange(s.ctx, ingest.Change{ @@ -1496,7 +1497,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateTrustlineAndRemoveContrac eurID, err := xdr.MustNewCreditAsset("EUR", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) err = s.processor.ProcessChange(s.ctx, ingest.Change{ @@ -2085,7 +2086,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractID() { eurID, err := xdr.MustNewCreditAsset("EUR", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) err = s.processor.ProcessChange(s.ctx, ingest.Change{ @@ -2138,7 +2139,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateTrustlineAndRemoveContrac eurID, err := xdr.MustNewCreditAsset("EUR", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) err = s.processor.ProcessChange(s.ctx, ingest.Change{ @@ -2238,7 +2239,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractIDFromZeroRow() { eurID, err := xdr.MustNewCreditAsset("EUR", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) err = s.processor.ProcessChange(s.ctx, ingest.Change{ @@ -2292,7 +2293,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractIDAndBalanceZeroR eurID, err := xdr.MustNewCreditAsset("EUR", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) err = s.processor.ProcessChange(s.ctx, ingest.Change{ @@ -2308,7 +2309,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractIDAndBalanceZeroR Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ LastModifiedLedgerSeq: lastModifiedLedgerSeq, - Data: BalanceToContractData(eurID, [32]byte{1}, 9), + Data: sac.BalanceToContractData(eurID, [32]byte{1}, 9), }, })) keyHash := getKeyHashForBalance(s.T(), eurID, [32]byte{1}) @@ -2330,7 +2331,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractIDAndBalanceZeroR Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ LastModifiedLedgerSeq: lastModifiedLedgerSeq, - Data: BalanceToContractData(eurID, [32]byte{2}, 1), + Data: sac.BalanceToContractData(eurID, [32]byte{2}, 1), }, })) otherKeyHash := getKeyHashForBalance(s.T(), eurID, [32]byte{2}) @@ -2406,7 +2407,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractIDAndRow() { eurID, err := xdr.MustNewCreditAsset("EUR", trustLineIssuer.Address()).ContractID("") s.Assert().NoError(err) - eurContractData, err := AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) + eurContractData, err := sac.AssetToContractData(false, "EUR", trustLineIssuer.Address(), eurID) s.Assert().NoError(err) err = s.processor.ProcessChange(s.ctx, ingest.Change{ diff --git a/services/horizon/internal/ingest/processors/contract_asset_stats.go b/services/horizon/internal/ingest/processors/contract_asset_stats.go index d39ceb6b6d..4faca551ef 100644 --- a/services/horizon/internal/ingest/processors/contract_asset_stats.go +++ b/services/horizon/internal/ingest/processors/contract_asset_stats.go @@ -7,6 +7,7 @@ import ( "math/big" "github.com/stellar/go/ingest" + "github.com/stellar/go/ingest/sac" "github.com/stellar/go/services/horizon/internal/db2/history" "github.com/stellar/go/support/errors" "github.com/stellar/go/xdr" @@ -107,7 +108,7 @@ func (s *ContractAssetStatSet) GetAssetToContractMap() map[xdr.Hash]*xdr.Asset { func (s *ContractAssetStatSet) ingestAssetContractMetadata(change ingest.Change) (bool, error) { if change.Pre != nil { - asset := AssetFromContractData(*change.Pre, s.networkPassphrase) + asset := sac.AssetFromContractData(*change.Pre, s.networkPassphrase) if asset == nil { return false, nil } @@ -123,13 +124,13 @@ func (s *ContractAssetStatSet) ingestAssetContractMetadata(change ingest.Change) // The contract id for any soroban contract should never change and // therefore we return a fatal ingestion error if we encounter // a stellar asset changing contract ids. - postAsset := AssetFromContractData(*change.Post, s.networkPassphrase) + postAsset := sac.AssetFromContractData(*change.Post, s.networkPassphrase) if postAsset == nil || !(*postAsset).Equals(*asset) { return false, ingest.NewStateError(fmt.Errorf("asset contract changed asset")) } return true, nil } else if change.Post != nil { - asset := AssetFromContractData(*change.Post, s.networkPassphrase) + asset := sac.AssetFromContractData(*change.Post, s.networkPassphrase) if asset == nil { return false, nil } @@ -161,7 +162,7 @@ func (s *ContractAssetStatSet) ingestContractAssetBalance(ctx context.Context, c return nil } - _, postAmt, postOk := ContractBalanceFromContractData(*change.Post, s.networkPassphrase) + _, postAmt, postOk := sac.ContractBalanceFromContractData(*change.Post, s.networkPassphrase) // we only ingest created ledger entries if we determine that they resemble the shape of // a Stellar Asset Contract balance ledger entry if !postOk { @@ -211,7 +212,7 @@ func (s *ContractAssetStatSet) ingestContractAssetBalance(ctx context.Context, c // entry from our db when the entry is removed from the ledger. s.removedBalances = append(s.removedBalances, keyHash) - _, preAmt, ok := ContractBalanceFromContractData(*change.Pre, s.networkPassphrase) + _, preAmt, ok := sac.ContractBalanceFromContractData(*change.Pre, s.networkPassphrase) if !ok { return nil } @@ -236,14 +237,14 @@ func (s *ContractAssetStatSet) ingestContractAssetBalance(ctx context.Context, c return nil } - holder, amt, ok := ContractBalanceFromContractData(*change.Pre, s.networkPassphrase) + holder, amt, ok := sac.ContractBalanceFromContractData(*change.Pre, s.networkPassphrase) if !ok { return nil } // if the updated ledger entry is not in the expected format then this // cannot be emitted by the stellar asset contract, so ignore it - postHolder, postAmt, postOk := ContractBalanceFromContractData(*change.Post, s.networkPassphrase) + postHolder, postAmt, postOk := sac.ContractBalanceFromContractData(*change.Post, s.networkPassphrase) if !postOk || postHolder != holder { return nil } diff --git a/services/horizon/internal/ingest/processors/contract_asset_stats_test.go b/services/horizon/internal/ingest/processors/contract_asset_stats_test.go index ce89920ecd..8e4335ddbe 100644 --- a/services/horizon/internal/ingest/processors/contract_asset_stats_test.go +++ b/services/horizon/internal/ingest/processors/contract_asset_stats_test.go @@ -10,13 +10,14 @@ import ( "github.com/stretchr/testify/mock" "github.com/stellar/go/ingest" + "github.com/stellar/go/ingest/sac" "github.com/stellar/go/keypair" "github.com/stellar/go/services/horizon/internal/db2/history" "github.com/stellar/go/xdr" ) func getKeyHashForBalance(t *testing.T, assetContractId, holderID [32]byte) xdr.Hash { - ledgerKey := ContractBalanceLedgerKey(assetContractId, holderID) + ledgerKey := sac.ContractBalanceLedgerKey(assetContractId, holderID) bin, err := ledgerKey.MarshalBinary() assert.NoError(t, err) return sha256.Sum256(bin) @@ -46,7 +47,7 @@ func TestAddContractData(t *testing.T) { 150, ) - xlmContractData, err := AssetToContractData(true, "", "", xlmID) + xlmContractData, err := sac.AssetToContractData(true, "", "", xlmID) assert.NoError(t, err) err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, @@ -62,7 +63,7 @@ func TestAddContractData(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(xlmID, [32]byte{}, 100), + Data: sac.BalanceToContractData(xlmID, [32]byte{}, 100), }, }) assert.NoError(t, err) @@ -72,12 +73,12 @@ func TestAddContractData(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(uniID, [32]byte{}, 0), + Data: sac.BalanceToContractData(uniID, [32]byte{}, 0), }, }) assert.NoError(t, err) - usdcContractData, err := AssetToContractData(false, "USDC", usdcIssuer, usdcID) + usdcContractData, err := sac.AssetToContractData(false, "USDC", usdcIssuer, usdcID) assert.NoError(t, err) err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, @@ -87,7 +88,7 @@ func TestAddContractData(t *testing.T) { }) assert.NoError(t, err) - etherContractData, err := AssetToContractData(false, "ETHER", etherIssuer, etherID) + etherContractData, err := sac.AssetToContractData(false, "ETHER", etherIssuer, etherID) assert.NoError(t, err) err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, @@ -102,7 +103,7 @@ func TestAddContractData(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(etherID, [32]byte{}, 50), + Data: sac.BalanceToContractData(etherID, [32]byte{}, 50), }, }) assert.NoError(t, err) @@ -112,7 +113,7 @@ func TestAddContractData(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(etherID, [32]byte{1}, 150), + Data: sac.BalanceToContractData(etherID, [32]byte{1}, 150), }, }) assert.NoError(t, err) @@ -121,7 +122,7 @@ func TestAddContractData(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Post: &xdr.LedgerEntry{ - Data: balanceToContractData(etherID, [32]byte{1}, xdr.Int128Parts{Hi: -1, Lo: 0}), + Data: sac.BalanceInt128ToContractData(etherID, [32]byte{1}, xdr.Int128Parts{Hi: -1, Lo: 0}), }, }) assert.NoError(t, err) @@ -133,7 +134,7 @@ func TestAddContractData(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(btcID, [32]byte{2}, 300), + Data: sac.BalanceToContractData(btcID, [32]byte{2}, 300), }, }) assert.NoError(t, err) @@ -218,10 +219,10 @@ func TestUpdateContractBalance(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(usdcID, [32]byte{}, 50), + Data: sac.BalanceToContractData(usdcID, [32]byte{}, 50), }, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(usdcID, [32]byte{}, 100), + Data: sac.BalanceToContractData(usdcID, [32]byte{}, 100), }, }) assert.NoError(t, err) @@ -241,10 +242,10 @@ func TestUpdateContractBalance(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(usdcID, [32]byte{2}, 30), + Data: sac.BalanceToContractData(usdcID, [32]byte{2}, 30), }, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(usdcID, [32]byte{2}, 100), + Data: sac.BalanceToContractData(usdcID, [32]byte{2}, 100), }, }) assert.NoError(t, err) @@ -256,10 +257,10 @@ func TestUpdateContractBalance(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(usdcID, [32]byte{4}, 0), + Data: sac.BalanceToContractData(usdcID, [32]byte{4}, 0), }, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(usdcID, [32]byte{4}, 100), + Data: sac.BalanceToContractData(usdcID, [32]byte{4}, 100), }, }) assert.NoError(t, err) @@ -278,10 +279,10 @@ func TestUpdateContractBalance(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(etherID, [32]byte{}, 200), + Data: sac.BalanceToContractData(etherID, [32]byte{}, 200), }, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(etherID, [32]byte{}, 50), + Data: sac.BalanceToContractData(etherID, [32]byte{}, 50), }, }) assert.NoError(t, err) @@ -290,10 +291,10 @@ func TestUpdateContractBalance(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(etherID, [32]byte{}, 200), + Data: sac.BalanceToContractData(etherID, [32]byte{}, 200), }, Post: &xdr.LedgerEntry{ - Data: balanceToContractData(etherID, [32]byte{1}, xdr.Int128Parts{Hi: -1, Lo: 0}), + Data: sac.BalanceInt128ToContractData(etherID, [32]byte{1}, xdr.Int128Parts{Hi: -1, Lo: 0}), }, }) assert.NoError(t, err) @@ -302,10 +303,10 @@ func TestUpdateContractBalance(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: balanceToContractData(etherID, [32]byte{1}, xdr.Int128Parts{Hi: -1, Lo: 0}), + Data: sac.BalanceInt128ToContractData(etherID, [32]byte{1}, xdr.Int128Parts{Hi: -1, Lo: 0}), }, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(etherID, [32]byte{}, 200), + Data: sac.BalanceToContractData(etherID, [32]byte{}, 200), }, }) assert.NoError(t, err) @@ -314,10 +315,10 @@ func TestUpdateContractBalance(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(btcID, [32]byte{2}, 300), + Data: sac.BalanceToContractData(btcID, [32]byte{2}, 300), }, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(btcID, [32]byte{2}, 300), + Data: sac.BalanceToContractData(btcID, [32]byte{2}, 300), }, }) assert.NoError(t, err) @@ -335,10 +336,10 @@ func TestUpdateContractBalance(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(btcID, [32]byte{5}, 10), + Data: sac.BalanceToContractData(btcID, [32]byte{5}, 10), }, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(btcID, [32]byte{5}, 15), + Data: sac.BalanceToContractData(btcID, [32]byte{5}, 15), }, }) assert.ErrorContains(t, err, "contract balance has invalid expiration ledger keyhash") @@ -348,10 +349,10 @@ func TestUpdateContractBalance(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(btcID, [32]byte{6}, 120), + Data: sac.BalanceToContractData(btcID, [32]byte{6}, 120), }, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(btcID, [32]byte{6}, 135), + Data: sac.BalanceToContractData(btcID, [32]byte{6}, 135), }, }) assert.ErrorContains(t, err, "contract balance has invalid expiration ledger keyhash") @@ -362,10 +363,10 @@ func TestUpdateContractBalance(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(uniID, [32]byte{4}, 50), + Data: sac.BalanceToContractData(uniID, [32]byte{4}, 50), }, Post: &xdr.LedgerEntry{ - Data: BalanceToContractData(uniID, [32]byte{4}, 75), + Data: sac.BalanceToContractData(uniID, [32]byte{4}, 75), }, }) assert.NoError(t, err) @@ -427,7 +428,7 @@ func TestRemoveContractData(t *testing.T) { 150, ) - usdcContractData, err := AssetToContractData(false, "USDC", usdcIssuer, usdcID) + usdcContractData, err := sac.AssetToContractData(false, "USDC", usdcIssuer, usdcID) assert.NoError(t, err) err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, @@ -442,7 +443,7 @@ func TestRemoveContractData(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(usdcID, [32]byte{}, 50), + Data: sac.BalanceToContractData(usdcID, [32]byte{}, 50), }, }) assert.NoError(t, err) @@ -452,7 +453,7 @@ func TestRemoveContractData(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(usdcID, [32]byte{1}, 20), + Data: sac.BalanceToContractData(usdcID, [32]byte{1}, 20), }, }) assert.NoError(t, err) @@ -461,7 +462,7 @@ func TestRemoveContractData(t *testing.T) { err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Pre: &xdr.LedgerEntry{ - Data: BalanceToContractData(usdcID, [32]byte{2}, 34), + Data: sac.BalanceToContractData(usdcID, [32]byte{2}, 34), }, }) assert.NoError(t, err) diff --git a/services/horizon/internal/ingest/verify_test.go b/services/horizon/internal/ingest/verify_test.go index b86eaf4db1..c121d99728 100644 --- a/services/horizon/internal/ingest/verify_test.go +++ b/services/horizon/internal/ingest/verify_test.go @@ -14,9 +14,9 @@ import ( "github.com/stellar/go/gxdr" "github.com/stellar/go/ingest" + "github.com/stellar/go/ingest/sac" "github.com/stellar/go/randxdr" "github.com/stellar/go/services/horizon/internal/db2/history" - "github.com/stellar/go/services/horizon/internal/ingest/processors" "github.com/stellar/go/services/horizon/internal/test" "github.com/stellar/go/support/db" "github.com/stellar/go/xdr" @@ -241,7 +241,7 @@ func assetContractMetadataFromTrustline(tt *test.T, trustline xdr.LedgerEntryCha tt.Assert.NoError( trustline.Created.Data.MustTrustLine().Asset.Extract(&assetType, &code, &issuer), ) - ledgerData, err := processors.AssetToContractData(assetType == xdr.AssetTypeAssetTypeNative, code, issuer, contractID) + ledgerData, err := sac.AssetToContractData(assetType == xdr.AssetTypeAssetTypeNative, code, issuer, contractID) tt.Assert.NoError(err) assetContractMetadata := xdr.LedgerEntryChange{ Type: xdr.LedgerEntryChangeTypeLedgerEntryCreated, @@ -266,7 +266,7 @@ func balanceContractDataFromTrustline(tt *test.T, trustline xdr.LedgerEntryChang Type: xdr.LedgerEntryChangeTypeLedgerEntryCreated, Created: &xdr.LedgerEntry{ LastModifiedLedgerSeq: trustline.Created.LastModifiedLedgerSeq, - Data: processors.BalanceToContractData(contractID, *trustlineData.AccountId.Ed25519, uint64(trustlineData.Balance)), + Data: sac.BalanceToContractData(contractID, *trustlineData.AccountId.Ed25519, uint64(trustlineData.Balance)), }, } return assetContractMetadata diff --git a/services/horizon/internal/integration/claimable_balance_ops_test.go b/services/horizon/internal/integration/claimable_balance_ops_test.go index 867df6072c..58546f496d 100644 --- a/services/horizon/internal/integration/claimable_balance_ops_test.go +++ b/services/horizon/internal/integration/claimable_balance_ops_test.go @@ -42,10 +42,11 @@ func TestClaimableBalanceCreationOperationsAndEffects(t *testing.T) { expectedBalanceID, err := xdr.MarshalHex(claimCreationOp.BalanceId) tt.NoError(err) - response, err := itest.Client().Operations(sdk.OperationRequest{}) + response, err := itest.Client().Operations(sdk.OperationRequest{ + Order: "desc", + }) ops := response.Embedded.Records tt.NoError(err) - tt.Len(ops, 1) cb := ops[0].(operations.CreateClaimableBalance) tt.Equal("native", cb.Asset) tt.Equal("10.0000000", cb.Amount) diff --git a/services/horizon/internal/integration/extend_footprint_ttl_test.go b/services/horizon/internal/integration/extend_footprint_ttl_test.go index 5ccbfa97e8..8c9278c767 100644 --- a/services/horizon/internal/integration/extend_footprint_ttl_test.go +++ b/services/horizon/internal/integration/extend_footprint_ttl_test.go @@ -8,7 +8,6 @@ import ( "github.com/stellar/go/clients/horizonclient" "github.com/stellar/go/protocols/horizon/operations" "github.com/stellar/go/services/horizon/internal/test/integration" - "github.com/stellar/go/txnbuild" ) func TestExtendFootprintTtl(t *testing.T) { @@ -28,18 +27,18 @@ func TestExtendFootprintTtl(t *testing.T) { require.NoError(t, err) installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), add_u64_contract) - preFlightOp, minFee := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) - tx := itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) + tx := itest.MustSubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) _, err = itest.Client().TransactionDetail(tx.Hash) require.NoError(t, err) - sourceAccount, bumpFootPrint, minFee := itest.PreflightExtendExpiration( + sourceAccount, bumpFootPrint := itest.PreflightExtendExpiration( itest.Master().Address(), preFlightOp.Ext.SorobanData.Resources.Footprint.ReadWrite, 10000, ) - tx = itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &bumpFootPrint) + tx = itest.MustSubmitOperations(&sourceAccount, itest.Master(), &bumpFootPrint) ops, err := itest.Client().Operations(horizonclient.OperationRequest{ForTransaction: tx.Hash}) require.NoError(t, err) diff --git a/services/horizon/internal/integration/generate_ledgers_test.go b/services/horizon/internal/integration/generate_ledgers_test.go index 0da07c1a08..591ad56df1 100644 --- a/services/horizon/internal/integration/generate_ledgers_test.go +++ b/services/horizon/internal/integration/generate_ledgers_test.go @@ -22,7 +22,6 @@ import ( "github.com/stellar/go/ingest" "github.com/stellar/go/ingest/loadtest" "github.com/stellar/go/keypair" - "github.com/stellar/go/protocols/horizon" proto "github.com/stellar/go/protocols/stellarcore" "github.com/stellar/go/services/horizon/internal/test/integration" "github.com/stellar/go/txnbuild" @@ -33,7 +32,6 @@ const loadTestNetworkPassphrase = "load test network" type sorobanTransaction struct { op *txnbuild.InvokeHostFunction - fee int64 signer *keypair.Full sequenceNumber int64 } @@ -62,40 +60,6 @@ func TestGenerateLedgers(t *testing.T) { // transactionsPerLedger should be a multiple of maxAccountsPerTransaction require.Zero(t, transactionsPerLedger%maxAccountsPerTransaction) - txErr := itest.CoreClient().UpgradeTxSetSize(context.Background(), uint32(transactionsPerLedger*100), time.Unix(0, 0)) - require.NoError(t, txErr) - - txErr = itest.CoreClient().UpgradeSorobanTxSetSize(context.Background(), uint32(transactionsPerLedger*100), time.Unix(0, 0)) - require.NoError(t, txErr) - - contents, err := os.ReadFile(filepath.Join("testdata", "unlimited-config.xdr")) - require.NoError(t, err) - var configSet xdr.ConfigUpgradeSet - err = xdr.SafeUnmarshalBase64(string(contents), &configSet) - require.NoError(t, err) - - upgradeTransactions, upgradeKey, err := stellarcore.GenSorobanConfigUpgradeTxAndKey(stellarcore.GenSorobanConfig{ - BaseSeqNum: 0, - NetworkPassphrase: itest.Config().NetworkPassphrase, - SigningKey: itest.Master(), - StellarCorePath: itest.CoreBinaryPath(), - }, configSet) - require.NoError(t, err) - - for _, transaction := range upgradeTransactions { - var b64 string - b64, err = xdr.MarshalBase64(transaction) - require.NoError(t, err) - var response horizon.Transaction - response, err = itest.Client().SubmitTransactionXDR(b64) - require.NoError(t, err) - require.True(t, response.Successful) - } - - require.NoError(t, - itest.CoreClient().UpgradeSorobanConfig(context.Background(), upgradeKey, time.Unix(0, 0)), - ) - xlm := xdr.MustNewNativeAsset() createSAC(itest, xlm) @@ -111,8 +75,7 @@ func TestGenerateLedgers(t *testing.T) { var accountLedgers []uint32 for i := 0; i < 2*transactionsPerLedger; i += maxAccountsPerTransaction { keys, curAccounts := itest.CreateAccounts(maxAccountsPerTransaction, "10000000") - var account horizon.Account - account, err = itest.Client().AccountDetail(horizonclient.AccountRequest{AccountID: curAccounts[0].GetAccountID()}) + account, err := itest.Client().AccountDetail(horizonclient.AccountRequest{AccountID: curAccounts[0].GetAccountID()}) require.NoError(t, err) accountLedgers = append(accountLedgers, account.LastModifiedLedger) @@ -141,25 +104,22 @@ func TestGenerateLedgers(t *testing.T) { } else if i%2 == 1 { for j := 0; j < transfersPerTx; j++ { var contractID xdr.Hash - _, err = rand.Read(contractID[:]) + _, err := rand.Read(contractID[:]) require.NoError(t, err) bulkRecipients = append(bulkRecipients, contractAddressParam(contractID)) } } op = bulkTransfer(itest, bulkContractID, sender, xlm, &bulkRecipients, &bulkAmounts) - preFlightOp, minFee := itest.PreflightHostFunctions(accounts[i], *op) + preFlightOp := itest.PreflightHostFunctions(accounts[i], *op) preFlightOp.Ext.SorobanData.Resources.ReadBytes *= 10 preFlightOp.Ext.SorobanData.Resources.WriteBytes *= 10 preFlightOp.Ext.SorobanData.Resources.Instructions *= 10 preFlightOp.Ext.SorobanData.ResourceFee *= 10 - minFee *= 10 - var sequenceNumber int64 - sequenceNumber, err = accounts[i].GetSequenceNumber() + sequenceNumber, err := accounts[i].GetSequenceNumber() require.NoError(t, err) transactions = append(transactions, sorobanTransaction{ op: &preFlightOp, - fee: minFee + txnbuild.MinBaseFee, signer: signers[i], sequenceNumber: sequenceNumber, }) @@ -240,8 +200,7 @@ func TestGenerateLedgers(t *testing.T) { if change.Type != xdr.LedgerEntryTypeAccount { continue } - var ledgerKey xdr.LedgerKey - ledgerKey, err = change.LedgerKey() + ledgerKey, err := change.LedgerKey() require.NoError(t, err) require.True(t, accountSet[ledgerKey.MustAccount().AccountId.Address()]) } @@ -417,7 +376,7 @@ func txSubWorker( pending := map[string]bool{} for _, tx := range subset { account := txnbuild.NewSimpleAccount(tx.signer.Address(), tx.sequenceNumber+sequenceOffset) - tx, err := itest.CreateSignedTransactionFromOpsWithFee(&account, []*keypair.Full{tx.signer}, tx.fee, tx.op) + tx, err := itest.CreateSignedTransactionFromOps(&account, []*keypair.Full{tx.signer}, tx.op) require.NoError(itest.CurrentTest(), err) hash, err := tx.HashHex(itest.Config().NetworkPassphrase) diff --git a/services/horizon/internal/integration/invokehostfunction_test.go b/services/horizon/internal/integration/invokehostfunction_test.go index 28e81d7de4..c7ab7565d8 100644 --- a/services/horizon/internal/integration/invokehostfunction_test.go +++ b/services/horizon/internal/integration/invokehostfunction_test.go @@ -10,6 +10,7 @@ import ( "github.com/stellar/go/amount" "github.com/stellar/go/clients/horizonclient" + "github.com/stellar/go/keypair" "github.com/stellar/go/protocols/horizon/operations" "github.com/stellar/go/services/horizon/internal/test/integration" "github.com/stellar/go/strkey" @@ -44,8 +45,8 @@ func TestContractInvokeHostFunctionInstallContract(t *testing.T) { require.NoError(t, err) installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), add_u64_contract) - preFlightOp, minFee := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) - tx := itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) + tx := itest.MustSubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) clientTx, err := itest.Client().TransactionDetail(tx.Hash) require.NoError(t, err) @@ -77,6 +78,58 @@ func TestContractInvokeHostFunctionInstallContract(t *testing.T) { } +func TestSorobanFeeBumpTransaction(t *testing.T) { + if integration.GetCoreMaxSupportedProtocol() < 20 { + t.Skip("This test run does not support less than Protocol 20") + } + + itest := integration.NewTest(t, integration.Config{ + EnableStellarRPC: true, + }) + + feeKp, feeAccount := itest.CreateAccount("10000") + + // establish which account will be contract owner, and load it's current seq + sourceAccount, err := itest.Client().AccountDetail(horizonclient.AccountRequest{ + AccountID: itest.Master().Address(), + }) + require.NoError(t, err) + + installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), add_u64_contract) + preFlightOp := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) + inner, err := itest.CreateSignedTransactionFromOps(&sourceAccount, []*keypair.Full{itest.Master()}, &preFlightOp) + require.NoError(t, err) + feeBump, err := txnbuild.NewFeeBumpTransaction(txnbuild.FeeBumpTransactionParams{ + Inner: inner, + FeeAccount: feeAccount.GetAccountID(), + BaseFee: txnbuild.MinBaseFee, + }) + require.NoError(t, err) + feeBump, err = feeBump.Sign(itest.GetPassPhrase(), feeKp) + require.NoError(t, err) + + tx, err := itest.Client().SubmitFeeBumpTransaction(feeBump) + require.NoError(t, err) + require.True(t, tx.Successful) + + clientTx, err := itest.Client().TransactionDetail(tx.Hash) + require.NoError(t, err) + + assert.Equal(t, tx.Hash, clientTx.Hash) + var txResult xdr.TransactionResult + err = xdr.SafeUnmarshalBase64(clientTx.ResultXdr, &txResult) + require.NoError(t, err) + + clientInvokeOp, err := itest.Client().Operations(horizonclient.OperationRequest{ + ForTransaction: tx.Hash, + }) + require.NoError(t, err) + + invokeHostFunctionOpJson, ok := clientInvokeOp.Embedded.Records[0].(operations.InvokeHostFunction) + assert.True(t, ok) + assert.Equal(t, invokeHostFunctionOpJson.Function, "HostFunctionTypeHostFunctionTypeUploadContractWasm") +} + func TestContractInvokeHostFunctionCreateContractByAddress(t *testing.T) { if integration.GetCoreMaxSupportedProtocol() < 20 { t.Skip("This test run does not support less than Protocol 20") @@ -94,13 +147,13 @@ func TestContractInvokeHostFunctionCreateContractByAddress(t *testing.T) { // Install the contract installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), add_u64_contract) - preFlightOp, minFee := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) - itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) + itest.MustSubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) // Create the contract createContractOp := assembleCreateContractOp(t, itest.Master().Address(), add_u64_contract, "a1") - preFlightOp, minFee = itest.PreflightHostFunctions(&sourceAccount, *createContractOp) - tx, err := itest.SubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp = itest.PreflightHostFunctions(&sourceAccount, *createContractOp) + tx, err := itest.SubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) require.NoError(t, err) clientTx, err := itest.Client().TransactionDetail(tx.Hash) @@ -147,8 +200,8 @@ func TestContractInvokeHostFunctionCreateConstructorContract(t *testing.T) { // Install the contract installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), constructor_contract) - preFlightOp, minFee := itest.PreflightHostFunctions(itest.MasterAccount(), *installContractOp) - itest.MustSubmitOperationsWithFee(itest.MasterAccount(), itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp := itest.PreflightHostFunctions(itest.MasterAccount(), *installContractOp) + itest.MustSubmitOperations(itest.MasterAccount(), itest.Master(), &preFlightOp) // Create the contract senderAddressArg := accountAddressParam(itest.Master().Address()) @@ -169,8 +222,8 @@ func TestContractInvokeHostFunctionCreateConstructorContract(t *testing.T) { amount, }, ) - preFlightOp, minFee = itest.PreflightHostFunctions(itest.MasterAccount(), *createContractOp) - tx, err := itest.SubmitOperationsWithFee(itest.MasterAccount(), itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp = itest.PreflightHostFunctions(itest.MasterAccount(), *createContractOp) + tx, err := itest.SubmitOperations(itest.MasterAccount(), itest.Master(), &preFlightOp) require.NoError(t, err) contractID := preFlightOp.Ext.SorobanData.Resources.Footprint.ReadWrite[0].MustContractData().Contract.ContractId @@ -241,13 +294,13 @@ func TestContractInvokeHostFunctionInvokeStatelessContractFn(t *testing.T) { // Install the contract installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), add_u64_contract) - preFlightOp, minFee := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) - itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) + itest.MustSubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) // Create the contract createContractOp := assembleCreateContractOp(t, itest.Master().Address(), add_u64_contract, "a1") - preFlightOp, minFee = itest.PreflightHostFunctions(&sourceAccount, *createContractOp) - tx, err := itest.SubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp = itest.PreflightHostFunctions(&sourceAccount, *createContractOp) + tx, err := itest.SubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) require.NoError(t, err) // contract has been deployed, now invoke a simple 'add' fn on the contract @@ -285,8 +338,8 @@ func TestContractInvokeHostFunctionInvokeStatelessContractFn(t *testing.T) { SourceAccount: sourceAccount.AccountID, } - preFlightOp, minFee = itest.PreflightHostFunctions(&sourceAccount, *invokeHostFunctionOp) - tx, err = itest.SubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp = itest.PreflightHostFunctions(&sourceAccount, *invokeHostFunctionOp) + tx, err = itest.SubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) require.NoError(t, err) clientTx, err := itest.Client().TransactionDetail(tx.Hash) @@ -350,14 +403,14 @@ func TestContractInvokeHostFunctionInvokeStatefulContractFn(t *testing.T) { // Install the contract installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), increment_contract) - preFlightOp, minFee := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) - itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) + itest.MustSubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) // Create the contract createContractOp := assembleCreateContractOp(t, itest.Master().Address(), increment_contract, "a1") - preFlightOp, minFee = itest.PreflightHostFunctions(&sourceAccount, *createContractOp) - tx, err := itest.SubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp = itest.PreflightHostFunctions(&sourceAccount, *createContractOp) + tx, err := itest.SubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) require.NoError(t, err) // contract has been deployed, now invoke a simple 'add' fn on the contract @@ -380,8 +433,8 @@ func TestContractInvokeHostFunctionInvokeStatefulContractFn(t *testing.T) { SourceAccount: sourceAccount.AccountID, } - preFlightOp, minFee = itest.PreflightHostFunctions(&sourceAccount, *invokeHostFunctionOp) - tx, err = itest.SubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp = itest.PreflightHostFunctions(&sourceAccount, *invokeHostFunctionOp) + tx, err = itest.SubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) require.NoError(t, err) clientTx, err := itest.Client().TransactionDetail(tx.Hash) diff --git a/services/horizon/internal/integration/sac_test.go b/services/horizon/internal/integration/sac_test.go index 19f27d47ac..8b95760410 100644 --- a/services/horizon/internal/integration/sac_test.go +++ b/services/horizon/internal/integration/sac_test.go @@ -12,11 +12,11 @@ import ( "github.com/stellar/go/amount" "github.com/stellar/go/clients/horizonclient" + "github.com/stellar/go/ingest/sac" "github.com/stellar/go/keypair" "github.com/stellar/go/protocols/horizon/effects" "github.com/stellar/go/protocols/horizon/operations" "github.com/stellar/go/services/horizon/internal/db2/history" - "github.com/stellar/go/services/horizon/internal/ingest/processors" "github.com/stellar/go/services/horizon/internal/test/integration" "github.com/stellar/go/strkey" "github.com/stellar/go/txnbuild" @@ -132,12 +132,12 @@ func createSAC(itest *integration.Test, asset xdr.Asset) { SourceAccount: itest.Master().Address(), } _, _, preFlightOp := assertInvokeHostFnSucceeds(itest, itest.Master(), invokeHostFunction) - sourceAccount, extendTTLOp, minFee := itest.PreflightExtendExpiration( + sourceAccount, extendTTLOp := itest.PreflightExtendExpiration( itest.Master().Address(), preFlightOp.Ext.SorobanData.Resources.Footprint.ReadWrite, LongTermTTL, ) - itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &extendTTLOp) + itest.MustSubmitOperations(&sourceAccount, itest.Master(), &extendTTLOp) } func TestContractMintToContract(t *testing.T) { @@ -147,7 +147,6 @@ func TestContractMintToContract(t *testing.T) { itest := integration.NewTest(t, integration.Config{ EnableStellarRPC: true, - QuickExpiration: true, }) issuer := itest.Master().Address() @@ -161,17 +160,40 @@ func TestContractMintToContract(t *testing.T) { strkeyRecipientContractID, err := strkey.Encode(strkey.VersionByteContract, recipientContractID[:]) assert.NoError(t, err) - mintAmount := xdr.Int128Parts{Lo: math.MaxUint64 - 3, Hi: math.MaxInt64} - _, mintTx, _ := assertInvokeHostFnSucceeds( - itest, - itest.Master(), - mintWithAmt( - itest, - issuer, asset, - i128Param(int64(mintAmount.Hi), uint64(mintAmount.Lo)), - contractAddressParam(recipientContractID)), - ) - assertContainsEffect(t, getTxEffects(itest, mintTx, asset), + // calling transfer from the issuer account will also mint the asset + invokeHostOp, err := txnbuild.NewPaymentToContract(txnbuild.PaymentToContractParams{ + NetworkPassphrase: itest.GetPassPhrase(), + Destination: strkey.MustEncode(strkey.VersionByteContract, recipientContractID[:]), + Amount: amount.String(3), + Asset: txnbuild.CreditAsset{ + Code: code, + Issuer: issuer, + }, + SourceAccount: issuer, + }) + assert.NoError(t, err) + transferTx := itest.MustSubmitOperations(itest.MasterAccount(), itest.Master(), &invokeHostOp) + + assertContainsEffect(t, getTxEffects(itest, transferTx.Hash, asset), + effects.EffectAccountDebited, + effects.EffectContractCredited) + + // call transfer again to exercise code path when the contract balance already exists + invokeHostOp, err = txnbuild.NewPaymentToContract(txnbuild.PaymentToContractParams{ + NetworkPassphrase: itest.GetPassPhrase(), + Destination: strkey.MustEncode(strkey.VersionByteContract, recipientContractID[:]), + Amount: amount.String(3), + Asset: txnbuild.CreditAsset{ + Code: code, + Issuer: issuer, + }, + SourceAccount: issuer, + }) + assert.NoError(t, err) + transferTx = itest.MustSubmitOperations(itest.MasterAccount(), itest.Master(), &invokeHostOp) + + assertContainsEffect(t, getTxEffects(itest, transferTx.Hash, asset), + effects.EffectAccountDebited, effects.EffectContractCredited) balanceAmount, _, _ := assertInvokeHostFnSucceeds( @@ -180,19 +202,20 @@ func TestContractMintToContract(t *testing.T) { contractBalance(itest, issuer, asset, recipientContractID), ) assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, balanceAmount.Type) - assert.Equal(itest.CurrentTest(), xdr.Uint64(math.MaxUint64-3), (*balanceAmount.I128).Lo) - assert.Equal(itest.CurrentTest(), xdr.Int64(math.MaxInt64), (*balanceAmount.I128).Hi) - assertEventPayments(itest, mintTx, asset, "", strkeyRecipientContractID, "mint", amount.String128(mintAmount)) + assert.Equal(itest.CurrentTest(), xdr.Uint64(6), (*balanceAmount.I128).Lo) + assert.Equal(itest.CurrentTest(), xdr.Int64(0), (*balanceAmount.I128).Hi) - // calling transfer from the issuer account will also mint the asset - _, transferTx, _ := assertInvokeHostFnSucceeds( + mintAmount := xdr.Int128Parts{Lo: math.MaxUint64 - 6, Hi: math.MaxInt64} + _, mintTx, _ := assertInvokeHostFnSucceeds( itest, itest.Master(), - transferWithAmount(itest, issuer, asset, i128Param(0, 3), contractAddressParam(recipientContractID)), + mintWithAmt( + itest, + issuer, asset, + i128Param(int64(mintAmount.Hi), uint64(mintAmount.Lo)), + contractAddressParam(recipientContractID)), ) - - assertContainsEffect(t, getTxEffects(itest, transferTx, asset), - effects.EffectAccountDebited, + assertContainsEffect(t, getTxEffects(itest, mintTx, asset), effects.EffectContractCredited) balanceAmount, _, _ = assertInvokeHostFnSucceeds( @@ -203,6 +226,7 @@ func TestContractMintToContract(t *testing.T) { assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, balanceAmount.Type) assert.Equal(itest.CurrentTest(), xdr.Uint64(math.MaxUint64), (*balanceAmount.I128).Lo) assert.Equal(itest.CurrentTest(), xdr.Int64(math.MaxInt64), (*balanceAmount.I128).Hi) + assertEventPayments(itest, mintTx, asset, "", strkeyRecipientContractID, "mint", amount.String128(mintAmount)) // 2^127 - 1 balanceContracts := new(big.Int).Lsh(big.NewInt(1), 127) @@ -280,19 +304,19 @@ func TestExpirationAndRestoration(t *testing.T) { invokeStoreSet( itest, storeContractID, - processors.BalanceToContractData( + sac.BalanceToContractData( storeContractID, [32]byte{1}, 23, ), ), ) - sourceAccount, extendTTLOp, minFee := itest.PreflightExtendExpiration( + sourceAccount, extendTTLOp := itest.PreflightExtendExpiration( itest.Master().Address(), setOp.Ext.SorobanData.Resources.Footprint.ReadWrite, LongTermTTL, ) - itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &extendTTLOp) + itest.MustSubmitOperations(&sourceAccount, itest.Master(), &extendTTLOp) assertAssetStats(itest, assetStats{ code: code, issuer: issuer, @@ -306,9 +330,10 @@ func TestExpirationAndRestoration(t *testing.T) { }) // create balance which we will expire - balanceToExpire := processors.BalanceToContractData( + holder := [32]byte{2} + balanceToExpire := sac.BalanceToContractData( storeContractID, - [32]byte{2}, + holder, 37, ) assertInvokeHostFnSucceeds( @@ -364,7 +389,7 @@ func TestExpirationAndRestoration(t *testing.T) { invokeStoreSet( itest, storeContractID, - processors.BalanceToContractData( + sac.BalanceToContractData( storeContractID, [32]byte{1}, 50, @@ -384,11 +409,20 @@ func TestExpirationAndRestoration(t *testing.T) { }) // restore expired balance - sourceAccount, restoreFootprint, minFee := itest.RestoreFootprint( - itest.Master().Address(), - balanceToExpireLedgerKey, - ) - itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &restoreFootprint) + restoreFootprint, err := txnbuild.NewAssetBalanceRestoration(txnbuild.AssetBalanceRestorationParams{ + NetworkPassphrase: itest.GetPassPhrase(), + Contract: strkey.MustEncode(strkey.VersionByteContract, holder[:]), + Asset: txnbuild.CreditAsset{ + Code: code, + Issuer: issuer, + }, + SourceAccount: itest.Master().Address(), + }) + assert.NoError(t, err) + // set the contract id to storeContractID because we are restoring a fake asset balance + restoreFootprint.Ext.SorobanData.Resources.Footprint.ReadWrite[0].ContractData.Contract.ContractId = &storeContractID + itest.MustSubmitOperations(itest.MasterAccount(), itest.Master(), &restoreFootprint) + assertAssetStats(itest, assetStats{ code: code, issuer: issuer, @@ -411,7 +445,7 @@ func TestExpirationAndRestoration(t *testing.T) { invokeStoreSet( itest, storeContractID, - processors.BalanceToContractData( + sac.BalanceToContractData( storeContractID, [32]byte{1}, 3, @@ -437,7 +471,7 @@ func TestExpirationAndRestoration(t *testing.T) { invokeStoreRemove( itest, storeContractID, - processors.ContractBalanceLedgerKey( + sac.ContractBalanceLedgerKey( storeContractID, [32]byte{1}, ), @@ -506,7 +540,6 @@ func TestContractTransferBetweenAccounts(t *testing.T) { itest := integration.NewTest(t, integration.Config{ EnableStellarRPC: true, - QuickExpiration: true, }) issuer := itest.Master().Address() @@ -582,7 +615,6 @@ func TestContractTransferBetweenAccountAndContract(t *testing.T) { itest := integration.NewTest(t, integration.Config{ EnableStellarRPC: true, - QuickExpiration: true, }) issuer := itest.Master().Address() @@ -620,16 +652,6 @@ func TestContractTransferBetweenAccountAndContract(t *testing.T) { initAssetContract(itest, issuer, asset, recipientContractID, recipientContractHash), ) - // Add funds to recipient contract - _, mintTx, _ := assertInvokeHostFnSucceeds( - itest, - itest.Master(), - mint(itest, issuer, asset, "1000", contractAddressParam(recipientContractID)), - ) - assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("1000")) - assertContainsEffect(t, getTxEffects(itest, mintTx, asset), - effects.EffectContractCredited) - assertAssetStats(itest, assetStats{ code: code, issuer: issuer, @@ -637,20 +659,28 @@ func TestContractTransferBetweenAccountAndContract(t *testing.T) { balanceAccounts: amount.MustParse("1000"), balanceArchivedContracts: big.NewInt(0), numArchivedContracts: 0, - numContracts: 1, - balanceContracts: big.NewInt(int64(amount.MustParse("1000"))), + numContracts: 0, + balanceContracts: big.NewInt(0), contractID: stellarAssetContractID(itest, asset), }) // transfer from account to contract - _, transferTx, _ := assertInvokeHostFnSucceeds( - itest, - recipientKp, - transfer(itest, recipientKp.Address(), asset, "30", contractAddressParam(recipientContractID)), - ) + invokeHostOp, err := txnbuild.NewPaymentToContract(txnbuild.PaymentToContractParams{ + NetworkPassphrase: itest.GetPassPhrase(), + Destination: strkey.MustEncode(strkey.VersionByteContract, recipientContractID[:]), + Amount: "30", + Asset: txnbuild.CreditAsset{ + Code: code, + Issuer: issuer, + }, + SourceAccount: recipientKp.Address(), + }) + assert.NoError(t, err) + transferTx := itest.MustSubmitOperations(recipient, recipientKp, &invokeHostOp) + assertAccountInvokeHostFunctionOperation(itest, recipientKp.Address(), recipientKp.Address(), strkeyRecipientContractID, "30.0000000") assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("970")) - assertContainsEffect(t, getTxEffects(itest, transferTx, asset), + assertContainsEffect(t, getTxEffects(itest, transferTx.Hash, asset), effects.EffectAccountDebited, effects.EffectContractCredited) assertAssetStats(itest, assetStats{ code: code, @@ -660,33 +690,64 @@ func TestContractTransferBetweenAccountAndContract(t *testing.T) { balanceArchivedContracts: big.NewInt(0), numArchivedContracts: 0, numContracts: 1, - balanceContracts: big.NewInt(int64(amount.MustParse("1030"))), + balanceContracts: big.NewInt(int64(amount.MustParse("30"))), contractID: stellarAssetContractID(itest, asset), }) - assertEventPayments(itest, transferTx, asset, recipientKp.Address(), strkeyRecipientContractID, "transfer", "30.0000000") + assertEventPayments(itest, transferTx.Hash, asset, recipientKp.Address(), strkeyRecipientContractID, "transfer", "30.0000000") + + // transfer from account to contract again to exercise code path where contract balance already exists + invokeHostOp, err = txnbuild.NewPaymentToContract(txnbuild.PaymentToContractParams{ + NetworkPassphrase: itest.GetPassPhrase(), + Destination: strkey.MustEncode(strkey.VersionByteContract, recipientContractID[:]), + Amount: "70", + Asset: txnbuild.CreditAsset{ + Code: code, + Issuer: issuer, + }, + SourceAccount: recipientKp.Address(), + }) + assert.NoError(t, err) + transferTx = itest.MustSubmitOperations(recipient, recipientKp, &invokeHostOp) + + assertAccountInvokeHostFunctionOperation(itest, recipientKp.Address(), recipientKp.Address(), strkeyRecipientContractID, "70.0000000") + assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("900")) + assertContainsEffect(t, getTxEffects(itest, transferTx.Hash, asset), + effects.EffectAccountDebited, effects.EffectContractCredited) + assertAssetStats(itest, assetStats{ + code: code, + issuer: issuer, + numAccounts: 1, + balanceAccounts: amount.MustParse("900"), + balanceArchivedContracts: big.NewInt(0), + numArchivedContracts: 0, + numContracts: 1, + balanceContracts: big.NewInt(int64(amount.MustParse("100"))), + contractID: stellarAssetContractID(itest, asset), + }) + assertEventPayments(itest, transferTx.Hash, asset, recipientKp.Address(), strkeyRecipientContractID, "transfer", "70.0000000") // transfer from contract to account - _, transferTx, _ = assertInvokeHostFnSucceeds( + _, transferTxHash, _ := assertInvokeHostFnSucceeds( itest, recipientKp, - transferFromContract(itest, recipientKp.Address(), asset, recipientContractID, recipientContractHash, "500", accountAddressParam(recipient.GetAccountID())), + transferFromContract(itest, recipientKp.Address(), asset, recipientContractID, recipientContractHash, "50", accountAddressParam(recipient.GetAccountID())), ) - assertAccountInvokeHostFunctionOperation(itest, recipientKp.Address(), strkeyRecipientContractID, recipientKp.Address(), "500.0000000") - assertContainsEffect(t, getTxEffects(itest, transferTx, asset), + assertAccountInvokeHostFunctionOperation(itest, recipientKp.Address(), strkeyRecipientContractID, recipientKp.Address(), "50.0000000") + assertContainsEffect(t, getTxEffects(itest, transferTxHash, asset), effects.EffectContractDebited, effects.EffectAccountCredited) - assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("1470")) + assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("950")) assertAssetStats(itest, assetStats{ code: code, issuer: issuer, numAccounts: 1, - balanceAccounts: amount.MustParse("1470"), + balanceAccounts: amount.MustParse("950"), balanceArchivedContracts: big.NewInt(0), numArchivedContracts: 0, numContracts: 1, - balanceContracts: big.NewInt(int64(amount.MustParse("530"))), + balanceContracts: big.NewInt(int64(amount.MustParse("50"))), contractID: stellarAssetContractID(itest, asset), }) - assertEventPayments(itest, transferTx, asset, strkeyRecipientContractID, recipientKp.Address(), "transfer", "500.0000000") + assertEventPayments(itest, transferTxHash, asset, strkeyRecipientContractID, recipientKp.Address(), "transfer", "50.0000000") balanceAmount, _, _ := assertInvokeHostFnSucceeds( itest, @@ -694,7 +755,7 @@ func TestContractTransferBetweenAccountAndContract(t *testing.T) { contractBalance(itest, issuer, asset, recipientContractID), ) assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, balanceAmount.Type) - assert.Equal(itest.CurrentTest(), xdr.Uint64(5300000000), (*balanceAmount.I128).Lo) + assert.Equal(itest.CurrentTest(), xdr.Uint64(500000000), (*balanceAmount.I128).Lo) assert.Equal(itest.CurrentTest(), xdr.Int64(0), (*balanceAmount.I128).Hi) } @@ -1411,8 +1472,8 @@ func burn(itest *integration.Test, sourceAccount string, asset xdr.Asset, assetA func assertInvokeHostFnSucceeds(itest *integration.Test, signer *keypair.Full, op *txnbuild.InvokeHostFunction) (*xdr.ScVal, string, *txnbuild.InvokeHostFunction) { acc := itest.MustGetAccount(signer) - preFlightOp, minFee := itest.PreflightHostFunctions(&acc, *op) - clientTx, err := itest.SubmitOperationsWithFee(&acc, signer, minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp := itest.PreflightHostFunctions(&acc, *op) + clientTx, err := itest.SubmitOperations(&acc, signer, &preFlightOp) require.NoError(itest.CurrentTest(), err) var txResult xdr.TransactionResult @@ -1462,12 +1523,12 @@ func mustCreateAndInstallContract(itest *integration.Test, signer *keypair.Full, createContractOp.Ext.SorobanData.Resources.Footprint.ReadWrite..., ) - sourceAccount, extendTTLOp, minFee := itest.PreflightExtendExpiration( + sourceAccount, extendTTLOp := itest.PreflightExtendExpiration( itest.Master().Address(), keys, LongTermTTL, ) - itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &extendTTLOp) + itest.MustSubmitOperations(&sourceAccount, itest.Master(), &extendTTLOp) contractHash := createContractOp.Ext.SorobanData.Resources.Footprint.ReadOnly[0].MustContractCode().Hash contractID := createContractOp.Ext.SorobanData.Resources.Footprint.ReadWrite[0].MustContractData().Contract.ContractId diff --git a/services/horizon/internal/integration/state_verifier_test.go b/services/horizon/internal/integration/state_verifier_test.go index 552bcf28e3..474fc65841 100644 --- a/services/horizon/internal/integration/state_verifier_test.go +++ b/services/horizon/internal/integration/state_verifier_test.go @@ -9,16 +9,19 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stellar/go/keypair" "github.com/stellar/go/services/horizon/internal/db2/history" "github.com/stellar/go/services/horizon/internal/test/integration" "github.com/stellar/go/txnbuild" "github.com/stellar/go/xdr" - "github.com/stretchr/testify/assert" ) func TestStateVerifier(t *testing.T) { - itest := integration.NewTest(t, integration.Config{}) + itest := integration.NewTest(t, integration.Config{ + QuickExpiration: true, + }) sponsored := keypair.MustRandom() sponsoredSource := &txnbuild.SimpleAccount{ diff --git a/services/horizon/internal/integration/transaction_preconditions_test.go b/services/horizon/internal/integration/transaction_preconditions_test.go index cc60eb2b2f..c9de84ac29 100644 --- a/services/horizon/internal/integration/transaction_preconditions_test.go +++ b/services/horizon/internal/integration/transaction_preconditions_test.go @@ -198,25 +198,28 @@ func TestTransactionPreconditionsMinSequenceNumberLedgerGap(t *testing.T) { t.Skip("Can't run with protocol < 19") } master := itest.Master() - masterAccount := itest.MasterAccount() - currentAccountSeq, err := masterAccount.GetSequenceNumber() - tt.NoError(err) + masterAccount := itest.MustGetAccount(master) - // gather up the current sequence number - networkLedger, err := itest.GetCurrentCoreLedgerSequence() - tt.NoError(err) + var networkLedger int + var err error + tt.Eventually(func() bool { + // gather up the current sequence number + networkLedger, err = itest.GetCurrentCoreLedgerSequence() + tt.NoError(err) + return uint32(networkLedger) > masterAccount.SequenceLedger + }, time.Second*10, time.Second) // build a tx with seqnum based on master.seqNum+1 as source account - txParams := buildTXParams(master, masterAccount, currentAccountSeq+1) + txParams := buildTXParams(master, &masterAccount, masterAccount.Sequence+1) // this txsub will error because the tx preconditions require a min sequence gap // which has been set 10000 sequence numbers greater than the current difference between // network ledger sequence and account sequnece numbers - txParams.Preconditions.MinSequenceNumberLedgerGap = uint32(int64(networkLedger) - currentAccountSeq + 10000) + txParams.Preconditions.MinSequenceNumberLedgerGap = uint32(networkLedger) - masterAccount.SequenceLedger + 10000 _, err = itest.SubmitMultiSigTransaction([]*keypair.Full{master}, txParams) tt.Error(err) - txParams.Preconditions.MinSequenceNumberLedgerGap = uint32(int64(networkLedger) - currentAccountSeq - 1) + txParams.Preconditions.MinSequenceNumberLedgerGap = uint32(networkLedger) - masterAccount.SequenceLedger - 1 // Now the transaction should be submitted without problems, the min sequence gap // is set to be one less then the current difference between network sequence and account sequence number. tx, err := itest.SubmitMultiSigTransaction([]*keypair.Full{master}, txParams) diff --git a/services/horizon/internal/integration/transaction_test.go b/services/horizon/internal/integration/transaction_test.go index 24b560b3d7..22ea465f2e 100644 --- a/services/horizon/internal/integration/transaction_test.go +++ b/services/horizon/internal/integration/transaction_test.go @@ -82,8 +82,8 @@ func TestP20MetaTransaction(t *testing.T) { require.NoError(t, err) installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), add_u64_contract) - preFlightOp, minFee := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) - clientTx := itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) + clientTx := itest.MustSubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) var txMetaResult xdr.TransactionMeta err = xdr.SafeUnmarshalBase64(clientTx.ResultMetaXdr, &txMetaResult) @@ -112,8 +112,8 @@ func TestP20MetaDisabledTransaction(t *testing.T) { require.NoError(t, err) installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), add_u64_contract) - preFlightOp, minFee := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) - clientTx := itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) + clientTx := itest.MustSubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) assert.Empty(t, clientTx.ResultMetaXdr) } diff --git a/services/horizon/internal/integration/txsub_async_test.go b/services/horizon/internal/integration/txsub_async_test.go index 1701702775..414768a5ed 100644 --- a/services/horizon/internal/integration/txsub_async_test.go +++ b/services/horizon/internal/integration/txsub_async_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/stellar/go/clients/horizonclient" + "github.com/stellar/go/keypair" "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/services/horizon/internal/test/integration" "github.com/stellar/go/support/errors" @@ -50,12 +51,16 @@ func TestAsyncTxSub_SuccessfulSubmission(t *testing.T) { LedgerBounds: &txnbuild.LedgerBounds{MinLedger: 0, MaxLedger: 100}, }, } + tx, err := itest.CreateSignedTransaction([]*keypair.Full{master}, txParams) + assert.NoError(t, err) + expectedHash, err := tx.HashHex(itest.GetPassPhrase()) + assert.NoError(t, err) - txResp, err := itest.AsyncSubmitTransaction(master, txParams) + txResp, err := itest.Client().AsyncSubmitTransaction(tx) assert.NoError(t, err) assert.Equal(t, txResp, horizon.AsyncTransactionSubmissionResponse{ TxStatus: "PENDING", - Hash: "6cbb7f714bd08cea7c30cab7818a35c510cbbfc0a6aa06172a1e94146ecf0165", + Hash: expectedHash, }) err = getTransaction(itest.Client(), txResp.Hash) @@ -84,13 +89,18 @@ func TestAsyncTxSub_SubmissionError(t *testing.T) { }, } - txResp, err := itest.AsyncSubmitTransaction(master, txParams) + tx, err := itest.CreateSignedTransaction([]*keypair.Full{master}, txParams) + assert.NoError(t, err) + expectedHash, err := tx.HashHex(itest.GetPassPhrase()) + assert.NoError(t, err) + + txResp, err := itest.Client().AsyncSubmitTransaction(tx) assert.NoError(t, err) assert.Equal(t, txResp, horizon.AsyncTransactionSubmissionResponse{ ErrorResultXDR: "AAAAAAAAAGT////7AAAAAA==", DeprecatedErrorResultXDR: "AAAAAAAAAGT////7AAAAAA==", TxStatus: "ERROR", - Hash: "0684df00f20efd5876f1b8d17bc6d3a68d8b85c06bb41e448815ecaa6307a251", + Hash: expectedHash, }) } @@ -116,20 +126,30 @@ func TestAsyncTxSub_SubmissionTryAgainLater(t *testing.T) { }, } - txResp, err := itest.AsyncSubmitTransaction(master, txParams) + tx, err := itest.CreateSignedTransaction([]*keypair.Full{master}, txParams) + assert.NoError(t, err) + expectedHash, err := tx.HashHex(itest.GetPassPhrase()) + assert.NoError(t, err) + + txResp, err := itest.Client().AsyncSubmitTransaction(tx) assert.NoError(t, err) assert.Equal(t, txResp, horizon.AsyncTransactionSubmissionResponse{ ErrorResultXDR: "", TxStatus: "PENDING", - Hash: "6cbb7f714bd08cea7c30cab7818a35c510cbbfc0a6aa06172a1e94146ecf0165", + Hash: expectedHash, }) - txResp, err = itest.AsyncSubmitTransaction(master, txParams) + tx, err = itest.CreateSignedTransaction([]*keypair.Full{master}, txParams) + assert.NoError(t, err) + expectedHash, err = tx.HashHex(itest.GetPassPhrase()) + assert.NoError(t, err) + + txResp, err = itest.Client().AsyncSubmitTransaction(tx) assert.NoError(t, err) assert.Equal(t, txResp, horizon.AsyncTransactionSubmissionResponse{ ErrorResultXDR: "", TxStatus: "TRY_AGAIN_LATER", - Hash: "d5eb72a4c1832b89965850fff0bd9bba4b6ca102e7c89099dcaba5e7d7d2e049", + Hash: expectedHash, }) } @@ -150,8 +170,14 @@ func TestAsyncTxSub_TransactionMalformed(t *testing.T) { require.NoError(t, err) installContractOp := assembleInstallContractCodeOp(t, master.Address(), "soroban_sac_test.wasm") - preFlightOp, minFee := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) - txParams := integration.GetBaseTransactionParamsWithFee(&sourceAccount, minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) + txParams := txnbuild.TransactionParams{ + SourceAccount: &sourceAccount, + Operations: []txnbuild.Operation{&preFlightOp}, + BaseFee: txnbuild.MinBaseFee, + Preconditions: txnbuild.Preconditions{TimeBounds: txnbuild.NewInfiniteTimeout()}, + IncrementSequenceNum: true, + } _, err = itest.AsyncSubmitTransaction(master, txParams) assert.EqualError( t, err, diff --git a/services/horizon/internal/integration/txsub_test.go b/services/horizon/internal/integration/txsub_test.go index 04c2eb17f0..b768b1504a 100644 --- a/services/horizon/internal/integration/txsub_test.go +++ b/services/horizon/internal/integration/txsub_test.go @@ -40,6 +40,7 @@ func TestTxSub(t *testing.T) { HorizonEnvironment: map[string]string{ "DISABLE_TX_SUB": "true", }, + QuickExpiration: true, }) master := itest.Master() @@ -74,8 +75,8 @@ func TestTxSubLimitsBodySize(t *testing.T) { require.NoError(t, err) installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), "soroban_sac_test.wasm") - preFlightOp, minFee := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) - _, err = itest.SubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp := itest.PreflightHostFunctions(&sourceAccount, *installContractOp) + _, err = itest.SubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) assert.EqualError( t, err, "horizon error: \"Transaction Malformed\" - check horizon.Error.Problem for more information", @@ -87,8 +88,8 @@ func TestTxSubLimitsBodySize(t *testing.T) { require.NoError(t, err) installContractOp = assembleInstallContractCodeOp(t, itest.Master().Address(), "soroban_add_u64.wasm") - preFlightOp, minFee = itest.PreflightHostFunctions(&sourceAccount, *installContractOp) - tx, err := itest.SubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp) + preFlightOp = itest.PreflightHostFunctions(&sourceAccount, *installContractOp) + tx, err := itest.SubmitOperations(&sourceAccount, itest.Master(), &preFlightOp) require.NoError(t, err) require.True(t, tx.Successful) } diff --git a/services/horizon/internal/test/integration/integration.go b/services/horizon/internal/test/integration/integration.go index 4a9c9c3446..77e9c68aa5 100644 --- a/services/horizon/internal/test/integration/integration.go +++ b/services/horizon/internal/test/integration/integration.go @@ -214,6 +214,8 @@ func NewTest(t *testing.T, config Config) *Test { } i.WaitForHorizonIngest() + + i.upgradeLimits() } return i @@ -725,6 +727,45 @@ func (i *Test) waitForCore() { i.t.Fatalf("Core could not sync after %v + %v", maxWaitForCoreStartup, maxWaitForCoreUpgrade) } +func (i *Test) upgradeLimits() { + if i.config.ProtocolVersion < 22 || i.config.SkipCoreContainerCreation || i.config.QuickExpiration { + return + } + + err := i.CoreClient().UpgradeTxSetSize(context.Background(), 100_000, time.Unix(0, 0)) + require.NoError(i.t, err) + + err = i.CoreClient().UpgradeSorobanTxSetSize(context.Background(), 100_000, time.Unix(0, 0)) + require.NoError(i.t, err) + + contents, err := os.ReadFile(filepath.Join("testdata", "unlimited-config.xdr")) + require.NoError(i.t, err) + var configSet xdr.ConfigUpgradeSet + err = xdr.SafeUnmarshalBase64(string(contents), &configSet) + require.NoError(i.t, err) + + upgradeTransactions, upgradeKey, err := stellarcore.GenSorobanConfigUpgradeTxAndKey(stellarcore.GenSorobanConfig{ + BaseSeqNum: 0, + NetworkPassphrase: i.Config().NetworkPassphrase, + SigningKey: i.Master(), + StellarCorePath: i.CoreBinaryPath(), + }, configSet) + require.NoError(i.t, err) + + for _, transaction := range upgradeTransactions { + var b64 string + b64, err = xdr.MarshalBase64(transaction) + require.NoError(i.t, err) + response, err := i.Client().SubmitTransactionXDR(b64) + require.NoError(i.t, err) + require.True(i.t, response.Successful) + } + + require.NoError(i.t, + i.CoreClient().UpgradeSorobanConfig(context.Background(), upgradeKey, time.Unix(0, 0)), + ) +} + const stellarRPCInitTime = 60 * 6 * time.Second const stellarRPCHealthCheckInterval = time.Second @@ -770,7 +811,7 @@ type RPCSimulateTxResponse struct { func (i *Test) PreflightHostFunctions( sourceAccount txnbuild.Account, function txnbuild.InvokeHostFunction, -) (txnbuild.InvokeHostFunction, int64) { +) txnbuild.InvokeHostFunction { if function.HostFunction.Type == xdr.HostFunctionTypeHostFunctionTypeInvokeContract { fmt.Printf("Preflighting function call to: %s\n", string(function.HostFunction.InvokeContract.FunctionName)) } @@ -795,7 +836,7 @@ func (i *Test) PreflightHostFunctions( } function.Auth = funAuth - return function, result.MinResourceFee + return function } func (i *Test) simulateTransaction( @@ -809,7 +850,13 @@ func (i *Test) simulateTransaction( // TODO: soroban-tools should be exporting a proper Go client ch := jhttp.NewChannel("http://localhost:"+strconv.Itoa(StellarRPCPort), nil) stellarRPCClient := jrpc2.NewClient(ch, nil) - txParams := GetBaseTransactionParamsWithFee(sourceAccount, txnbuild.MinBaseFee, op) + txParams := txnbuild.TransactionParams{ + SourceAccount: sourceAccount, + Operations: []txnbuild.Operation{op}, + BaseFee: txnbuild.MinBaseFee, + Preconditions: txnbuild.Preconditions{TimeBounds: txnbuild.NewInfiniteTimeout()}, + IncrementSequenceNum: true, + } txParams.IncrementSequenceNum = false tx, err := txnbuild.NewTransaction(txParams) assert.NoError(i.t, err) @@ -887,7 +934,7 @@ func (i *Test) WaitUntilLedgerEntryTTL(ledgerKey xdr.LedgerKey) { func (i *Test) PreflightExtendExpiration( account string, ledgerKeys []xdr.LedgerKey, extendAmt uint32, -) (proto.Account, txnbuild.ExtendFootprintTtl, int64) { +) (proto.Account, txnbuild.ExtendFootprintTtl) { sourceAccount, err := i.Client().AccountDetail(sdk.AccountRequest{ AccountID: account, }) @@ -910,18 +957,18 @@ func (i *Test) PreflightExtendExpiration( }, }, } - result, transactionData := i.simulateTransaction(&sourceAccount, &bumpFootprint) + _, transactionData := i.simulateTransaction(&sourceAccount, &bumpFootprint) bumpFootprint.Ext = xdr.TransactionExt{ V: 1, SorobanData: &transactionData, } - return sourceAccount, bumpFootprint, result.MinResourceFee + return sourceAccount, bumpFootprint } func (i *Test) RestoreFootprint( account string, ledgerKey xdr.LedgerKey, -) (proto.Account, txnbuild.RestoreFootprint, int64) { +) (proto.Account, txnbuild.RestoreFootprint) { sourceAccount, err := i.Client().AccountDetail(sdk.AccountRequest{ AccountID: account, }) @@ -942,13 +989,13 @@ func (i *Test) RestoreFootprint( }, }, } - result, transactionData := i.simulateTransaction(&sourceAccount, &restoreFootprint) + _, transactionData := i.simulateTransaction(&sourceAccount, &restoreFootprint) restoreFootprint.Ext = xdr.TransactionExt{ V: 1, SorobanData: &transactionData, } - return sourceAccount, restoreFootprint, result.MinResourceFee + return sourceAccount, restoreFootprint } // UpgradeProtocol arms Core with upgrade and blocks until protocol is upgraded. @@ -1263,13 +1310,7 @@ func (i *Test) MustGetAccount(source *keypair.Full) proto.Account { func (i *Test) MustSubmitOperations( source txnbuild.Account, signer *keypair.Full, ops ...txnbuild.Operation, ) proto.Transaction { - return i.MustSubmitOperationsWithFee(source, signer, txnbuild.MinBaseFee, ops...) -} - -func (i *Test) MustSubmitOperationsWithFee( - source txnbuild.Account, signer *keypair.Full, fee int64, ops ...txnbuild.Operation, -) proto.Transaction { - tx, err := i.SubmitOperationsWithFee(source, signer, fee, ops...) + tx, err := i.SubmitOperations(source, signer, ops...) panicIf(err) return tx } @@ -1283,19 +1324,7 @@ func (i *Test) SubmitOperations( func (i *Test) SubmitMultiSigOperations( source txnbuild.Account, signers []*keypair.Full, ops ...txnbuild.Operation, ) (proto.Transaction, error) { - return i.SubmitMultiSigOperationsWithFee(source, signers, txnbuild.MinBaseFee, ops...) -} - -func (i *Test) SubmitOperationsWithFee( - source txnbuild.Account, signer *keypair.Full, fee int64, ops ...txnbuild.Operation, -) (proto.Transaction, error) { - return i.SubmitMultiSigOperationsWithFee(source, []*keypair.Full{signer}, fee, ops...) -} - -func (i *Test) SubmitMultiSigOperationsWithFee( - source txnbuild.Account, signers []*keypair.Full, fee int64, ops ...txnbuild.Operation, -) (proto.Transaction, error) { - tx, err := i.CreateSignedTransactionFromOpsWithFee(source, signers, fee, ops...) + tx, err := i.CreateSignedTransactionFromOps(source, signers, ops...) if err != nil { return proto.Transaction{}, err } @@ -1371,31 +1400,13 @@ func (i *Test) CreateSignedTransaction(signers []*keypair.Full, txParams txnbuil func (i *Test) CreateSignedTransactionFromOps( source txnbuild.Account, signers []*keypair.Full, ops ...txnbuild.Operation, ) (*txnbuild.Transaction, error) { - return i.CreateSignedTransactionFromOpsWithFee(source, signers, txnbuild.MinBaseFee, ops...) -} - -func (i *Test) CreateSignedTransactionFromOpsWithFee( - source txnbuild.Account, signers []*keypair.Full, fee int64, ops ...txnbuild.Operation, -) (*txnbuild.Transaction, error) { - txParams := GetBaseTransactionParamsWithFee(source, fee, ops...) - return i.CreateSignedTransaction(signers, txParams) -} - -func GetBaseTransactionParamsWithFee(source txnbuild.Account, fee int64, ops ...txnbuild.Operation) txnbuild.TransactionParams { - return txnbuild.TransactionParams{ + return i.CreateSignedTransaction(signers, txnbuild.TransactionParams{ SourceAccount: source, Operations: ops, - BaseFee: fee, + BaseFee: txnbuild.MinBaseFee, Preconditions: txnbuild.Preconditions{TimeBounds: txnbuild.NewInfiniteTimeout()}, IncrementSequenceNum: true, - } -} - -func (i *Test) CreateUnsignedTransaction( - source txnbuild.Account, ops ...txnbuild.Operation, -) (*txnbuild.Transaction, error) { - txParams := GetBaseTransactionParamsWithFee(source, txnbuild.MinBaseFee, ops...) - return txnbuild.NewTransaction(txParams) + }) } func (i *Test) GetCurrentCoreLedgerSequence() (int, error) { diff --git a/txnbuild/invoke_host_function.go b/txnbuild/invoke_host_function.go index c278b5f81c..da983aa5f5 100644 --- a/txnbuild/invoke_host_function.go +++ b/txnbuild/invoke_host_function.go @@ -1,6 +1,9 @@ package txnbuild import ( + "github.com/stellar/go/amount" + "github.com/stellar/go/ingest/sac" + "github.com/stellar/go/strkey" "github.com/stellar/go/support/errors" "github.com/stellar/go/xdr" ) @@ -12,6 +15,210 @@ type InvokeHostFunction struct { Ext xdr.TransactionExt } +type SorobanFees struct { + Instructions uint32 + ReadBytes uint32 + WriteBytes uint32 + ResourceFee int64 +} + +var defaultPaymentToContractFees = SorobanFees{ + Instructions: 400_000, + ReadBytes: 1_000, + WriteBytes: 1_000, + ResourceFee: 5_000_000, +} + +// PaymentToContractParams configures the payment returned by NewPaymentToContract +type PaymentToContractParams struct { + // NetworkPassphrase is the passphrase for the Stellar network + NetworkPassphrase string + // Destination is the contract recipient of the payment + Destination string + // Amount is the amount being transferred + Amount string + // Asset is the asset being transferred + Asset Asset + // SourceAccount is the source account of the payment, it must be a Stellar account in strkey encoded`VersionByteAccountID` format, i.e. a 'G' account. + SourceAccount string + // Fees configures the fee values for the + // soroban transaction. If this field is omitted + // default fee values will be used + Fees *SorobanFees +} + +// NewPaymentToContract constructs an invoke host operation to send a payment from a +// an account to a destination smart contract. Note the account sending the payment +// must be the source account of the operation because the returned invoke host operation +// will use the source account as the auth credentials. +func NewPaymentToContract(params PaymentToContractParams) (InvokeHostFunction, error) { + asset, err := params.Asset.ToXDR() + if err != nil { + return InvokeHostFunction{}, err + } + + var assetContractID xdr.Hash + assetContractID, err = asset.ContractID(params.NetworkPassphrase) + if err != nil { + return InvokeHostFunction{}, err + } + + sourceAccount, err := xdr.AddressToAccountId(params.SourceAccount) + if err != nil { + return InvokeHostFunction{}, err + } + + decoded, err := strkey.Decode(strkey.VersionByteContract, params.Destination) + if err != nil { + return InvokeHostFunction{}, err + } + var destinationContractID xdr.Hash + copy(destinationContractID[:], decoded) + + parsedAmount, err := amount.Parse(params.Amount) + if err != nil { + return InvokeHostFunction{}, err + } + + transferArgs := xdr.ScVec{ + xdr.ScVal{ + Type: xdr.ScValTypeScvAddress, + Address: &xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeAccount, + AccountId: &sourceAccount, + }, + }, + xdr.ScVal{ + Type: xdr.ScValTypeScvAddress, + Address: &xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeContract, + ContractId: &destinationContractID, + }, + }, + xdr.ScVal{ + Type: xdr.ScValTypeScvI128, + I128: &xdr.Int128Parts{ + Hi: 0, + Lo: xdr.Uint64(parsedAmount), + }, + }, + } + + resources := defaultPaymentToContractFees + if params.Fees != nil { + resources = *params.Fees + } + + assetContractInstance := xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeContractData, + ContractData: &xdr.LedgerKeyContractData{ + Contract: xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeContract, + ContractId: &assetContractID, + }, + Key: xdr.ScVal{ + Type: xdr.ScValTypeScvLedgerKeyContractInstance, + }, + Durability: xdr.ContractDataDurabilityPersistent, + }, + } + + footprint := xdr.LedgerFootprint{ + ReadOnly: []xdr.LedgerKey{ + assetContractInstance, + }, + ReadWrite: []xdr.LedgerKey{ + sac.ContractBalanceLedgerKey(assetContractID, destinationContractID), + }, + } + + if asset.IsNative() { + footprint.ReadWrite = append(footprint.ReadWrite, + xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeAccount, + Account: &xdr.LedgerKeyAccount{ + AccountId: sourceAccount, + }, + }, + ) + } else { + issuer, err := asset.GetIssuerAccountId() + if err != nil { + return InvokeHostFunction{}, err + } + footprint.ReadOnly = append(footprint.ReadOnly, + xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeAccount, + Account: &xdr.LedgerKeyAccount{ + AccountId: issuer, + }, + }, + ) + if !sourceAccount.Equals(issuer) { + footprint.ReadWrite = append(footprint.ReadWrite, + xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeTrustline, + TrustLine: &xdr.LedgerKeyTrustLine{ + AccountId: sourceAccount, + Asset: xdr.TrustLineAsset{ + Type: asset.Type, + AlphaNum4: asset.AlphaNum4, + AlphaNum12: asset.AlphaNum12, + }, + }, + }, + ) + } + } + + return InvokeHostFunction{ + HostFunction: xdr.HostFunction{ + Type: xdr.HostFunctionTypeHostFunctionTypeInvokeContract, + InvokeContract: &xdr.InvokeContractArgs{ + ContractAddress: xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeContract, + ContractId: &assetContractID, + }, + FunctionName: "transfer", + Args: transferArgs, + }, + }, + SourceAccount: params.SourceAccount, + Auth: []xdr.SorobanAuthorizationEntry{ + { + Credentials: xdr.SorobanCredentials{ + Type: xdr.SorobanCredentialsTypeSorobanCredentialsSourceAccount, + }, + RootInvocation: xdr.SorobanAuthorizedInvocation{ + Function: xdr.SorobanAuthorizedFunction{ + Type: xdr.SorobanAuthorizedFunctionTypeSorobanAuthorizedFunctionTypeContractFn, + ContractFn: &xdr.InvokeContractArgs{ + ContractAddress: xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeContract, + ContractId: &assetContractID, + }, + FunctionName: "transfer", + Args: transferArgs, + }, + }, + }, + }, + }, + Ext: xdr.TransactionExt{ + V: 1, + SorobanData: &xdr.SorobanTransactionData{ + Resources: xdr.SorobanResources{ + Footprint: footprint, + Instructions: xdr.Uint32(resources.Instructions), + ReadBytes: xdr.Uint32(resources.ReadBytes), + WriteBytes: xdr.Uint32(resources.WriteBytes), + }, + ResourceFee: xdr.Int64(resources.ResourceFee), + }, + }, + }, nil +} + func (f *InvokeHostFunction) BuildXDR() (xdr.Operation, error) { opType := xdr.OperationTypeInvokeHostFunction diff --git a/txnbuild/invoke_host_function_test.go b/txnbuild/invoke_host_function_test.go index 8fd5e6e48e..9aae9958db 100644 --- a/txnbuild/invoke_host_function_test.go +++ b/txnbuild/invoke_host_function_test.go @@ -3,11 +3,62 @@ package txnbuild import ( "testing" + "github.com/stretchr/testify/require" + + "github.com/stellar/go/network" + "github.com/stellar/go/strkey" "github.com/stellar/go/xdr" "github.com/stretchr/testify/assert" ) +func TestPaymentToContract(t *testing.T) { + issuer := newKeypair0() + sourceAccount := newKeypair1() + params := PaymentToContractParams{ + NetworkPassphrase: network.PublicNetworkPassphrase, + Destination: "invalid", + Amount: "10", + Asset: CreditAsset{ + Code: "USD", + Issuer: issuer.Address(), + }, + SourceAccount: sourceAccount.Address(), + } + _, err := NewPaymentToContract(params) + require.Error(t, err) + + params.Destination = newKeypair2().Address() + _, err = NewPaymentToContract(params) + require.Error(t, err) + + contractID := xdr.Hash{1} + params.Destination = strkey.MustEncode(strkey.VersionByteContract, contractID[:]) + + op, err := NewPaymentToContract(params) + require.NoError(t, err) + require.NoError(t, op.Validate()) + require.Equal(t, int64(op.Ext.SorobanData.ResourceFee), defaultPaymentToContractFees.ResourceFee) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.WriteBytes), defaultPaymentToContractFees.WriteBytes) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.ReadBytes), defaultPaymentToContractFees.ReadBytes) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.Instructions), defaultPaymentToContractFees.Instructions) + + params.Fees = &SorobanFees{ + Instructions: 1, + ReadBytes: 2, + WriteBytes: 3, + ResourceFee: 4, + } + + op, err = NewPaymentToContract(params) + require.NoError(t, err) + require.NoError(t, op.Validate()) + require.Equal(t, int64(op.Ext.SorobanData.ResourceFee), int64(4)) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.WriteBytes), uint32(3)) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.ReadBytes), uint32(2)) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.Instructions), uint32(1)) +} + func TestCreateInvokeHostFunctionValid(t *testing.T) { kp1 := newKeypair1() sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761100)) diff --git a/txnbuild/restore_footprint.go b/txnbuild/restore_footprint.go index d71a94fb4b..2a4ff3a4cd 100644 --- a/txnbuild/restore_footprint.go +++ b/txnbuild/restore_footprint.go @@ -1,6 +1,8 @@ package txnbuild import ( + "github.com/stellar/go/ingest/sac" + "github.com/stellar/go/strkey" "github.com/stellar/go/support/errors" "github.com/stellar/go/xdr" ) @@ -10,6 +12,77 @@ type RestoreFootprint struct { Ext xdr.TransactionExt } +var defaultAssetBalanceRestorationFees = SorobanFees{ + Instructions: 0, + ReadBytes: 500, + WriteBytes: 500, + ResourceFee: 4_000_000, +} + +// AssetBalanceRestorationParams configures the restore footprint operation returned by +// NewAssetBalanceRestoration +type AssetBalanceRestorationParams struct { + // NetworkPassphrase is the passphrase for the Stellar network + NetworkPassphrase string + // Contract is the contract which holds the asset balance + Contract string + // Asset is the asset which is held in the balance + Asset Asset + // SourceAccount is the source account for the restoration operation + SourceAccount string + // Fees configures the fee values for the + // soroban transaction. If this field is omitted + // default fee values will be used + Fees SorobanFees +} + +// NewAssetBalanceRestoration constructs a restore footprint operation which restores an +// asset balance for a smart contract +func NewAssetBalanceRestoration(params AssetBalanceRestorationParams) (RestoreFootprint, error) { + asset, err := params.Asset.ToXDR() + if err != nil { + return RestoreFootprint{}, err + } + + var assetContractID xdr.Hash + assetContractID, err = asset.ContractID(params.NetworkPassphrase) + if err != nil { + return RestoreFootprint{}, err + } + + decoded, err := strkey.Decode(strkey.VersionByteContract, params.Contract) + if err != nil { + return RestoreFootprint{}, err + } + var contractID xdr.Hash + copy(contractID[:], decoded) + + resources := params.Fees + if resources.ResourceFee == 0 { + resources = defaultAssetBalanceRestorationFees + } + + return RestoreFootprint{ + SourceAccount: params.SourceAccount, + Ext: xdr.TransactionExt{ + V: 1, + SorobanData: &xdr.SorobanTransactionData{ + Resources: xdr.SorobanResources{ + Footprint: xdr.LedgerFootprint{ + ReadWrite: []xdr.LedgerKey{ + sac.ContractBalanceLedgerKey(assetContractID, contractID), + }, + }, + Instructions: xdr.Uint32(resources.Instructions), + ReadBytes: xdr.Uint32(resources.ReadBytes), + WriteBytes: xdr.Uint32(resources.WriteBytes), + }, + ResourceFee: xdr.Int64(resources.ResourceFee), + }, + }, + }, nil +} + func (f *RestoreFootprint) BuildXDR() (xdr.Operation, error) { xdrOp := xdr.RestoreFootprintOp{ Ext: xdr.ExtensionPoint{ diff --git a/txnbuild/restore_footprint_test.go b/txnbuild/restore_footprint_test.go new file mode 100644 index 0000000000..487b0cf1ec --- /dev/null +++ b/txnbuild/restore_footprint_test.go @@ -0,0 +1,57 @@ +package txnbuild + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/stellar/go/network" + "github.com/stellar/go/strkey" + "github.com/stellar/go/xdr" +) + +func TestRestoreAssetBalance(t *testing.T) { + issuer := newKeypair0() + sourceAccount := newKeypair1() + params := AssetBalanceRestorationParams{ + NetworkPassphrase: network.PublicNetworkPassphrase, + Contract: "invalid", + Asset: CreditAsset{ + Code: "USD", + Issuer: issuer.Address(), + }, + SourceAccount: sourceAccount.Address(), + } + _, err := NewAssetBalanceRestoration(params) + require.Error(t, err) + + params.Contract = newKeypair2().Address() + _, err = NewAssetBalanceRestoration(params) + require.Error(t, err) + + contractID := xdr.Hash{1} + params.Contract = strkey.MustEncode(strkey.VersionByteContract, contractID[:]) + + op, err := NewAssetBalanceRestoration(params) + require.NoError(t, err) + require.NoError(t, op.Validate()) + require.Equal(t, int64(op.Ext.SorobanData.ResourceFee), defaultAssetBalanceRestorationFees.ResourceFee) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.WriteBytes), defaultAssetBalanceRestorationFees.WriteBytes) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.ReadBytes), defaultAssetBalanceRestorationFees.ReadBytes) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.Instructions), defaultAssetBalanceRestorationFees.Instructions) + + params.Fees = SorobanFees{ + Instructions: 1, + ReadBytes: 2, + WriteBytes: 3, + ResourceFee: 4, + } + + op, err = NewAssetBalanceRestoration(params) + require.NoError(t, err) + require.NoError(t, op.Validate()) + require.Equal(t, int64(op.Ext.SorobanData.ResourceFee), int64(4)) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.WriteBytes), uint32(3)) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.ReadBytes), uint32(2)) + require.Equal(t, uint32(op.Ext.SorobanData.Resources.Instructions), uint32(1)) +} diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index 0cf284fe02..b32d73c7e5 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -847,16 +847,6 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { return nil, errors.New("transaction has no operations") } - // check if maxFee fits in a uint32 - // 64 bit fees are only available in fee bump transactions - // if maxFee is negative then there must have been an int overflow - hi, lo := bits.Mul64(uint64(params.BaseFee), uint64(len(params.Operations))) - if hi > 0 || lo > math.MaxUint32 { - return nil, errors.Errorf( - "base fee %d results in an overflow of max fee", params.BaseFee) - } - tx.maxFee = int64(lo) - // Check that all preconditions are valid if err = tx.preconditions.Validate(); err != nil { return nil, errors.Wrap(err, "invalid preconditions") @@ -872,7 +862,6 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { V1: &xdr.TransactionV1Envelope{ Tx: xdr.Transaction{ SourceAccount: sourceAccount, - Fee: xdr.Uint32(tx.maxFee), SeqNum: xdr.SequenceNumber(sequence), Cond: precondXdr, }, @@ -910,13 +899,32 @@ func NewTransaction(params TransactionParams) (*Transaction, error) { } // In case it's a smart contract transaction, we need to include the Ext field within the envelope. + var sorobanFee uint64 if sorobanOp != nil { envelope.V1.Tx.Ext, err = sorobanOp.BuildTransactionExt() if err != nil { return nil, errors.Wrap(err, fmt.Sprintf("failed to build operation %T", sorobanOp)) } + if envelope.V1.Tx.Ext.SorobanData != nil { + sorobanFee = uint64(envelope.V1.Tx.Ext.SorobanData.ResourceFee) + } } + // check if maxFee fits in a uint32 + // 64 bit fees are only available in fee bump transactions + // if maxFee is negative then there must have been an int overflow + hi, lo := bits.Mul64(uint64(params.BaseFee), uint64(len(params.Operations))) + if hi > 0 || lo > math.MaxUint32 { + return nil, errors.Errorf( + "base fee %d results in an overflow of max fee", params.BaseFee) + } + totalFee := lo + sorobanFee + if totalFee < lo || totalFee > math.MaxUint32 { + return nil, fmt.Errorf("soroban fee %v results in an overflow of max fee", sorobanFee) + } + tx.maxFee = int64(totalFee) + envelope.V1.Tx.Fee = xdr.Uint32(totalFee) + tx.envelope = envelope return tx, nil } @@ -976,18 +984,11 @@ func NewFeeBumpTransaction(params FeeBumpTransactionParams) (*FeeBumpTransaction // number of operations in the inner transaction. Correspondingly, the minimum fee for // the fee-bump transaction is one base fee more than the minimum fee for the inner // transaction. - maxFee: params.BaseFee * int64(len(inner.operations)+1), feeAccount: params.FeeAccount, inner: new(Transaction), } *tx.inner = *inner - hi, lo := bits.Mul64(uint64(params.BaseFee), uint64(len(inner.operations)+1)) - if hi > 0 || lo > math.MaxInt64 { - return nil, errors.Errorf("base fee %d results in an overflow of max fee", params.BaseFee) - } - tx.maxFee = int64(lo) - if tx.baseFee < tx.inner.baseFee { return tx, errors.New("base fee cannot be lower than provided inner transaction fee") } @@ -997,6 +998,21 @@ func NewFeeBumpTransaction(params FeeBumpTransactionParams) (*FeeBumpTransaction ) } + var sorobanFee uint64 + if inner.envelope.V1 != nil && inner.envelope.V1.Tx.Ext.SorobanData != nil { + sorobanFee = uint64(inner.envelope.V1.Tx.Ext.SorobanData.ResourceFee) + } + + hi, lo := bits.Mul64(uint64(params.BaseFee), uint64(len(inner.operations)+1)) + if hi > 0 || lo > math.MaxInt64 { + return nil, errors.Errorf("base fee %d results in an overflow of max fee", params.BaseFee) + } + totalFee := lo + sorobanFee + if totalFee < lo || totalFee > math.MaxInt64 { + return nil, fmt.Errorf("soroban fee %v results in an overflow of max fee", sorobanFee) + } + tx.maxFee = int64(totalFee) + var feeSource xdr.MuxedAccount if err := feeSource.SetAddress(tx.feeAccount); err != nil { return tx, errors.Wrap(err, "fee account is not a valid address") diff --git a/txnbuild/transaction_fee_test.go b/txnbuild/transaction_fee_test.go index 04dd989fef..d3adfac20c 100644 --- a/txnbuild/transaction_fee_test.go +++ b/txnbuild/transaction_fee_test.go @@ -4,8 +4,9 @@ import ( "math" "testing" - "github.com/stellar/go/keypair" "github.com/stretchr/testify/assert" + + "github.com/stellar/go/keypair" ) func TestBaseFeeCanBeZeroOrPositive(t *testing.T) {