diff --git a/common.mk b/common.mk index 0f9c96b3e..e8aec6522 100644 --- a/common.mk +++ b/common.mk @@ -45,4 +45,8 @@ endif # ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION if CAP_0083 AM_CPPFLAGS += -DCAP_0083 -endif \ No newline at end of file +endif + +if CAP_0084 +AM_CPPFLAGS += -DCAP_0084 +endif diff --git a/configure.ac b/configure.ac index 76cc59523..79e213170 100644 --- a/configure.ac +++ b/configure.ac @@ -564,6 +564,9 @@ AM_CONDITIONAL(ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION, AM_CONDITIONAL(CAP_0083, [test x$enable_next_protocol_version_unsafe_for_production = xyes]) +AM_CONDITIONAL(CAP_0084, + [test x$enable_next_protocol_version_unsafe_for_production = xyes]) + AC_PATH_PROG(CARGO, cargo) if test x"$CARGO" = x; then AC_MSG_ERROR([cannot find cargo, needed for rust code]) diff --git a/src/Makefile.am b/src/Makefile.am index e33128a57..13ba0ef36 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -100,6 +100,9 @@ XDR_FEATURE_FLAGS = if CAP_0083 XDR_FEATURE_FLAGS += -DCAP_0083 endif +if CAP_0084 +XDR_FEATURE_FLAGS += -DCAP_0084 +endif SUFFIXES = .x .h .rs .x.h: diff --git a/src/ledger/LedgerHashUtils.h b/src/ledger/LedgerHashUtils.h index b9748d593..41a944212 100644 --- a/src/ledger/LedgerHashUtils.h +++ b/src/ledger/LedgerHashUtils.h @@ -128,6 +128,12 @@ template <> class hash stellar::hashMix( res, std::hash()(addr.liquidityPoolId())); break; +#ifdef CAP_0084 + case stellar::SC_ADDRESS_TYPE_MUXED_CONTRACT: + stellar::hashMix( + res, stellar::shortHash::xdrComputeHash(addr.muxedContract())); + break; +#endif } return res; } diff --git a/src/protocol-curr/xdr b/src/protocol-curr/xdr index 68fa1ac55..2769fed74 160000 --- a/src/protocol-curr/xdr +++ b/src/protocol-curr/xdr @@ -1 +1 @@ -Subproject commit 68fa1ac55692f68ad2a2ca549d0a283273554439 +Subproject commit 2769fed744c4ec5a2cf6581addfc1704abfcba7c diff --git a/src/rust/soroban/p27 b/src/rust/soroban/p27 index b03d2563f..18393ad6c 160000 --- a/src/rust/soroban/p27 +++ b/src/rust/soroban/p27 @@ -1 +1 @@ -Subproject commit b03d2563f3a08d51095a19bdbb6d90364b8ce04a +Subproject commit 18393ad6c13ad854f77a29e9e2903a468c1f0514 diff --git a/src/rust/src/dep-trees/p27-expect.txt b/src/rust/src/dep-trees/p27-expect.txt index 7ca586c02..758ca7c78 100644 --- a/src/rust/src/dep-trees/p27-expect.txt +++ b/src/rust/src/dep-trees/p27-expect.txt @@ -259,7 +259,7 @@ soroban-env-host v27.0.0 (src/rust/soroban/p27/soroban-env-host) │ │ │ ├── itoa v1.0.6 │ │ │ ├── ryu v1.0.13 │ │ │ └── serde v1.0.192 (*) -│ │ ├── stellar-xdr v27.0.0 +│ │ ├── stellar-xdr v27.0.0 (https://github.com/sisuresh/rs-stellar-xdr?rev=bd30d420ce6f63ccd9360e371830384cb84d1c04#bd30d420) │ │ │ ├── escape-bytes v0.1.1 │ │ │ ├── ethnum v1.5.3 │ │ │ ├── hex v0.4.3 @@ -286,7 +286,7 @@ soroban-env-host v27.0.0 (src/rust/soroban/p27/soroban-env-host) │ │ └── wasmparser-nostd v0.100.2 │ │ └── indexmap-nostd v0.4.0 │ ├── static_assertions v1.1.0 -│ ├── stellar-xdr v27.0.0 +│ ├── stellar-xdr v27.0.0 (https://github.com/sisuresh/rs-stellar-xdr?rev=bd30d420ce6f63ccd9360e371830384cb84d1c04#bd30d420) │ │ ├── base64 v0.22.1 │ │ ├── escape-bytes v0.1.1 │ │ ├── ethnum v1.5.3 @@ -301,7 +301,10 @@ soroban-env-host v27.0.0 (src/rust/soroban/p27/soroban-env-host) │ │ └── hashbrown v0.14.1 │ └── semver v1.0.17 │ [build-dependencies] -│ └── crate-git-revision v0.0.6 (*) +│ └── crate-git-revision v0.0.9 +│ ├── serde v1.0.192 (*) +│ ├── serde_derive v1.0.192 (proc-macro) (*) +│ └── serde_json v1.0.108 (*) ├── soroban-wasmi v0.31.1-soroban.20.0.1 (https://github.com/stellar/wasmi?rev=0ed3f3dee30dc41ebe21972399e0a73a41944aa0#0ed3f3de) (*) ├── static_assertions v1.1.0 ├── stellar-strkey v0.0.13 (*) diff --git a/src/transactions/test/InvokeHostFunctionTests.cpp b/src/transactions/test/InvokeHostFunctionTests.cpp index 13166c0c8..fe0a02033 100644 --- a/src/transactions/test/InvokeHostFunctionTests.cpp +++ b/src/transactions/test/InvokeHostFunctionTests.cpp @@ -600,7 +600,10 @@ TEST_CASE("Stellar asset contract transfer with CAP-67 address types", SorobanTest test(cfg); auto& root = test.getRoot(); - auto a1 = root.create("a1", 1'000'000'000); + // a1 makes several native transfers within a single run (100M + 300M + + // 400M for the muxed-contract case), so it needs enough balance to stay + // above its account reserve after all of them. + auto a1 = root.create("a1", 2'000'000'000); auto a2 = root.create("a2", 1'000'000'000); Asset asset = makeAsset(root.getSecretKey(), "USDC"); a1.changeTrust(asset, 2'000'000'000); @@ -685,6 +688,25 @@ TEST_CASE("Stellar asset contract transfer with CAP-67 address types", a1, makeClaimableBalanceAddress(ClaimableBalanceID()), 1)); REQUIRE(client.lastEvent() == std::nullopt); } +#ifdef CAP_0084 + { + INFO("transfer to muxed contract (CAP-0084)"); + // The destination is the SAC-transfer contract wrapped in a muxed + // contract address; the SAC de-muxes to the underlying contract for + // the balance and surfaces the id via the `to_muxed_id` event. + REQUIRE(client.transfer( + a1, + makeMuxedContractAddress( + transferContract.getAddress().contractId(), + 987'654'321'987'654'321ULL), + 400'000'000)); + REQUIRE(*client.lastEvent() == + client.makeTransferEvent(a1Address, + transferContract.getAddress(), + 400'000'000, + 987'654'321'987'654'321ULL)); + } +#endif }; SECTION("native asset") diff --git a/src/transactions/test/SorobanTxTestUtils.cpp b/src/transactions/test/SorobanTxTestUtils.cpp index 461559386..7f6e891ab 100644 --- a/src/transactions/test/SorobanTxTestUtils.cpp +++ b/src/transactions/test/SorobanTxTestUtils.cpp @@ -66,6 +66,17 @@ makeMuxedAccountAddress(AccountID const& accountID, uint64_t id) return addr; } +#ifdef CAP_0084 +SCAddress +makeMuxedContractAddress(Hash const& contractId, uint64_t id) +{ + SCAddress addr(SC_ADDRESS_TYPE_MUXED_CONTRACT); + addr.muxedContract().contractId = contractId; + addr.muxedContract().id = id; + return addr; +} +#endif + SCVal makeI32(int32_t i32) { @@ -1729,11 +1740,7 @@ AssetContractTestClient::transferInvocation(TestAccount& fromAcc, SCVal toVal(SCV_ADDRESS); toAddr = maybeMuxedToAddr; - if (maybeMuxedToAddr.type() != SCAddressType::SC_ADDRESS_TYPE_MUXED_ACCOUNT) - { - toVal.address() = maybeMuxedToAddr; - } - else + if (maybeMuxedToAddr.type() == SCAddressType::SC_ADDRESS_TYPE_MUXED_ACCOUNT) { PublicKey pk; pk.ed25519() = maybeMuxedToAddr.muxedAccount().ed25519; @@ -1741,6 +1748,21 @@ AssetContractTestClient::transferInvocation(TestAccount& fromAcc, makeMuxedAccountAddress(pk, maybeMuxedToAddr.muxedAccount().id); toAddr = makeAccountAddress(pk); } +#ifdef CAP_0084 + else if (maybeMuxedToAddr.type() == + SCAddressType::SC_ADDRESS_TYPE_MUXED_CONTRACT) + { + // The muxed contract address is passed to the SAC verbatim; balances + // are keyed on the underlying (de-muxed) contract. + toVal.address() = maybeMuxedToAddr; + toAddr = + makeContractAddress(maybeMuxedToAddr.muxedContract().contractId); + } +#endif + else + { + toVal.address() = maybeMuxedToAddr; + } LedgerKey fromBalanceKey = makeBalanceKey(fromAcc.getPublicKey()); LedgerKey toBalanceKey = makeBalanceKey(toAddr); diff --git a/src/transactions/test/SorobanTxTestUtils.h b/src/transactions/test/SorobanTxTestUtils.h index b61d2d404..bca8ddd20 100644 --- a/src/transactions/test/SorobanTxTestUtils.h +++ b/src/transactions/test/SorobanTxTestUtils.h @@ -19,6 +19,9 @@ namespace txtest SCAddress makeContractAddress(Hash const& hash); SCAddress makeMuxedAccountAddress(AccountID const& accountID, uint64_t id); +#ifdef CAP_0084 +SCAddress makeMuxedContractAddress(Hash const& contractId, uint64_t id); +#endif SCVal makeI32(int32_t i32); SCVal makeI128(uint64_t u64); SCSymbol makeSymbol(std::string const& str); diff --git a/src/util/XDRCereal.cpp b/src/util/XDRCereal.cpp index 596831f71..2f93f9e6d 100644 --- a/src/util/XDRCereal.cpp +++ b/src/util/XDRCereal.cpp @@ -59,6 +59,19 @@ cereal_override(cereal::JSONOutputArchive& ar, stellar::SCAddress const& addr, addr.liquidityPoolId().data(), addr.liquidityPoolId().size())), field); return; +#ifdef CAP_0084 + case stellar::SC_ADDRESS_TYPE_MUXED_CONTRACT: + ar.setNextName(field); + ar.startNode(); + xdr::archive(ar, addr.muxedContract().id, "id"); + xdr::archive(ar, + stellar::strKey::toStrKey(stellar::strKey::STRKEY_CONTRACT, + addr.muxedContract().contractId) + .value, + "contractId"); + ar.finishNode(); + return; +#endif default: // Unknown address type - serialize as "Unknown(type_id)" xdr::archive(ar,