Skip to content

Commit

Permalink
fix: add parity to SimulateTransactionBumpAndRestoreFootprint
Browse files Browse the repository at this point in the history
  • Loading branch information
willemneal committed Sep 11, 2023
1 parent a18e44f commit ffa5446
Showing 1 changed file with 149 additions and 0 deletions.
149 changes: 149 additions & 0 deletions cmd/soroban-rpc/internal/test/cli_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package test

import (
"context"
"crypto/sha256"
"fmt"
"os"
Expand All @@ -14,6 +15,8 @@ import (
"github.com/stellar/go/keypair"
"github.com/stellar/go/txnbuild"
"github.com/stellar/go/xdr"
"github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/methods"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gotest.tools/v3/icmd"
)
Expand Down Expand Up @@ -63,6 +66,152 @@ func TestCLIContractDeployAndInvoke(t *testing.T) {
require.Contains(t, output, `["Hello","world"]`)
}

func TestCLISimulateTransactionBumpAndRestoreFootprint(t *testing.T) {
test := NewCLITest(t)
ch := jhttp.NewChannel(test.sorobanRPCURL(), nil)
client := jrpc2.NewClient(ch, nil)

sourceAccount := keypair.Root(StandaloneNetworkPassphrase)
address := sourceAccount.Address()
account := txnbuild.NewSimpleAccount(address, 0)

helloWorldContract := getHelloWorldContract(t)

params := preflightTransactionParams(t, client, txnbuild.TransactionParams{
SourceAccount: &account,
IncrementSequenceNum: true,
Operations: []txnbuild.Operation{
createInstallContractCodeOperation(account.AccountID, helloWorldContract),
},
BaseFee: txnbuild.MinBaseFee,
Preconditions: txnbuild.Preconditions{
TimeBounds: txnbuild.NewInfiniteTimeout(),
},
})
tx, err := txnbuild.NewTransaction(params)
assert.NoError(t, err)
sendSuccessfulTransaction(t, client, sourceAccount, tx)

params = preflightTransactionParams(t, client, txnbuild.TransactionParams{
SourceAccount: &account,
IncrementSequenceNum: true,
Operations: []txnbuild.Operation{
createCreateContractOperation(t, address, helloWorldContract, StandaloneNetworkPassphrase),
},
BaseFee: txnbuild.MinBaseFee,
Preconditions: txnbuild.Preconditions{
TimeBounds: txnbuild.NewInfiniteTimeout(),
},
})
tx, err = txnbuild.NewTransaction(params)
assert.NoError(t, err)
sendSuccessfulTransaction(t, client, sourceAccount, tx)

contractID := getContractID(t, address, testSalt, StandaloneNetworkPassphrase)
runSuccessfulCLICmd(t, fmt.Sprintf("contract invoke --id %s -- inc", contractID))

// get the counter ledger entry expiration
contractIDHash := xdr.Hash(contractID)
counterSym := xdr.ScSymbol("COUNTER")
key := xdr.LedgerKey{
Type: xdr.LedgerEntryTypeContractData,
ContractData: &xdr.LedgerKeyContractData{
Contract: xdr.ScAddress{
Type: xdr.ScAddressTypeScAddressTypeContract,
ContractId: &contractIDHash,
},
Key: xdr.ScVal{
Type: xdr.ScValTypeScvSymbol,
Sym: &counterSym,
},
Durability: xdr.ContractDataDurabilityPersistent,
},
}

binKey, err := key.MarshalBinary()
assert.NoError(t, err)

expirationKey := xdr.LedgerKey{
Type: xdr.LedgerEntryTypeExpiration,
Expiration: &xdr.LedgerKeyExpiration{
KeyHash: sha256.Sum256(binKey),
},
}

keyB64, err := xdr.MarshalBase64(expirationKey)
require.NoError(t, err)
getLedgerEntryrequest := methods.GetLedgerEntryRequest{
Key: keyB64,
}
var getLedgerEntryResult methods.GetLedgerEntryResponse
err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult)
assert.NoError(t, err)
var entry xdr.LedgerEntryData
assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry))

assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type)
initialExpirationSeq := entry.Expiration.ExpirationLedgerSeq

// bump the initial expiration
params = preflightTransactionParams(t, client, txnbuild.TransactionParams{
SourceAccount: &account,
IncrementSequenceNum: true,
Operations: []txnbuild.Operation{
&txnbuild.BumpFootprintExpiration{
LedgersToExpire: 20,
Ext: xdr.TransactionExt{
V: 1,
SorobanData: &xdr.SorobanTransactionData{
Resources: xdr.SorobanResources{
Footprint: xdr.LedgerFootprint{
ReadOnly: []xdr.LedgerKey{key},
},
},
},
},
},
},
BaseFee: txnbuild.MinBaseFee,
Preconditions: txnbuild.Preconditions{
TimeBounds: txnbuild.NewInfiniteTimeout(),
},
})
tx, err = txnbuild.NewTransaction(params)
assert.NoError(t, err)
sendSuccessfulTransaction(t, client, sourceAccount, tx)

err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult)
assert.NoError(t, err)
assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry))
assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type)
newExpirationSeq := entry.Expiration.ExpirationLedgerSeq
assert.Greater(t, newExpirationSeq, initialExpirationSeq)

// Wait until it expires
waitForExpiration := func() {
expired := false
for i := 0; i < 50; i++ {
err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult)
assert.NoError(t, err)
assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry))
assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type)
// See https://soroban.stellar.org/docs/fundamentals-and-concepts/state-expiration#expiration-ledger
currentLedger := getLedgerEntryResult.LatestLedger + 1
if xdr.Uint32(currentLedger) > entry.Expiration.ExpirationLedgerSeq {
expired = true
t.Logf("ledger entry expired")
break
}
t.Log("waiting for ledger entry to expire at ledger", entry.Expiration.ExpirationLedgerSeq)
time.Sleep(time.Second)
}
require.True(t, expired)
}
waitForExpiration()
output := runSuccessfulCLICmd(t, fmt.Sprintf("contract invoke --id %s -- inc", contractID))
require.Equal(t, "2", output)
}

func runSuccessfulCLICmd(t *testing.T, cmd string) string {
res := runCLICommand(t, cmd)
require.NoError(t, res.Error, fmt.Sprintf("stderr:\n%s\nstdout:\n%s\n", res.Stderr(), res.Stdout()))
Expand Down

0 comments on commit ffa5446

Please sign in to comment.