|
18 | 18 | #include <xdrpp/printer.h> |
19 | 19 |
|
20 | 20 | #include "bucket/BucketManager.h" |
| 21 | +#include "bucket/test/BucketTestUtils.h" |
21 | 22 | #include "crypto/Random.h" |
22 | 23 | #include "crypto/SecretKey.h" |
23 | 24 | #include "herder/Herder.h" |
@@ -11088,3 +11089,99 @@ TEST_CASE_VERSIONS("classic phase bumps sequence of soroban source account", |
11088 | 11089 | -refund, ledgerVersion, true); |
11089 | 11090 | }); |
11090 | 11091 | } |
| 11092 | + |
| 11093 | +#ifdef CAP_0085_EXECUTABLE_REF |
| 11094 | +TEST_CASE("create and invoke external ref contract", "[tx][soroban]") |
| 11095 | +{ |
| 11096 | + VirtualClock clock; |
| 11097 | + auto cfg = getTestConfig(); |
| 11098 | + auto app = createTestApplication<BucketTestUtils::BucketTestApplication>( |
| 11099 | + clock, cfg); |
| 11100 | + SorobanTest test(app); |
| 11101 | + releaseAssert(protocolVersionStartsFrom( |
| 11102 | + test.getLedgerVersion(), EXTERNAL_EXECUTABLE_REF_PROTOCOL_VERSION)); |
| 11103 | + |
| 11104 | + auto wasm = rust_bridge::get_test_wasm_add_i32(); |
| 11105 | + SCBytes wasmBytes(wasm.data.begin(), wasm.data.end()); |
| 11106 | + Hash wasmHash = sha256(wasmBytes); |
| 11107 | + auto wasmKey = contractCodeKey(wasmHash); |
| 11108 | + SorobanResources uploadResources = |
| 11109 | + defaultUploadWasmResourcesWithoutFootprint(wasm, |
| 11110 | + test.getLedgerVersion()); |
| 11111 | + auto uploadTx = makeSorobanWasmUploadTx(test.getApp(), test.getRoot(), wasm, |
| 11112 | + uploadResources, 1000); |
| 11113 | + REQUIRE(isSuccessResult(test.invokeTx(uploadTx))); |
| 11114 | + |
| 11115 | + SCAddress owner = makeContractAddress(sha256("external ref owner")); |
| 11116 | + SCString tag("exec ref"); |
| 11117 | + SCVal tagKey(SCV_EXECUTABLE_TAG); |
| 11118 | + tagKey.executable_tag() = tag; |
| 11119 | + auto refKey = |
| 11120 | + contractDataKey(owner, tagKey, ContractDataDurability::PERSISTENT); |
| 11121 | + |
| 11122 | + // Inject the reference entry (and its TTL) directly into the ledger, which |
| 11123 | + // works thanks to using BucketTestUtils::BucketTestApplication app. |
| 11124 | + // We currently don't have a test Wasm that creates executable references, |
| 11125 | + // if we add one, we should use it instead of injecting the entry directly. |
| 11126 | + { |
| 11127 | + auto ledgerSeq = test.getLCLSeq() + 1; |
| 11128 | + |
| 11129 | + LedgerEntry refEntry; |
| 11130 | + refEntry.lastModifiedLedgerSeq = ledgerSeq; |
| 11131 | + refEntry.data.type(CONTRACT_DATA); |
| 11132 | + auto& cd = refEntry.data.contractData(); |
| 11133 | + cd.contract = owner; |
| 11134 | + cd.key = tagKey; |
| 11135 | + cd.durability = ContractDataDurability::PERSISTENT; |
| 11136 | + cd.val = makeBytesSCVal(wasmHash); |
| 11137 | + |
| 11138 | + LedgerEntry ttlEntry; |
| 11139 | + ttlEntry.lastModifiedLedgerSeq = ledgerSeq; |
| 11140 | + ttlEntry.data.type(TTL); |
| 11141 | + ttlEntry.data.ttl().keyHash = getTTLKey(refKey).ttl().keyHash; |
| 11142 | + ttlEntry.data.ttl().liveUntilLedgerSeq = ledgerSeq + 1'000'000; |
| 11143 | + |
| 11144 | + app->getLedgerManager().setNextLedgerEntryBatchForBucketTesting( |
| 11145 | + {refEntry, ttlEntry}, {}, {}, /*alsoAddActualEntries=*/true); |
| 11146 | + closeLedger(test.getApp()); |
| 11147 | + } |
| 11148 | + |
| 11149 | + auto idPreimage = |
| 11150 | + makeContractIDPreimage(test.getRoot(), sha256("external ref salt")); |
| 11151 | + auto contractID = xdrSha256( |
| 11152 | + makeFullContractIdPreimage(test.getApp().getNetworkID(), idPreimage)); |
| 11153 | + auto contractAddress = makeContractAddress(contractID); |
| 11154 | + auto contractInstanceKey = makeContractInstanceKey(contractAddress); |
| 11155 | + |
| 11156 | + ContractExecutable executable(CONTRACT_EXECUTABLE_EXTERNAL_REF); |
| 11157 | + executable.external_ref().executable_owner = owner; |
| 11158 | + executable.external_ref().tag = tag; |
| 11159 | + |
| 11160 | + SorobanResources createResources{}; |
| 11161 | + createResources.instructions = 2'000'000; |
| 11162 | + createResources.writeBytes = 500; |
| 11163 | + createResources.footprint.readOnly = {refKey, wasmKey}; |
| 11164 | + createResources.footprint.readWrite = {contractInstanceKey}; |
| 11165 | + |
| 11166 | + auto createTx = |
| 11167 | + makeSorobanCreateContractTx(test.getApp(), test.getRoot(), idPreimage, |
| 11168 | + executable, createResources, 1000); |
| 11169 | + REQUIRE(isSuccessResult(test.invokeTx(createTx))); |
| 11170 | + |
| 11171 | + { |
| 11172 | + auto ledgerView = |
| 11173 | + test.getApp().getLedgerManager().copyImmutableLedgerView(); |
| 11174 | + auto le = ledgerView.load(contractInstanceKey); |
| 11175 | + REQUIRE(le); |
| 11176 | + REQUIRE(le.current().data.contractData().val.instance().executable == |
| 11177 | + executable); |
| 11178 | + } |
| 11179 | + TestContract contract(test, contractAddress, |
| 11180 | + {contractInstanceKey, refKey, wasmKey}); |
| 11181 | + auto spec = SorobanInvocationSpec().setInstructions(1'000'000); |
| 11182 | + auto invocation = |
| 11183 | + contract.prepareInvocation("add", {makeI32(3), makeI32(4)}, spec); |
| 11184 | + REQUIRE(invocation.invoke()); |
| 11185 | + REQUIRE(invocation.getReturnValue().i32() == 7); |
| 11186 | +} |
| 11187 | +#endif // CAP_0085_EXECUTABLE_REF |
0 commit comments