mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 11:45:53 +00:00
feat: Account permission support (#2145)
Fixes: #1975. Also switch to libxrpl 2.5.0-b1. Fixes: #2139, #2140.
This commit is contained in:
@@ -31,7 +31,7 @@ class Clio(ConanFile):
|
|||||||
'protobuf/3.21.9',
|
'protobuf/3.21.9',
|
||||||
'grpc/1.50.1',
|
'grpc/1.50.1',
|
||||||
'openssl/1.1.1v',
|
'openssl/1.1.1v',
|
||||||
'xrpl/2.4.0',
|
'xrpl/2.5.0-b1',
|
||||||
'zlib/1.3.1',
|
'zlib/1.3.1',
|
||||||
'libbacktrace/cci.20210118'
|
'libbacktrace/cci.20210118'
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -137,6 +137,8 @@ struct Amendments {
|
|||||||
REGISTER(fixInvalidTxFlags);
|
REGISTER(fixInvalidTxFlags);
|
||||||
REGISTER(fixFrozenLPTokenTransfer);
|
REGISTER(fixFrozenLPTokenTransfer);
|
||||||
REGISTER(DeepFreeze);
|
REGISTER(DeepFreeze);
|
||||||
|
REGISTER(PermissionDelegation);
|
||||||
|
REGISTER(fixPayChanCancelAfter);
|
||||||
|
|
||||||
// Obsolete but supported by libxrpl
|
// Obsolete but supported by libxrpl
|
||||||
REGISTER(CryptoConditionsSuite);
|
REGISTER(CryptoConditionsSuite);
|
||||||
|
|||||||
@@ -185,6 +185,13 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
|
|||||||
);
|
);
|
||||||
auto const seq = input.permissionedDomain->at(JS(seq)).as_int64();
|
auto const seq = input.permissionedDomain->at(JS(seq)).as_int64();
|
||||||
key = ripple::keylet::permissionedDomain(*account, seq).key;
|
key = ripple::keylet::permissionedDomain(*account, seq).key;
|
||||||
|
} else if (input.delegate) {
|
||||||
|
auto const account =
|
||||||
|
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(input.delegate->at(JS(account))));
|
||||||
|
auto const authorize =
|
||||||
|
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(input.delegate->at(JS(authorize)))
|
||||||
|
);
|
||||||
|
key = ripple::keylet::delegate(*account, *authorize).key;
|
||||||
} else {
|
} else {
|
||||||
// Must specify 1 of the following fields to indicate what type
|
// Must specify 1 of the following fields to indicate what type
|
||||||
if (ctx.apiVersion == 1)
|
if (ctx.apiVersion == 1)
|
||||||
@@ -319,7 +326,8 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
|
|||||||
{JS(oracle), ripple::ltORACLE},
|
{JS(oracle), ripple::ltORACLE},
|
||||||
{JS(credential), ripple::ltCREDENTIAL},
|
{JS(credential), ripple::ltCREDENTIAL},
|
||||||
{JS(mptoken), ripple::ltMPTOKEN},
|
{JS(mptoken), ripple::ltMPTOKEN},
|
||||||
{JS(permissioned_domain), ripple::ltPERMISSIONED_DOMAIN}
|
{JS(permissioned_domain), ripple::ltPERMISSIONED_DOMAIN},
|
||||||
|
{JS(delegate), ripple::ltDELEGATE}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto const parseBridgeFromJson = [](boost::json::value const& bridgeJson) {
|
auto const parseBridgeFromJson = [](boost::json::value const& bridgeJson) {
|
||||||
@@ -408,6 +416,8 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
|
|||||||
input.mptoken = jv.at(JS(mptoken)).as_object();
|
input.mptoken = jv.at(JS(mptoken)).as_object();
|
||||||
} else if (jsonObject.contains(JS(permissioned_domain))) {
|
} else if (jsonObject.contains(JS(permissioned_domain))) {
|
||||||
input.permissionedDomain = jv.at(JS(permissioned_domain)).as_object();
|
input.permissionedDomain = jv.at(JS(permissioned_domain)).as_object();
|
||||||
|
} else if (jsonObject.contains(JS(delegate))) {
|
||||||
|
input.delegate = jv.at(JS(delegate)).as_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jsonObject.contains("include_deleted"))
|
if (jsonObject.contains("include_deleted"))
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ public:
|
|||||||
std::optional<uint32_t> createAccountClaimId;
|
std::optional<uint32_t> createAccountClaimId;
|
||||||
std::optional<ripple::uint256> oracleNode;
|
std::optional<ripple::uint256> oracleNode;
|
||||||
std::optional<ripple::uint256> credential;
|
std::optional<ripple::uint256> credential;
|
||||||
|
std::optional<boost::json::object> delegate;
|
||||||
bool includeDeleted = false;
|
bool includeDeleted = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -392,6 +393,23 @@ public:
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}},
|
}}},
|
||||||
|
{JS(delegate),
|
||||||
|
meta::WithCustomError{
|
||||||
|
validation::Type<std::string, boost::json::object>{}, Status(ClioError::RpcMalformedRequest)
|
||||||
|
},
|
||||||
|
meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
|
||||||
|
meta::IfType<boost::json::object>{meta::Section{
|
||||||
|
{JS(account),
|
||||||
|
meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
|
||||||
|
meta::WithCustomError{
|
||||||
|
validation::CustomValidators::accountBase58Validator, Status(ClioError::RpcMalformedAddress)
|
||||||
|
}},
|
||||||
|
{JS(authorize),
|
||||||
|
meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
|
||||||
|
meta::WithCustomError{
|
||||||
|
validation::CustomValidators::accountBase58Validator, Status(ClioError::RpcMalformedAddress)
|
||||||
|
}}
|
||||||
|
}}},
|
||||||
{JS(ledger), check::Deprecated{}},
|
{JS(ledger), check::Deprecated{}},
|
||||||
{"include_deleted", validation::Type<bool>{}},
|
{"include_deleted", validation::Type<bool>{}},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1516,6 +1516,31 @@ createPermissionedDomainObject(
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ripple::STObject
|
||||||
|
createDelegateObject(
|
||||||
|
std::string_view accountId,
|
||||||
|
std::string_view authorize,
|
||||||
|
std::string_view ledgerIndex,
|
||||||
|
uint64_t ownerNode,
|
||||||
|
ripple::uint256 previousTxId,
|
||||||
|
uint32_t previousTxSeq
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ripple::STObject object(ripple::sfLedgerEntry);
|
||||||
|
|
||||||
|
object.setFieldH256(ripple::sfLedgerIndex, ripple::uint256(ledgerIndex));
|
||||||
|
object.setFieldU16(ripple::sfLedgerEntryType, ripple::ltDELEGATE);
|
||||||
|
object.setAccountID(ripple::sfAccount, getAccountIdWithString(accountId));
|
||||||
|
object.setAccountID(ripple::sfAuthorize, getAccountIdWithString(authorize));
|
||||||
|
object.setFieldArray(ripple::sfPermissions, ripple::STArray{});
|
||||||
|
object.setFieldU64(ripple::sfOwnerNode, ownerNode);
|
||||||
|
object.setFieldH256(ripple::sfPreviousTxnID, previousTxId);
|
||||||
|
object.setFieldU32(ripple::sfPreviousTxnLgrSeq, previousTxSeq);
|
||||||
|
object.setFieldU32(ripple::sfFlags, 0);
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
ripple::STObject
|
ripple::STObject
|
||||||
createOraclePriceData(
|
createOraclePriceData(
|
||||||
uint64_t assetPrice,
|
uint64_t assetPrice,
|
||||||
|
|||||||
@@ -464,6 +464,16 @@ createPermissionedDomainObject(
|
|||||||
uint32_t previousTxSeq
|
uint32_t previousTxSeq
|
||||||
);
|
);
|
||||||
|
|
||||||
|
[[nodiscard]] ripple::STObject
|
||||||
|
createDelegateObject(
|
||||||
|
std::string_view accountId,
|
||||||
|
std::string_view authorize,
|
||||||
|
std::string_view ledgerIndex,
|
||||||
|
uint64_t ownerNode,
|
||||||
|
ripple::uint256 previousTxId,
|
||||||
|
uint32_t previousTxSeq
|
||||||
|
);
|
||||||
|
|
||||||
[[nodiscard]] ripple::STObject
|
[[nodiscard]] ripple::STObject
|
||||||
createOraclePriceData(
|
createOraclePriceData(
|
||||||
uint64_t assetPrice,
|
uint64_t assetPrice,
|
||||||
|
|||||||
@@ -2193,6 +2193,106 @@ generateTestValuesForParametersTest()
|
|||||||
.expectedError = "malformedRequest",
|
.expectedError = "malformedRequest",
|
||||||
.expectedErrorMessage = "Malformed request.",
|
.expectedErrorMessage = "Malformed request.",
|
||||||
},
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
.testName = "Delegate_InvalidType",
|
||||||
|
.testJson = R"json({"delegate": 123})json",
|
||||||
|
.expectedError = "malformedRequest",
|
||||||
|
.expectedErrorMessage = "Malformed request."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
.testName = "Delegate_InvalidStringIndex",
|
||||||
|
.testJson = R"json({"delegate": "invalid_hex_string"})json",
|
||||||
|
.expectedError = "malformedRequest",
|
||||||
|
.expectedErrorMessage = "Malformed request."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
.testName = "Delegate_EmptyObject",
|
||||||
|
.testJson = R"json({"delegate": {}})json",
|
||||||
|
.expectedError = "malformedRequest",
|
||||||
|
.expectedErrorMessage = "Malformed request."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
.testName = "Delegate_MissingAccount",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"json({{
|
||||||
|
"delegate": {{
|
||||||
|
"authorize": "{}"
|
||||||
|
}}
|
||||||
|
}})json",
|
||||||
|
kACCOUNT2
|
||||||
|
),
|
||||||
|
.expectedError = "malformedRequest",
|
||||||
|
.expectedErrorMessage = "Malformed request."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
.testName = "Delegate_AccountNotString",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"json({{
|
||||||
|
"delegate": {{
|
||||||
|
"account": 123,
|
||||||
|
"authorize": "{}"
|
||||||
|
}}
|
||||||
|
}})json",
|
||||||
|
kACCOUNT2
|
||||||
|
),
|
||||||
|
.expectedError = "malformedAddress",
|
||||||
|
.expectedErrorMessage = "Malformed address."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
.testName = "Delegate_AccountInvalid",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"json({{
|
||||||
|
"delegate": {{
|
||||||
|
"account": "invalid_address",
|
||||||
|
"authorize": "{}"
|
||||||
|
}}
|
||||||
|
}})json",
|
||||||
|
kACCOUNT2
|
||||||
|
),
|
||||||
|
.expectedError = "malformedAddress",
|
||||||
|
.expectedErrorMessage = "Malformed address."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
.testName = "Delegate_MissingAuthorize",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"json({{
|
||||||
|
"delegate": {{
|
||||||
|
"account": "{}"
|
||||||
|
}}
|
||||||
|
}})json",
|
||||||
|
kACCOUNT
|
||||||
|
),
|
||||||
|
.expectedError = "malformedRequest",
|
||||||
|
.expectedErrorMessage = "Malformed request."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
.testName = "Delegate_AuthorizeNotString",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"json({{
|
||||||
|
"delegate": {{
|
||||||
|
"account": "{}",
|
||||||
|
"authorize": 123
|
||||||
|
}}
|
||||||
|
}})json",
|
||||||
|
kACCOUNT
|
||||||
|
),
|
||||||
|
.expectedError = "malformedAddress",
|
||||||
|
.expectedErrorMessage = "Malformed address."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
.testName = "Delegate_AuthorizeInvalid",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"json({{
|
||||||
|
"delegate": {{
|
||||||
|
"account": "{}",
|
||||||
|
"authorize": "invalid_address"
|
||||||
|
}}
|
||||||
|
}})json",
|
||||||
|
kACCOUNT
|
||||||
|
),
|
||||||
|
.expectedError = "malformedAddress",
|
||||||
|
.expectedErrorMessage = "Malformed address."
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2957,7 +3057,36 @@ generateTestValuesForNormalPathTest()
|
|||||||
ripple::keylet::permissionedDomain(ripple::parseBase58<ripple::AccountID>(kACCOUNT).value(), kRANGE_MAX)
|
ripple::keylet::permissionedDomain(ripple::parseBase58<ripple::AccountID>(kACCOUNT).value(), kRANGE_MAX)
|
||||||
.key,
|
.key,
|
||||||
.mockedEntity = createPermissionedDomainObject(kACCOUNT, kINDEX1, kRANGE_MAX, 0, ripple::uint256{0}, 0)
|
.mockedEntity = createPermissionedDomainObject(kACCOUNT, kINDEX1, kRANGE_MAX, 0, ripple::uint256{0}, 0)
|
||||||
}
|
},
|
||||||
|
NormalPathTestBundle{
|
||||||
|
.testName = "DelegateViaStringIndex",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"json({{
|
||||||
|
"binary": true,
|
||||||
|
"delegate": "{}"
|
||||||
|
}})json",
|
||||||
|
kINDEX1
|
||||||
|
),
|
||||||
|
.expectedIndex = ripple::uint256{kINDEX1},
|
||||||
|
.mockedEntity = createDelegateObject(kACCOUNT, kACCOUNT2, kINDEX1, 0, ripple::uint256{0}, 0)
|
||||||
|
},
|
||||||
|
NormalPathTestBundle{
|
||||||
|
.testName = "DelegateViaObject",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"json({{
|
||||||
|
"binary": true,
|
||||||
|
"delegate": {{
|
||||||
|
"account": "{}",
|
||||||
|
"authorize": "{}"
|
||||||
|
}}
|
||||||
|
}})json",
|
||||||
|
kACCOUNT,
|
||||||
|
kACCOUNT2
|
||||||
|
),
|
||||||
|
.expectedIndex =
|
||||||
|
ripple::keylet::delegate(getAccountIdWithString(kACCOUNT), getAccountIdWithString(kACCOUNT2)).key,
|
||||||
|
.mockedEntity = createDelegateObject(kACCOUNT, kACCOUNT2, kINDEX1, 0, ripple::uint256{0}, 0)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -834,7 +834,7 @@ TEST_F(RPCTxTest, CTIDNotMatch)
|
|||||||
ASSERT_FALSE(output);
|
ASSERT_FALSE(output);
|
||||||
|
|
||||||
auto const err = rpc::makeError(output.result.error());
|
auto const err = rpc::makeError(output.result.error());
|
||||||
EXPECT_EQ(err.at("error").as_string(), "unknown");
|
EXPECT_EQ(err.at("error").as_string(), "wrongNetwork");
|
||||||
EXPECT_EQ(err.at("error_code").as_uint64(), rpc::RippledError::rpcWRONG_NETWORK);
|
EXPECT_EQ(err.at("error_code").as_uint64(), rpc::RippledError::rpcWRONG_NETWORK);
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
err.at("error_message").as_string(),
|
err.at("error_message").as_string(),
|
||||||
|
|||||||
Reference in New Issue
Block a user