diff --git a/cmd/soroban-rpc/internal/test/cli_test.go b/cmd/soroban-rpc/internal/test/cli_test.go index e4b5de52a..87c83d8fe 100644 --- a/cmd/soroban-rpc/internal/test/cli_test.go +++ b/cmd/soroban-rpc/internal/test/cli_test.go @@ -1,6 +1,7 @@ package test import ( + "context" "crypto/sha256" "encoding/hex" "fmt" @@ -18,6 +19,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gotest.tools/v3/icmd" + + "github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/methods" ) func TestCLIContractInstall(t *testing.T) { @@ -76,6 +79,92 @@ func TestCLIRestorePreamble(t *testing.T) { // This ensures that the CLI restores the entry (using the RestorePreamble in the simulateTransaction response) ch := jhttp.NewChannel(test.sorobanRPCURL(), nil) client := jrpc2.NewClient(ch, nil) + waitForLedgerEntryToExpire(t, client, expirationKey(t, counterLedgerKey(t, strkeyContractID))) + + count = runSuccessfulCLICmd(t, fmt.Sprintf("contract invoke --id %s -- inc", strkeyContractID)) + require.Equal(t, "3", count) +} + +func TestCLIBump(t *testing.T) { + test := NewCLITest(t) + strkeyContractID := runSuccessfulCLICmd(t, fmt.Sprintf("contract deploy --salt=%s --wasm %s", hex.EncodeToString(testSalt[:]), helloWorldContractPath)) + count := runSuccessfulCLICmd(t, fmt.Sprintf("contract invoke --id %s -- inc", strkeyContractID)) + require.Equal(t, "1", count) + + ch := jhttp.NewChannel(test.sorobanRPCURL(), nil) + client := jrpc2.NewClient(ch, nil) + + initialExpirationSeq := getExpirationForLedgerEntry(t, client, expirationKey(t, counterLedgerKey(t, strkeyContractID))) + + bumpOutput := runSuccessfulCLICmd( + t, + fmt.Sprintf( + "contract bump --id %s --key COUNTER --durability persistent --ledgers-to-expire 20", + strkeyContractID, + ), + ) + + newExpirationSeq := getExpirationForLedgerEntry(t, client, expirationKey(t, counterLedgerKey(t, strkeyContractID))) + assert.Greater(t, newExpirationSeq, initialExpirationSeq) + assert.Equal(t, fmt.Sprintf("New expiration ledger: %d", newExpirationSeq), bumpOutput) +} + +func TestCLIRestore(t *testing.T) { + test := NewCLITest(t) + strkeyContractID := runSuccessfulCLICmd(t, fmt.Sprintf("contract deploy --salt=%s --wasm %s", hex.EncodeToString(testSalt[:]), helloWorldContractPath)) + count := runSuccessfulCLICmd(t, fmt.Sprintf("contract invoke --id %s -- inc", strkeyContractID)) + require.Equal(t, "1", count) + + ch := jhttp.NewChannel(test.sorobanRPCURL(), nil) + client := jrpc2.NewClient(ch, nil) + + initialExpirationSeq := getExpirationForLedgerEntry(t, client, expirationKey(t, counterLedgerKey(t, strkeyContractID))) + + // Wait for the counter ledger entry to expire and successfully invoke the `inc` contract function again + // This ensures that the CLI restores the entry (using the RestorePreamble in the simulateTransaction response) + waitForLedgerEntryToExpire(t, client, expirationKey(t, counterLedgerKey(t, strkeyContractID))) + + restoreOutput := runSuccessfulCLICmd( + t, + fmt.Sprintf( + "contract restore --id %s --key COUNTER", + strkeyContractID, + ), + ) + + newExpirationSeq := getExpirationForLedgerEntry(t, client, expirationKey(t, counterLedgerKey(t, strkeyContractID))) + assert.Greater(t, newExpirationSeq, initialExpirationSeq) + assert.Equal(t, fmt.Sprintf("New expiration ledger: %d", newExpirationSeq), restoreOutput) +} + +func getExpirationForLedgerEntry(t *testing.T, client *jrpc2.Client, ledgerKey xdr.LedgerKey) xdr.Uint32 { + keyB64, err := xdr.MarshalBase64(ledgerKey) + 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) + return entry.Expiration.ExpirationLedgerSeq +} + +func expirationKey(t *testing.T, key xdr.LedgerKey) xdr.LedgerKey { + binKey, err := key.MarshalBinary() + assert.NoError(t, err) + return xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeExpiration, + Expiration: &xdr.LedgerKeyExpiration{ + KeyHash: sha256.Sum256(binKey), + }, + } +} + +func counterLedgerKey(t *testing.T, strkeyContractID string) xdr.LedgerKey { contractIDBytes := strkey.MustDecode(strkey.VersionByteContract, strkeyContractID) require.Len(t, contractIDBytes, 32) var contractID [32]byte @@ -96,17 +185,7 @@ func TestCLIRestorePreamble(t *testing.T) { Durability: xdr.ContractDataDurabilityPersistent, }, } - - binKey, err := key.MarshalBinary() - assert.NoError(t, err) - - expiration := xdr.LedgerKeyExpiration{ - KeyHash: sha256.Sum256(binKey), - } - waitForLedgerEntryToExpire(t, client, expiration) - - count = runSuccessfulCLICmd(t, fmt.Sprintf("contract invoke --id %s -- inc", strkeyContractID)) - require.Equal(t, "3", count) + return key } func runSuccessfulCLICmd(t *testing.T, cmd string) string { diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index fa31785d3..8886fa762 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -749,27 +749,14 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { binKey, err := key.MarshalBinary() assert.NoError(t, err) - expiration := xdr.LedgerKeyExpiration{ - KeyHash: sha256.Sum256(binKey), - } expirationKey := xdr.LedgerKey{ - Type: xdr.LedgerEntryTypeExpiration, - Expiration: &expiration, - } - - keyB64, err := xdr.MarshalBase64(expirationKey) - require.NoError(t, err) - getLedgerEntryrequest := methods.GetLedgerEntryRequest{ - Key: keyB64, + Type: xdr.LedgerEntryTypeExpiration, + Expiration: &xdr.LedgerKeyExpiration{ + KeyHash: sha256.Sum256(binKey), + }, } - 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 + initialExpirationSeq := getExpirationForLedgerEntry(t, client, expirationKey) // bump the initial expiration params = preflightTransactionParams(t, client, txnbuild.TransactionParams{ @@ -799,15 +786,11 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { 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 + newExpirationSeq := getExpirationForLedgerEntry(t, client, expirationKey) assert.Greater(t, newExpirationSeq, initialExpirationSeq) // Wait until it expires - waitForLedgerEntryToExpire(t, client, expiration) + waitForLedgerEntryToExpire(t, client, expirationKey) // and restore it params = preflightTransactionParams(t, client, txnbuild.TransactionParams{ @@ -837,7 +820,7 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { sendSuccessfulTransaction(t, client, sourceAccount, tx) // Wait for expiration again and check the pre-restore field when trying to exec the contract again - waitForLedgerEntryToExpire(t, client, expiration) + waitForLedgerEntryToExpire(t, client, expirationKey) simulationResult := simulateTransactionFromTxParams(t, client, invokeIncPresistentEntryParams) require.NotNil(t, simulationResult.RestorePreamble) @@ -871,12 +854,7 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { sendSuccessfulTransaction(t, client, sourceAccount, tx) } -func waitForLedgerEntryToExpire(t *testing.T, client *jrpc2.Client, expiration xdr.LedgerKeyExpiration) { - expirationKey := xdr.LedgerKey{ - Type: xdr.LedgerEntryTypeExpiration, - Expiration: &expiration, - } - +func waitForLedgerEntryToExpire(t *testing.T, client *jrpc2.Client, expirationKey xdr.LedgerKey) { keyB64, err := xdr.MarshalBase64(expirationKey) require.NoError(t, err) getLedgerEntryrequest := methods.GetLedgerEntryRequest{