Files
rippled/src/test/jtx/impl/xchain_bridge.cpp
2026-04-01 15:46:14 +00:00

473 lines
14 KiB
C++

#include <test/jtx/Env.h>
#include <test/jtx/attester.h>
#include <test/jtx/xchain_bridge.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STBase.h>
#include <xrpl/protocol/STInteger.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/XChainAttestations.h>
#include <xrpl/protocol/jss.h>
namespace xrpl {
namespace test {
namespace jtx {
// use this for creating a bridge for a transaction
Json::Value
bridge(
Account const& lockingChainDoor,
Issue const& lockingChainIssue,
Account const& issuingChainDoor,
Issue const& issuingChainIssue)
{
Json::Value jv;
jv[jss::LockingChainDoor] = lockingChainDoor.human();
jv[jss::LockingChainIssue] = to_json(lockingChainIssue);
jv[jss::IssuingChainDoor] = issuingChainDoor.human();
jv[jss::IssuingChainIssue] = to_json(issuingChainIssue);
return jv;
}
// use this for creating a bridge for a rpc query
Json::Value
bridge_rpc(
Account const& lockingChainDoor,
Issue const& lockingChainIssue,
Account const& issuingChainDoor,
Issue const& issuingChainIssue)
{
Json::Value jv;
jv[jss::LockingChainDoor] = lockingChainDoor.human();
jv[jss::LockingChainIssue] = to_json(lockingChainIssue);
jv[jss::IssuingChainDoor] = issuingChainDoor.human();
jv[jss::IssuingChainIssue] = to_json(issuingChainIssue);
return jv;
}
Json::Value
bridge_create(
Account const& acc,
Json::Value const& bridge,
STAmount const& reward,
std::optional<STAmount> const& minAccountCreate)
{
Json::Value jv;
jv[jss::Account] = acc.human();
jv[sfXChainBridge.getJsonName()] = bridge;
jv[sfSignatureReward.getJsonName()] = reward.getJson(JsonOptions::none);
if (minAccountCreate)
jv[sfMinAccountCreateAmount.getJsonName()] = minAccountCreate->getJson(JsonOptions::none);
jv[jss::TransactionType] = jss::XChainCreateBridge;
return jv;
}
Json::Value
bridge_modify(
Account const& acc,
Json::Value const& bridge,
std::optional<STAmount> const& reward,
std::optional<STAmount> const& minAccountCreate)
{
Json::Value jv;
jv[jss::Account] = acc.human();
jv[sfXChainBridge.getJsonName()] = bridge;
if (reward)
jv[sfSignatureReward.getJsonName()] = reward->getJson(JsonOptions::none);
if (minAccountCreate)
jv[sfMinAccountCreateAmount.getJsonName()] = minAccountCreate->getJson(JsonOptions::none);
jv[jss::TransactionType] = jss::XChainModifyBridge;
return jv;
}
Json::Value
xchain_create_claim_id(
Account const& acc,
Json::Value const& bridge,
STAmount const& reward,
Account const& otherChainSource)
{
Json::Value jv;
jv[jss::Account] = acc.human();
jv[sfXChainBridge.getJsonName()] = bridge;
jv[sfSignatureReward.getJsonName()] = reward.getJson(JsonOptions::none);
jv[sfOtherChainSource.getJsonName()] = otherChainSource.human();
jv[jss::TransactionType] = jss::XChainCreateClaimID;
return jv;
}
Json::Value
xchain_commit(
Account const& acc,
Json::Value const& bridge,
std::uint32_t claimID,
AnyAmount const& amt,
std::optional<Account> const& dst)
{
Json::Value jv;
jv[jss::Account] = acc.human();
jv[sfXChainBridge.getJsonName()] = bridge;
jv[sfXChainClaimID.getJsonName()] = claimID;
jv[jss::Amount] = amt.value.getJson(JsonOptions::none);
if (dst)
jv[sfOtherChainDestination.getJsonName()] = dst->human();
jv[jss::TransactionType] = jss::XChainCommit;
return jv;
}
Json::Value
xchain_claim(
Account const& acc,
Json::Value const& bridge,
std::uint32_t claimID,
AnyAmount const& amt,
Account const& dst)
{
Json::Value jv;
jv[sfAccount.getJsonName()] = acc.human();
jv[sfXChainBridge.getJsonName()] = bridge;
jv[sfXChainClaimID.getJsonName()] = claimID;
jv[sfDestination.getJsonName()] = dst.human();
jv[sfAmount.getJsonName()] = amt.value.getJson(JsonOptions::none);
jv[jss::TransactionType] = jss::XChainClaim;
return jv;
}
Json::Value
sidechain_xchain_account_create(
Account const& acc,
Json::Value const& bridge,
Account const& dst,
AnyAmount const& amt,
AnyAmount const& reward)
{
Json::Value jv;
jv[sfAccount.getJsonName()] = acc.human();
jv[sfXChainBridge.getJsonName()] = bridge;
jv[sfDestination.getJsonName()] = dst.human();
jv[sfAmount.getJsonName()] = amt.value.getJson(JsonOptions::none);
jv[sfSignatureReward.getJsonName()] = reward.value.getJson(JsonOptions::none);
jv[jss::TransactionType] = jss::XChainAccountCreateCommit;
return jv;
}
Json::Value
claim_attestation(
jtx::Account const& submittingAccount,
Json::Value const& jvBridge,
jtx::Account const& sendingAccount,
jtx::AnyAmount const& sendingAmount,
jtx::Account const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t claimID,
std::optional<jtx::Account> const& dst,
jtx::signer const& signer)
{
STXChainBridge const stBridge(jvBridge);
auto const& pk = signer.account.pk();
auto const& sk = signer.account.sk();
auto const sig = sign_claim_attestation(
pk,
sk,
stBridge,
sendingAccount,
sendingAmount.value,
rewardAccount,
wasLockingChainSend,
claimID,
dst);
Json::Value result;
result[sfAccount.getJsonName()] = submittingAccount.human();
result[sfXChainBridge.getJsonName()] = jvBridge;
result[sfAttestationSignerAccount.getJsonName()] = signer.account.human();
result[sfPublicKey.getJsonName()] = strHex(pk.slice());
result[sfSignature.getJsonName()] = strHex(sig);
result[sfOtherChainSource.getJsonName()] = toBase58(sendingAccount);
result[sfAmount.getJsonName()] = sendingAmount.value.getJson(JsonOptions::none);
result[sfAttestationRewardAccount.getJsonName()] = toBase58(rewardAccount);
result[sfWasLockingChainSend.getJsonName()] = wasLockingChainSend ? 1 : 0;
result[sfXChainClaimID.getJsonName()] = STUInt64{claimID}.getJson(JsonOptions::none);
if (dst)
result[sfDestination.getJsonName()] = toBase58(*dst);
result[jss::TransactionType] = jss::XChainAddClaimAttestation;
return result;
}
Json::Value
create_account_attestation(
jtx::Account const& submittingAccount,
Json::Value const& jvBridge,
jtx::Account const& sendingAccount,
jtx::AnyAmount const& sendingAmount,
jtx::AnyAmount const& rewardAmount,
jtx::Account const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t createCount,
jtx::Account const& dst,
jtx::signer const& signer)
{
STXChainBridge const stBridge(jvBridge);
auto const& pk = signer.account.pk();
auto const& sk = signer.account.sk();
auto const sig = jtx::sign_create_account_attestation(
pk,
sk,
stBridge,
sendingAccount,
sendingAmount.value,
rewardAmount.value,
rewardAccount,
wasLockingChainSend,
createCount,
dst);
Json::Value result;
result[sfAccount.getJsonName()] = submittingAccount.human();
result[sfXChainBridge.getJsonName()] = jvBridge;
result[sfAttestationSignerAccount.getJsonName()] = signer.account.human();
result[sfPublicKey.getJsonName()] = strHex(pk.slice());
result[sfSignature.getJsonName()] = strHex(sig);
result[sfOtherChainSource.getJsonName()] = toBase58(sendingAccount);
result[sfAmount.getJsonName()] = sendingAmount.value.getJson(JsonOptions::none);
result[sfAttestationRewardAccount.getJsonName()] = toBase58(rewardAccount);
result[sfWasLockingChainSend.getJsonName()] = wasLockingChainSend ? 1 : 0;
result[sfXChainAccountCreateCount.getJsonName()] =
STUInt64{createCount}.getJson(JsonOptions::none);
result[sfDestination.getJsonName()] = toBase58(dst);
result[sfSignatureReward.getJsonName()] = rewardAmount.value.getJson(JsonOptions::none);
result[jss::TransactionType] = jss::XChainAddAccountCreateAttestation;
return result;
}
JValueVec
claim_attestations(
jtx::Account const& submittingAccount,
Json::Value const& jvBridge,
jtx::Account const& sendingAccount,
jtx::AnyAmount const& sendingAmount,
std::vector<jtx::Account> const& rewardAccounts,
bool wasLockingChainSend,
std::uint64_t claimID,
std::optional<jtx::Account> const& dst,
std::vector<jtx::signer> const& signers,
std::size_t const numAtts,
std::size_t const fromIdx)
{
assert(fromIdx + numAtts <= rewardAccounts.size());
assert(fromIdx + numAtts <= signers.size());
JValueVec vec;
vec.reserve(numAtts);
for (auto i = fromIdx; i < fromIdx + numAtts; ++i)
{
vec.emplace_back(claim_attestation(
submittingAccount,
jvBridge,
sendingAccount,
sendingAmount,
rewardAccounts[i],
wasLockingChainSend,
claimID,
dst,
signers[i]));
}
return vec;
}
JValueVec
create_account_attestations(
jtx::Account const& submittingAccount,
Json::Value const& jvBridge,
jtx::Account const& sendingAccount,
jtx::AnyAmount const& sendingAmount,
jtx::AnyAmount const& rewardAmount,
std::vector<jtx::Account> const& rewardAccounts,
bool wasLockingChainSend,
std::uint64_t createCount,
jtx::Account const& dst,
std::vector<jtx::signer> const& signers,
std::size_t const numAtts,
std::size_t const fromIdx)
{
assert(fromIdx + numAtts <= rewardAccounts.size());
assert(fromIdx + numAtts <= signers.size());
JValueVec vec;
vec.reserve(numAtts);
for (auto i = fromIdx; i < fromIdx + numAtts; ++i)
{
vec.emplace_back(create_account_attestation(
submittingAccount,
jvBridge,
sendingAccount,
sendingAmount,
rewardAmount,
rewardAccounts[i],
wasLockingChainSend,
createCount,
dst,
signers[i]));
}
return vec;
}
XChainBridgeObjects::XChainBridgeObjects()
: mcDoor("mcDoor")
, mcAlice("mcAlice")
, mcBob("mcBob")
, mcCarol("mcCarol")
, mcGw("mcGw")
, scDoor("scDoor")
, scAlice("scAlice")
, scBob("scBob")
, scCarol("scCarol")
, scGw("scGw")
, scAttester("scAttester")
, scReward("scReward")
, mcuDoor("mcuDoor")
, mcuAlice("mcuAlice")
, mcuBob("mcuBob")
, mcuCarol("mcuCarol")
, mcuGw("mcuGw")
, scuDoor("scuDoor")
, scuAlice("scuAlice")
, scuBob("scuBob")
, scuCarol("scuCarol")
, scuGw("scuGw")
, mcUSD(mcGw["USD"])
, scUSD(scGw["USD"])
, jvXRPBridgeRPC(bridge_rpc(mcDoor, xrpIssue(), Account::master, xrpIssue()))
, jvb(bridge(mcDoor, xrpIssue(), Account::master, xrpIssue()))
, jvub(bridge(mcuDoor, xrpIssue(), Account::master, xrpIssue()))
, features(testable_amendments() | FeatureBitset{featureXChainBridge})
, signers([] {
constexpr int numSigners = UT_XCHAIN_DEFAULT_NUM_SIGNERS;
std::vector<signer> result;
result.reserve(numSigners);
for (int i = 0; i < numSigners; ++i)
{
using namespace std::literals;
auto const a = Account(
"signer_"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1);
result.emplace_back(a);
}
return result;
}())
, alt_signers([] {
constexpr int numSigners = UT_XCHAIN_DEFAULT_NUM_SIGNERS;
std::vector<signer> result;
result.reserve(numSigners);
for (int i = 0; i < numSigners; ++i)
{
using namespace std::literals;
auto const a = Account(
"alt_signer_"s + std::to_string(i),
(i % 2) ? KeyType::ed25519 : KeyType::secp256k1);
result.emplace_back(a);
}
return result;
}())
, payee([&] {
std::vector<Account> r;
r.reserve(signers.size());
for (int i = 0, e = signers.size(); i != e; ++i)
{
r.push_back(scReward);
}
return r;
}())
, payees([&] {
std::vector<Account> r;
r.reserve(signers.size());
for (int i = 0, e = signers.size(); i != e; ++i)
{
using namespace std::literals;
auto const a = Account("reward_"s + std::to_string(i));
r.push_back(a);
}
return r;
}())
, reward(XRP(1))
, split_reward_quorum(divide(reward, STAmount(UT_XCHAIN_DEFAULT_QUORUM), reward.issue()))
, split_reward_everyone(divide(reward, STAmount(UT_XCHAIN_DEFAULT_NUM_SIGNERS), reward.issue()))
, tiny_reward(drops(37))
, tiny_reward_split(
(divide(tiny_reward, STAmount(UT_XCHAIN_DEFAULT_QUORUM), tiny_reward.issue())))
, tiny_reward_remainder(
tiny_reward -
multiply(tiny_reward_split, STAmount(UT_XCHAIN_DEFAULT_QUORUM), tiny_reward.issue()))
, one_xrp(XRP(1))
, xrp_dust(divide(one_xrp, STAmount(10000), one_xrp.issue()))
{
}
void
XChainBridgeObjects::createMcBridgeObjects(Env& mcEnv)
{
STAmount const xrp_funds{XRP(10000)};
mcEnv.fund(xrp_funds, mcDoor, mcAlice, mcBob, mcCarol, mcGw);
// Signer's list must match the attestation signers
mcEnv(jtx::signers(mcDoor, signers.size(), signers));
// create XRP bridges in both direction
auto const reward = XRP(1);
STAmount const minCreate = XRP(20);
mcEnv(bridge_create(mcDoor, jvb, reward, minCreate));
mcEnv.close();
}
void
XChainBridgeObjects::createScBridgeObjects(Env& scEnv)
{
STAmount const xrp_funds{XRP(10000)};
scEnv.fund(xrp_funds, scDoor, scAlice, scBob, scCarol, scGw, scAttester, scReward);
// Signer's list must match the attestation signers
scEnv(jtx::signers(Account::master, signers.size(), signers));
// create XRP bridges in both direction
auto const reward = XRP(1);
STAmount const minCreate = XRP(20);
scEnv(bridge_create(Account::master, jvb, reward, minCreate));
scEnv.close();
}
void
XChainBridgeObjects::createBridgeObjects(Env& mcEnv, Env& scEnv)
{
createMcBridgeObjects(mcEnv);
createScBridgeObjects(scEnv);
}
} // namespace jtx
} // namespace test
} // namespace xrpl