From 3900289bf763fb2e6043a15f859bfac180ecb7db Mon Sep 17 00:00:00 2001 From: Siddharth Suresh Date: Thu, 27 Jul 2023 16:30:43 -0700 Subject: [PATCH 1/2] Footprint LedgerKey validation tests --- .../test/InvokeHostFunctionTests.cpp | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/transactions/test/InvokeHostFunctionTests.cpp b/src/transactions/test/InvokeHostFunctionTests.cpp index 5d9988e619..0032492534 100644 --- a/src/transactions/test/InvokeHostFunctionTests.cpp +++ b/src/transactions/test/InvokeHostFunctionTests.cpp @@ -298,6 +298,24 @@ TEST_CASE("basic contract invocation", "[tx][soroban]") auto contractKeys = deployContractWithSourceAccount(*app, addI32Wasm); auto const& contractID = contractKeys[0].contractData().contract; + auto isValid = [&](SorobanResources const& resources, + SCAddress const& address, SCSymbol const& functionName, + std::vector const& args) -> bool { + Operation op; + op.body.type(INVOKE_HOST_FUNCTION); + auto& ihf = op.body.invokeHostFunctionOp().hostFunction; + ihf.type(HOST_FUNCTION_TYPE_INVOKE_CONTRACT); + ihf.invokeContract().contractAddress = address; + ihf.invokeContract().functionName = functionName; + ihf.invokeContract().args.assign(args.begin(), args.end()); + + auto tx = sorobanTransactionFrameFromOps( + app->getNetworkID(), root, {op}, {}, resources, 100'000, 1200); + + LedgerTxn ltx(app->getLedgerTxnRoot()); + return tx->checkValid(*app, ltx, 0, 0, 0); + }; + auto call = [&](SorobanResources const& resources, SCAddress const& address, SCSymbol const& functionName, std::vector const& args, bool success) { @@ -460,6 +478,81 @@ TEST_CASE("basic contract invocation", "[tx][soroban]") resources.readBytes = 100; call(resources, contractID, scFunc, {sc7, sc16}, false); } + + SECTION("invalid footprint keys") + { + auto invalidFootprint = [&](xdr::xvector& + footprint) { + auto acc = root.create( + "acc", app->getLedgerManager().getLastMinBalance(1)); + SECTION("valid") + { + // add a valid trustline to the footprint to make sure the + // initial tx is valid. + footprint.emplace_back( + trustlineKey(root.getPublicKey(), makeAsset(acc, "USD"))); + REQUIRE(isValid(resources, contractID, scFunc, {sc7, sc16})); + } + SECTION("native asset trustline") + { + footprint.emplace_back( + trustlineKey(root.getPublicKey(), makeNativeAsset())); + REQUIRE(!isValid(resources, contractID, scFunc, {sc7, sc16})); + } + SECTION("issuer trustline") + { + footprint.emplace_back( + trustlineKey(root.getPublicKey(), makeAsset(root, "USD"))); + REQUIRE(!isValid(resources, contractID, scFunc, {sc7, sc16})); + } + auto invalidAssets = testutil::getInvalidAssets(root); + for (size_t i = 0; i < invalidAssets.size(); ++i) + { + auto key = trustlineKey(acc.getPublicKey(), invalidAssets[i]); + SECTION("invalid asset " + std::to_string(i)) + { + footprint.emplace_back(key); + REQUIRE( + !isValid(resources, contractID, scFunc, {sc7, sc16})); + } + } + SECTION("offer") + { + footprint.emplace_back(offerKey(root, 1)); + REQUIRE(!isValid(resources, contractID, scFunc, {sc7, sc16})); + } + SECTION("data") + { + footprint.emplace_back(dataKey(root, "name")); + REQUIRE(!isValid(resources, contractID, scFunc, {sc7, sc16})); + } + SECTION("claimable balance") + { + footprint.emplace_back( + claimableBalanceKey(ClaimableBalanceID{})); + REQUIRE(!isValid(resources, contractID, scFunc, {sc7, sc16})); + } + SECTION("liquidity pool") + { + footprint.emplace_back(liquidityPoolKey(PoolID{})); + REQUIRE(!isValid(resources, contractID, scFunc, {sc7, sc16})); + } + SECTION("config setting") + { + footprint.emplace_back(configSettingKey(ConfigSettingID{})); + REQUIRE(!isValid(resources, contractID, scFunc, {sc7, sc16})); + } + }; + + SECTION("readOnly") + { + invalidFootprint(resources.footprint.readOnly); + } + SECTION("readWrite") + { + invalidFootprint(resources.footprint.readWrite); + } + } } TEST_CASE("contract storage", "[tx][soroban]") From db589fb9aadcf1be5fbfd130d8d82cd85befdf5a Mon Sep 17 00:00:00 2001 From: Siddharth Suresh Date: Thu, 27 Jul 2023 17:29:47 -0700 Subject: [PATCH 2/2] Additional footprint tests --- .../test/InvokeHostFunctionTests.cpp | 56 +++++++---- .../InvokeHostFunctionTests.json | 95 +++++++++++++++++++ 2 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 test-tx-meta-baseline-next/InvokeHostFunctionTests.json diff --git a/src/transactions/test/InvokeHostFunctionTests.cpp b/src/transactions/test/InvokeHostFunctionTests.cpp index 0032492534..b022b78469 100644 --- a/src/transactions/test/InvokeHostFunctionTests.cpp +++ b/src/transactions/test/InvokeHostFunctionTests.cpp @@ -908,22 +908,6 @@ TEST_CASE("contract storage", "[tx][soroban]") put("key1", 0, ContractDataDurability::PERSISTENT); put("key2", 21, ContractDataDurability::PERSISTENT); - // Failure: contract data isn't in footprint - putWithFootprint("key1", 88, contractKeys, {}, 1000, false, - ContractDataDurability::PERSISTENT); - delWithFootprint("key1", contractKeys, {}, false, - ContractDataDurability::PERSISTENT); - - // Failure: contract data is read only - auto readOnlyFootprint = contractKeys; - readOnlyFootprint.push_back( - contractDataKey(contractID, makeSymbolSCVal("key2"), - ContractDataDurability::PERSISTENT, DATA_ENTRY)); - putWithFootprint("key2", 888888, readOnlyFootprint, {}, 1000, false, - ContractDataDurability::PERSISTENT); - delWithFootprint("key2", readOnlyFootprint, {}, false, - ContractDataDurability::PERSISTENT); - // Failure: insufficient write bytes putWithFootprint( "key2", 88888, contractKeys, @@ -1199,6 +1183,46 @@ TEST_CASE("contract storage", "[tx][soroban]") ContractDataDurability::PERSISTENT) == maxExpiration); } + SECTION("footprint tests") + { + put("key1", 0, ContractDataDurability::PERSISTENT); + put("key2", 21, ContractDataDurability::PERSISTENT); + SECTION("unused readWrite key") + { + auto acc = root.create( + "acc", app->getLedgerManager().getLastMinBalance(1)); + + REQUIRE(doesAccountExist(*app, acc)); + + putWithFootprint( + "key1", 0, contractKeys, + {contractDataKey(contractID, makeSymbolSCVal("key1"), + ContractDataDurability::PERSISTENT, + DATA_ENTRY), + accountKey(acc)}, + 1000, true, ContractDataDurability::PERSISTENT); + // make sure account still exists and hasn't change + REQUIRE(doesAccountExist(*app, acc)); + } + SECTION("incorrect footprint") + { + // Failure: contract data isn't in footprint + putWithFootprint("key1", 88, contractKeys, {}, 1000, false, + ContractDataDurability::PERSISTENT); + delWithFootprint("key1", contractKeys, {}, false, + ContractDataDurability::PERSISTENT); + + // Failure: contract data is read only + auto readOnlyFootprint = contractKeys; + readOnlyFootprint.push_back(contractDataKey( + contractID, makeSymbolSCVal("key2"), + ContractDataDurability::PERSISTENT, DATA_ENTRY)); + putWithFootprint("key2", 888888, readOnlyFootprint, {}, 1000, false, + ContractDataDurability::PERSISTENT); + delWithFootprint("key2", readOnlyFootprint, {}, false, + ContractDataDurability::PERSISTENT); + } + } } TEST_CASE("failed invocation with diagnostics", "[tx][soroban]") diff --git a/test-tx-meta-baseline-next/InvokeHostFunctionTests.json b/test-tx-meta-baseline-next/InvokeHostFunctionTests.json new file mode 100644 index 0000000000..e9aa48bc80 --- /dev/null +++ b/test-tx-meta-baseline-next/InvokeHostFunctionTests.json @@ -0,0 +1,95 @@ + +{ + "!cfg protocol version" : 20, + "!rng seed" : 12345, + "!test all versions" : true, + "!versions to test" : + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "basic contract invocation|invalid footprint keys|readOnly" : + [ + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=" + ], + "basic contract invocation|invalid footprint keys|readWrite" : + [ + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=", + "3l7t9opHMbU=" + ], + "contract storage|footprint tests|unused readWrite key" : [ "PJ5bN6hAeFA=" ] +}