cherry-ff (#52)

Co-authored-by: Richard Holland <richard.holland@starstone.co.nz>
This commit is contained in:
Denis Angell
2023-03-28 18:08:52 +00:00
parent b7ccfaa7b1
commit e71137fe4a
12 changed files with 157 additions and 106 deletions

View File

@@ -58,51 +58,62 @@ URIToken::preflight(PreflightContext const& ctx)
return temMALFORMED; return temMALFORMED;
} }
if (!([](std::vector<uint8_t> const& u) -> bool if (!([](std::vector<uint8_t> const& u) -> bool {
{ // this code is from
// this code is from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c // https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
uint8_t const* s = (uint8_t const*)u.data(); uint8_t const* s = (uint8_t const*)u.data();
uint8_t const* end = s + u.size(); uint8_t const* end = s + u.size();
while (s < end) { while (s < end)
{
if (*s < 0x80) if (*s < 0x80)
/* 0xxxxxxx */ /* 0xxxxxxx */
s++; s++;
else if ((s[0] & 0xe0) == 0xc0) { else if ((s[0] & 0xe0) == 0xc0)
{
/* 110XXXXx 10xxxxxx */ /* 110XXXXx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 || if ((s[1] & 0xc0) != 0x80 ||
(s[0] & 0xfe) == 0xc0) /* overlong? */ (s[0] & 0xfe) == 0xc0) /* overlong? */
return false; return false;
else else
s += 2; s += 2;
} else if ((s[0] & 0xf0) == 0xe0) { }
else if ((s[0] & 0xf0) == 0xe0)
{
/* 1110XXXX 10Xxxxxx 10xxxxxx */ /* 1110XXXX 10Xxxxxx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 || if ((s[1] & 0xc0) != 0x80 ||
(s[2] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
(s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ (s[0] == 0xe0 &&
(s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ (s[1] & 0xe0) == 0x80) || /* overlong? */
(s[0] == 0xef && s[1] == 0xbf && (s[0] == 0xed &&
(s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ (s[1] & 0xe0) == 0xa0) || /* surrogate? */
(s[0] == 0xef && s[1] == 0xbf &&
(s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */
return false; return false;
else else
s += 3; s += 3;
} else if ((s[0] & 0xf8) == 0xf0) { }
else if ((s[0] & 0xf8) == 0xf0)
{
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 || if ((s[1] & 0xc0) != 0x80 ||
(s[2] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
(s[3] & 0xc0) != 0x80 || (s[3] & 0xc0) != 0x80 ||
(s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ (s[0] == 0xf0 &&
(s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ (s[1] & 0xf0) == 0x80) || /* overlong? */
(s[0] == 0xf4 && s[1] > 0x8f) ||
s[0] > 0xf4) /* > U+10FFFF? */
return false; return false;
else else
s += 4; s += 4;
} else }
else
return false; return false;
} }
return true; return true;
})(uri)) })(uri))
{ {
JLOG(ctx.j.warn()) JLOG(ctx.j.warn()) << "Malformed transaction. URI must be a "
<< "Malformed transaction. URI must be a valid utf-8 string."; "valid utf-8 string.";
return temMALFORMED; return temMALFORMED;
} }
@@ -221,8 +232,7 @@ URIToken::preclaim(PreclaimContext const& ctx)
return tesSUCCESS; return tesSUCCESS;
} }
case ttURITOKEN_BUY: case ttURITOKEN_BUY: {
{
if (acc == *owner) if (acc == *owner)
return tecCANT_ACCEPT_OWN_NFTOKEN_OFFER; return tecCANT_ACCEPT_OWN_NFTOKEN_OFFER;
@@ -458,7 +468,7 @@ URIToken::doApply()
// check for any possible bars to a buy transaction // check for any possible bars to a buy transaction
// between these accounts for this asset // between these accounts for this asset
if (buyerIssuer) if (buyerIssuer)
{ {
// pass: issuer does not create own trustline // pass: issuer does not create own trustline
@@ -494,7 +504,7 @@ URIToken::doApply()
if (sellerIssuer) if (sellerIssuer)
{ {
// pass: issuer does not create own trustline // pass: issuer does not create own trustline
} }
else if (!sleDstLine) else if (!sleDstLine)
{ {
// they do not, so we can create one if they have sufficient // they do not, so we can create one if they have sufficient
@@ -512,7 +522,6 @@ URIToken::doApply()
return tecNO_LINE_INSUF_RESERVE; return tecNO_LINE_INSUF_RESERVE;
} }
} }
if (buyerIssuer) if (buyerIssuer)
{ {
// pass: issuer does not adjust own trustline // pass: issuer does not adjust own trustline

View File

@@ -30,7 +30,6 @@ namespace ripple {
class URIToken : public Transactor class URIToken : public Transactor
{ {
public: public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit URIToken(ApplyContext& ctx) : Transactor(ctx) explicit URIToken(ApplyContext& ctx) : Transactor(ctx)

View File

@@ -633,7 +633,7 @@ trustTransferAllowed(
std::shared_ptr<SLE>, std::shared_ptr<SLE>,
std::shared_ptr<SLE const>>::type SLEPtr; std::shared_ptr<SLE const>>::type SLEPtr;
if (isFakeXRP(issue.currency)) if (issue.currency == badCurrency())
return tecNO_PERMISSION; return tecNO_PERMISSION;
auto const sleIssuerAcc = view.read(keylet::account(issue.account)); auto const sleIssuerAcc = view.read(keylet::account(issue.account));
@@ -678,8 +678,9 @@ trustTransferAllowed(
// the point of transfer. // the point of transfer.
continue; continue;
} }
// sanity check the line, insane lines are a bar to xfer // sanity check the line, insane lines are a bar to xfer
if (lockedBalanceAllowed)
{ {
// these "strange" old lines, if they even exist anymore are // these "strange" old lines, if they even exist anymore are
// always a bar to xfer // always a bar to xfer

View File

@@ -418,7 +418,9 @@ Keylet
uritoken(AccountID const& issuer, Blob const& uri) uritoken(AccountID const& issuer, Blob const& uri)
{ {
return { return {
ltURI_TOKEN, indexHash(LedgerNameSpace::URI_TOKEN, issuer, Slice{uri.data(), uri.size()})}; ltURI_TOKEN,
indexHash(
LedgerNameSpace::URI_TOKEN, issuer, Slice{uri.data(), uri.size()})};
} }
} // namespace keylet } // namespace keylet

View File

@@ -403,7 +403,7 @@ TxFormats::TxFormats()
{sfTicketSequence, soeOPTIONAL}, {sfTicketSequence, soeOPTIONAL},
}, },
commonFields); commonFields);
add(jss::URITokenCancelSellOffer, add(jss::URITokenCancelSellOffer,
ttURITOKEN_CANCEL_SELL_OFFER, ttURITOKEN_CANCEL_SELL_OFFER,
{ {

View File

@@ -135,6 +135,12 @@ JSS(TxnSignature); // field.
JSS(TransactionType); // in: TransactionSign. JSS(TransactionType); // in: TransactionSign.
JSS(TransferRate); // in: TransferRate. JSS(TransferRate); // in: TransferRate.
JSS(TrustSet); // transaction type. JSS(TrustSet); // transaction type.
JSS(URIToken); // LedgerEntry
JSS(URITokenMint); // tx type
JSS(URITokenBurn); // tx type
JSS(URITokenBuy); // tx type
JSS(URITokenCreateSellOffer); // tx type
JSS(URITokenCancelSellOffer); // tx type
JSS(aborted); // out: InboundLedger JSS(aborted); // out: InboundLedger
JSS(accepted); // out: LedgerToJson, OwnerInfo, SubmitTransaction JSS(accepted); // out: LedgerToJson, OwnerInfo, SubmitTransaction
JSS(account); // in/out: many JSS(account); // in/out: many
@@ -666,12 +672,6 @@ JSS(unl); // out: UnlList
JSS(unlimited); // out: Connection.h JSS(unlimited); // out: Connection.h
JSS(uptime); // out: GetCounts JSS(uptime); // out: GetCounts
JSS(uri); // out: ValidatorSites JSS(uri); // out: ValidatorSites
JSS(URIToken); // LedgerEntry
JSS(URITokenMint); // tx type
JSS(URITokenBurn); // tx type
JSS(URITokenBuy); // tx type
JSS(URITokenCreateSellOffer); // tx type
JSS(URITokenCancelSellOffer); // tx type
JSS(url); // in/out: Subscribe, Unsubscribe JSS(url); // in/out: Subscribe, Unsubscribe
JSS(url_password); // in: Subscribe JSS(url_password); // in: Subscribe
JSS(url_username); // in: Subscribe JSS(url_username); // in: Subscribe

View File

@@ -206,6 +206,7 @@ doAccountObjects(RPC::JsonContext& context)
{jss::escrow, ltESCROW}, {jss::escrow, ltESCROW},
{jss::nft_page, ltNFTOKEN_PAGE}, {jss::nft_page, ltNFTOKEN_PAGE},
{jss::payment_channel, ltPAYCHAN}, {jss::payment_channel, ltPAYCHAN},
{jss::uri_token, ltURI_TOKEN},
{jss::state, ltRIPPLE_STATE}}; {jss::state, ltRIPPLE_STATE}};
typeFilter.emplace(); typeFilter.emplace();

View File

@@ -259,6 +259,16 @@ doLedgerEntry(RPC::JsonContext& context)
jvResult[jss::error] = "malformedRequest"; jvResult[jss::error] = "malformedRequest";
} }
} }
else if (context.params.isMember(jss::uri_token))
{
expectedType = ltURI_TOKEN;
if (!uNodeIndex.parseHex(context.params[jss::uri_token].asString()))
{
uNodeIndex = beast::zero;
jvResult[jss::error] = "malformedRequest";
}
}
else if (context.params.isMember(jss::ripple_state)) else if (context.params.isMember(jss::ripple_state))
{ {
expectedType = ltRIPPLE_STATE; expectedType = ltRIPPLE_STATE;

View File

@@ -1076,6 +1076,7 @@ chooseLedgerEntryType(Json::Value const& params)
{jss::hashes, ltLEDGER_HASHES}, {jss::hashes, ltLEDGER_HASHES},
{jss::offer, ltOFFER}, {jss::offer, ltOFFER},
{jss::payment_channel, ltPAYCHAN}, {jss::payment_channel, ltPAYCHAN},
{jss::uri_token, ltURI_TOKEN},
{jss::signer_list, ltSIGNER_LIST}, {jss::signer_list, ltSIGNER_LIST},
{jss::state, ltRIPPLE_STATE}, {jss::state, ltRIPPLE_STATE},
{jss::ticket, ltTICKET}, {jss::ticket, ltTICKET},

View File

@@ -46,7 +46,8 @@ struct URIToken_test : public beast::unit_test::suite
{ {
auto const uritSle = view.read({ltURI_TOKEN, tid}); auto const uritSle = view.read({ltURI_TOKEN, tid});
ripple::Dir const ownerDir(view, keylet::ownerDir(acct.id())); ripple::Dir const ownerDir(view, keylet::ownerDir(acct.id()));
return std::find(ownerDir.begin(), ownerDir.end(), uritSle) != ownerDir.end(); return std::find(ownerDir.begin(), ownerDir.end(), uritSle) !=
ownerDir.end();
} }
static std::size_t static std::size_t
@@ -116,8 +117,7 @@ struct URIToken_test : public beast::unit_test::suite
jtx::Account const& gw, jtx::Account const& gw,
jtx::IOU const& iou) jtx::IOU const& iou)
{ {
auto const sle = auto const sle = env.le(keylet::line(account, gw, iou.currency));
env.le(keylet::line(account, gw, iou.currency));
if (sle && sle->isFieldPresent(sfBalance)) if (sle && sle->isFieldPresent(sfBalance))
return (*sle)[sfBalance]; return (*sle)[sfBalance];
return STAmount(iou, 0); return STAmount(iou, 0);
@@ -200,7 +200,8 @@ struct URIToken_test : public beast::unit_test::suite
{ {
// If the URIToken amendment is not enabled, you should not be able // If the URIToken amendment is not enabled, you should not be able
// to mint, burn, buy, sell or clear uri tokens. // to mint, burn, buy, sell or clear uri tokens.
auto const amend = withURIToken ? features : features - featureURIToken; auto const amend =
withURIToken ? features : features - featureURIToken;
Env env{*this, amend}; Env env{*this, amend};
env.fund(XRP(1000), alice, bob); env.fund(XRP(1000), alice, bob);
@@ -558,7 +559,6 @@ struct URIToken_test : public beast::unit_test::suite
// tecINSUFFICIENT_FUNDS - insuficient amount sent // tecINSUFFICIENT_FUNDS - insuficient amount sent
env(buy(bob, hexid, USD(10000)), ter(tecINSUFFICIENT_FUNDS)); env(buy(bob, hexid, USD(10000)), ter(tecINSUFFICIENT_FUNDS));
env.close(); env.close();
// tecNO_LINE_INSUF_RESERVE - insuficient xrp to create line // tecNO_LINE_INSUF_RESERVE - insuficient xrp to create line
{ {
// fund dave 251 xrp (not enough for line reserve) // fund dave 251 xrp (not enough for line reserve)
@@ -656,7 +656,8 @@ struct URIToken_test : public beast::unit_test::suite
auto const tid = tokenid(alice, uri); auto const tid = tokenid(alice, uri);
std::string const hexid{strHex(tid)}; std::string const hexid{strHex(tid)};
std::string const digestval = "C16E7263F07AA41261DCC955660AF4646ADBA414E37B6F5A5BA50F75153F5CCC"; std::string const digestval =
"C16E7263F07AA41261DCC955660AF4646ADBA414E37B6F5A5BA50F75153F5CCC";
// has digest - has uri - no flags // has digest - has uri - no flags
{ {
@@ -664,7 +665,8 @@ struct URIToken_test : public beast::unit_test::suite
env(mint(alice, uri), json(sfDigest.fieldName, digestval)); env(mint(alice, uri), json(sfDigest.fieldName, digestval));
env.close(); env.close();
BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid));
BEAST_EXPECT(to_string(tokenDigest(*env.current(), tid)) == digestval); BEAST_EXPECT(
to_string(tokenDigest(*env.current(), tid)) == digestval);
// cleanup // cleanup
env(burn(alice, hexid)); env(burn(alice, hexid));
env.close(); env.close();
@@ -678,7 +680,8 @@ struct URIToken_test : public beast::unit_test::suite
json(sfDigest.fieldName, digestval)); json(sfDigest.fieldName, digestval));
env.close(); env.close();
BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid));
BEAST_EXPECT(to_string(tokenDigest(*env.current(), tid)) == digestval); BEAST_EXPECT(
to_string(tokenDigest(*env.current(), tid)) == digestval);
// cleanup // cleanup
env(burn(alice, hexid)); env(burn(alice, hexid));
env.close(); env.close();
@@ -1404,7 +1407,6 @@ struct URIToken_test : public beast::unit_test::suite
auto const USD = t.gw["USD"]; auto const USD = t.gw["USD"];
env.fund(XRP(5000), t.src, t.dst, t.gw); env.fund(XRP(5000), t.src, t.dst, t.gw);
env.close(); env.close();
if (t.hasTrustline) if (t.hasTrustline)
env.trust(USD(100000), t.src, t.dst); env.trust(USD(100000), t.src, t.dst);
else else
@@ -1457,21 +1459,19 @@ struct URIToken_test : public beast::unit_test::suite
bool negative; bool negative;
}; };
std::array<TestAccountData, 4> tests = { std::array<TestAccountData, 4> tests = {{
{ // acct no trustline
// acct no trustline // acct > issuer
// acct > issuer {Account("alice2"), Account{"gw0"}, false, true},
{Account("alice2"), Account{"gw0"}, false, true}, // acct < issuer
// acct < issuer {Account("carol0"), Account{"gw1"}, false, false},
{Account("carol0"), Account{"gw1"}, false, false},
// acct has trustline
// acct has trustline // acct > issuer
// acct > issuer {Account("alice2"), Account{"gw0"}, true, true},
{Account("alice2"), Account{"gw0"}, true, true}, // acct < issuer
// acct < issuer {Account("carol0"), Account{"gw1"}, true, false},
{Account("carol0"), Account{"gw1"}, true, false}, }};
}
};
// test gateway is buyer // test gateway is buyer
for (auto const& t : tests) for (auto const& t : tests)
@@ -1480,15 +1480,14 @@ struct URIToken_test : public beast::unit_test::suite
auto const USD = t.gw["USD"]; auto const USD = t.gw["USD"];
env.fund(XRP(5000), t.acct, t.gw); env.fund(XRP(5000), t.acct, t.gw);
env.close(); env.close();
if (t.hasTrustline) if (t.hasTrustline)
env.trust(USD(100000), t.acct); env.trust(USD(100000), t.acct);
env.close(); env.close();
if (t.hasTrustline) if (t.hasTrustline)
env(pay(t.gw, t.acct, USD(10000))); env(pay(t.gw, t.acct, USD(10000)));
env.close(); env.close();
// acct can create uritoken // acct can create uritoken
@@ -1504,7 +1503,8 @@ struct URIToken_test : public beast::unit_test::suite
env(sell(t.acct, id, delta)); env(sell(t.acct, id, delta));
env.close(); env.close();
auto const preAmount = t.hasTrustline ? 10000 : 0; auto const preAmount = t.hasTrustline ? 10000 : 0;
BEAST_EXPECT(preAcct == (t.negative ? -USD(preAmount) : USD(preAmount))); BEAST_EXPECT(
preAcct == (t.negative ? -USD(preAmount) : USD(preAmount)));
// gw can create buy // gw can create buy
env(buy(t.gw, id, delta)); env(buy(t.gw, id, delta));
@@ -1544,7 +1544,8 @@ struct URIToken_test : public beast::unit_test::suite
env(sell(t.gw, id, delta)); env(sell(t.gw, id, delta));
env.close(); env.close();
auto const preAmount = 10000; auto const preAmount = 10000;
BEAST_EXPECT(preAcct == (t.negative ? -USD(preAmount) : USD(preAmount))); BEAST_EXPECT(
preAcct == (t.negative ? -USD(preAmount) : USD(preAmount)));
// acct can create buy // acct can create buy
env(buy(t.acct, id, delta)); env(buy(t.acct, id, delta));
@@ -1623,7 +1624,6 @@ struct URIToken_test : public beast::unit_test::suite
auto const carol = Account("carol"); auto const carol = Account("carol");
auto const gw = Account{"gateway"}; auto const gw = Account{"gateway"};
auto const USD = gw["USD"]; auto const USD = gw["USD"];
// test Global Freeze // test Global Freeze
{ {
// setup env // setup env
@@ -1752,7 +1752,6 @@ struct URIToken_test : public beast::unit_test::suite
std::string const id{strHex(tokenid(alice, uri))}; std::string const id{strHex(tokenid(alice, uri))};
auto const delta = USD(100); auto const delta = USD(100);
auto preBob = env.balance(bob, USD.issue()); auto preBob = env.balance(bob, USD.issue());
// alice mints and sells // alice mints and sells
env(mint(alice, uri)); env(mint(alice, uri));
env(sell(alice, id, delta)); env(sell(alice, id, delta));
@@ -1803,7 +1802,6 @@ struct URIToken_test : public beast::unit_test::suite
// alice mints // alice mints
env(mint(alice, uri)); env(mint(alice, uri));
env.close(); env.close();
// alice sells // alice sells
env(sell(alice, hexid, delta)); env(sell(alice, hexid, delta));
env.close(); env.close();
@@ -1842,11 +1840,11 @@ struct URIToken_test : public beast::unit_test::suite
// alice mints // alice mints
env(mint(alice, uri)); env(mint(alice, uri));
env.close(); env.close();
// alice sells // alice sells
env(sell(alice, hexid, XRP(10))); env(sell(alice, hexid, XRP(10)));
env.close(); env.close();
// bob buys // bob buys
env(buy(bob, hexid, XRP(10))); env(buy(bob, hexid, XRP(10)));
env.close(); env.close();
@@ -1912,7 +1910,6 @@ struct URIToken_test : public beast::unit_test::suite
// test utf-8 success // test utf-8 success
{ {
// case: kosme // case: kosme
uri = "κόσμε"; uri = "κόσμε";
env(mint(alice, uri)); env(mint(alice, uri));
@@ -1944,13 +1941,14 @@ struct URIToken_test : public beast::unit_test::suite
// case: ipfs metadata url // case: ipfs metadata url
uri = "https://example.com/ipfs/"; uri = "https://example.com/ipfs/";
env(mint(alice, uri)); env(mint(alice, uri));
// BOUNDRY - START // BOUNDRY - START
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// case: 1 byte (U-00000000) // case: 1 byte (U-00000000)
uri = "\x00"; uri = "\x00";
env(mint(alice, uri), ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL env(mint(alice, uri),
ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL
// case: 2 bytes (U-00000080) // case: 2 bytes (U-00000080)
uri = "\xC2\x80"; uri = "\xC2\x80";
env(mint(alice, uri)); env(mint(alice, uri));
@@ -1962,10 +1960,12 @@ struct URIToken_test : public beast::unit_test::suite
env(mint(alice, uri)); env(mint(alice, uri));
// case: 5 bytes (U-00200000) // case: 5 bytes (U-00200000)
uri = "\xF8\x88\x80\x80\x80"; uri = "\xF8\x88\x80\x80\x80";
env(mint(alice, uri), ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL env(mint(alice, uri),
ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL
// case: 6 bytes (U-04000000) // case: 6 bytes (U-04000000)
uri = "\xFC\x84\x80\x80\x80\x80"; uri = "\xFC\x84\x80\x80\x80\x80";
env(mint(alice, uri), ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL env(mint(alice, uri),
ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL
// BOUNDRY - END // BOUNDRY - END
// ---------------------------------------------------------------- // ----------------------------------------------------------------
@@ -1978,22 +1978,27 @@ struct URIToken_test : public beast::unit_test::suite
env(mint(alice, uri)); env(mint(alice, uri));
// case: 3 bytes (U-0000FFFF) // case: 3 bytes (U-0000FFFF)
uri = "\xEF\xBF\xBF"; uri = "\xEF\xBF\xBF";
env(mint(alice, uri), ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL env(mint(alice, uri),
ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL
// case: 4 bytes (U-001FFFFF) // case: 4 bytes (U-001FFFFF)
uri = "\xF7\xBF\xBF\xBF"; uri = "\xF7\xBF\xBF\xBF";
env(mint(alice, uri), ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL env(mint(alice, uri),
ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL
// case: 5 bytes (U-03FFFFFF) // case: 5 bytes (U-03FFFFFF)
uri = "\xFB\xBF\xBF\xBF\xBF"; uri = "\xFB\xBF\xBF\xBF\xBF";
env(mint(alice, uri), ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL env(mint(alice, uri),
ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL
// case: 6 bytes (U-7FFFFFFF) // case: 6 bytes (U-7FFFFFFF)
uri = "\xFD\xBF\xBF\xBF\xBF\xBF"; uri = "\xFD\xBF\xBF\xBF\xBF\xBF";
env(mint(alice, uri), ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL env(mint(alice, uri),
ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL
// // BOUNDRY - OTHER // // BOUNDRY - OTHER
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// case: 1 bytes (U-0000D7FF) // case: 1 bytes (U-0000D7FF)
uri = "\xD7\xFF"; uri = "\xD7\xFF";
env(mint(alice, uri), ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL env(mint(alice, uri),
ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL
// case: 2 bytes (U-0000E000) // case: 2 bytes (U-0000E000)
uri = "\xEE\x80\x80"; uri = "\xEE\x80\x80";
env(mint(alice, uri)); env(mint(alice, uri));
@@ -2005,7 +2010,8 @@ struct URIToken_test : public beast::unit_test::suite
env(mint(alice, uri)); env(mint(alice, uri));
// // case: 4 bytes (U-00110000) // // case: 4 bytes (U-00110000)
uri = "\xF4\x90\x80\x80"; uri = "\xF4\x90\x80\x80";
env(mint(alice, uri), ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL env(mint(alice, uri),
ter(temMALFORMED)); // TODO: REVIEW - SHOULD NOT FAIL
} }
// test utf8 malformed // test utf8 malformed
{ {
@@ -2014,66 +2020,84 @@ struct URIToken_test : public beast::unit_test::suite
// First continuation byte 0x80: // First continuation byte 0x80:
uri = "\x80"; uri = "\x80";
env(mint(alice, uri), ter(temMALFORMED)); env(mint(alice, uri), ter(temMALFORMED));
// Last continuation byte 0xbf // Last continuation byte 0xbf
uri = "\xBF"; uri = "\xBF";
env(mint(alice, uri), ter(temMALFORMED)); env(mint(alice, uri), ter(temMALFORMED));
// 2 continuation bytes // 2 continuation bytes
uri = "<EFBFBD><EFBFBD>"; uri = "<EFBFBD><EFBFBD>";
env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL
// 3 continuation bytes // 3 continuation bytes
uri = "<EFBFBD><EFBFBD><EFBFBD>"; uri = "<EFBFBD><EFBFBD><EFBFBD>";
env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL
// 4 continuation bytes // 4 continuation bytes
uri = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>"; uri = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>";
env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL
// 5 continuation bytes // 5 continuation bytes
uri = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"; uri = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>";
env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL
// 6 continuation bytes // 6 continuation bytes
uri = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"; uri = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>";
env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL
// 7 continuation bytes // 7 continuation bytes
uri = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"; uri = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>";
env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL
// Sequence of all 64 possible continuation bytes (0x80-0xbf) // Sequence of all 64 possible continuation bytes (0x80-0xbf)
uri = "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF"; uri =
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E"
"\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D"
"\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC"
"\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB"
"\xBC\xBD\xBE\xBF";
env(mint(alice, uri), ter(temMALFORMED)); env(mint(alice, uri), ter(temMALFORMED));
// TODO: REVIEW - THIS IS NOT THE CORRECT 32 byte sequence. // TODO: REVIEW - THIS IS NOT THE CORRECT 32 byte sequence.
// All 32 first bytes of 2-byte sequences (0xc0-0xdf), each followed // All 32 first bytes of 2-byte sequences (0xc0-0xdf), each followed
// by a space character // by a space character
// uri = "\xE0\x80\x80 \xE0\x80\x81 \xE0\x80\x82 \xE0\x80\x83 \xE0\x80\x84 \xE0\x80\x85 \xE0\x80\x86 \xE0\x80\x87 \xE0\x80\x88 \xE0\x80\x89 \xE0\x80\x8A \xE0\x80\x8B \xE0\x80\x8C \xE0\x80\x8D \xE0\x80\x8E \xE0\x80\x8F \xE0\x80\x90"; // uri = "\xE0\x80\x80 \xE0\x80\x81 \xE0\x80\x82 \xE0\x80\x83
// env(mint(alice, uri), ter(temMALFORMED)); // \xE0\x80\x84 \xE0\x80\x85 \xE0\x80\x86 \xE0\x80\x87 \xE0\x80\x88
// \xE0\x80\x89 \xE0\x80\x8A \xE0\x80\x8B \xE0\x80\x8C \xE0\x80\x8D
// \xE0\x80\x8E \xE0\x80\x8F \xE0\x80\x90"; env(mint(alice, uri),
// ter(temMALFORMED));
// All 16 first bytes of 3-byte sequences (0xe0-0xef), each followed // All 16 first bytes of 3-byte sequences (0xe0-0xef), each followed
// by a space character // by a space character
uri = "\xE0\x80\x80 \xE0\x80\x81 \xE0\x80\x82 \xE0\x80\x83 \xE0\x80\x84 \xE0\x80\x85 \xE0\x80\x86 \xE0\x80\x87 \xE0\x80\x88 \xE0\x80\x89 \xE0\x80\x8A \xE0\x80\x8B \xE0\x80\x8C \xE0\x80\x8D \xE0\x80\x8E \xE0\x80\x8F \xE0\x80\x90"; uri =
"\xE0\x80\x80 \xE0\x80\x81 \xE0\x80\x82 \xE0\x80\x83 "
"\xE0\x80\x84 \xE0\x80\x85 \xE0\x80\x86 \xE0\x80\x87 "
"\xE0\x80\x88 \xE0\x80\x89 \xE0\x80\x8A \xE0\x80\x8B "
"\xE0\x80\x8C \xE0\x80\x8D \xE0\x80\x8E \xE0\x80\x8F "
"\xE0\x80\x90";
env(mint(alice, uri), ter(temMALFORMED)); env(mint(alice, uri), ter(temMALFORMED));
// All 8 first bytes of 4-byte sequences (0xf0-0xf7), each followed // All 8 first bytes of 4-byte sequences (0xf0-0xf7), each followed
// by a space character // by a space character
uri = "\xF0\x90\x80\x80 \xF0\x90\x80\x81 \xF0\x90\x80\x82 \xF0\x90\x80\x83 \xF0\x90\x80\x84 \xF0\x90\x80\x85 \xF0\x90\x80\x86 \xF0\x90\x80\x87"; uri =
env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL "\xF0\x90\x80\x80 \xF0\x90\x80\x81 \xF0\x90\x80\x82 "
"\xF0\x90\x80\x83 \xF0\x90\x80\x84 \xF0\x90\x80\x85 "
"\xF0\x90\x80\x86 \xF0\x90\x80\x87";
env(mint(alice, uri)); // TODO: REVIEW - SHOULD FAIL
// All 4 first bytes of 5-byte sequences (0xf8-0xfb), each followed // All 4 first bytes of 5-byte sequences (0xf8-0xfb), each followed
// by a space character // by a space character
uri = "\xF8\x88\x80\x80\x80 \xF8\x88\x80\x80\x81 \xF8\x88\x80\x80\x82 \xF8\x88\x80\x80\x83"; uri =
env(mint(alice, uri), ter(temMALFORMED)); // TODO: REVIEW - SHOULD FAIL "\xF8\x88\x80\x80\x80 \xF8\x88\x80\x80\x81 "
"\xF8\x88\x80\x80\x82 \xF8\x88\x80\x80\x83";
env(mint(alice, uri),
ter(temMALFORMED)); // TODO: REVIEW - SHOULD FAIL
// All 2 first bytes of 6-byte sequences (0xfc-0xfd), each followed // All 2 first bytes of 6-byte sequences (0xfc-0xfd), each followed
// by a space character // by a space character
uri = "\xFC\x84\x80\x80\x80\x80 \xFC\x84\x80\x80\x80\x81"; uri = "\xFC\x84\x80\x80\x80\x80 \xFC\x84\x80\x80\x80\x81";
env(mint(alice, uri), ter(temMALFORMED)); env(mint(alice, uri), ter(temMALFORMED));
// Sequences with last continuation byte missing // Sequences with last continuation byte missing
// Concatenation of incomplete sequences // Concatenation of incomplete sequences
@@ -2194,7 +2218,6 @@ struct URIToken_test : public beast::unit_test::suite
env(mint(alice, uri), ter(temMALFORMED)); env(mint(alice, uri), ter(temMALFORMED));
} }
} }
void void
testWithFeats(FeatureBitset features) testWithFeats(FeatureBitset features)
{ {

View File

@@ -585,6 +585,7 @@ public:
BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::signer_list), 0)); BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::signer_list), 0));
BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::state), 0)); BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::state), 0));
BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::ticket), 0)); BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::ticket), 0));
BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::uri_token), 0));
// gw mints an NFT so we can find it. // gw mints an NFT so we can find it.
uint256 const nftID{token::getNextID(env, gw, 0u, tfTransferable)}; uint256 const nftID{token::getNextID(env, gw, 0u, tfTransferable)};
@@ -746,7 +747,7 @@ public:
{ {
// Find the uri token. // Find the uri token.
std::string const uri(maxTokenURILength, '?'); std::string const uri(maxTokenURILength, '?');
Json::Value const resp = acct_objs(gw, jss::URIToken); Json::Value const resp = acct_objs(gw, jss::uri_token);
BEAST_EXPECT(acct_objs_is_size(resp, 1)); BEAST_EXPECT(acct_objs_is_size(resp, 1));
auto const& uritoken = resp[jss::result][jss::account_objects][0u]; auto const& uritoken = resp[jss::result][jss::account_objects][0u];
@@ -767,7 +768,8 @@ public:
jss::Check.c_str(), jss::Check.c_str(),
jss::NFTokenPage.c_str(), jss::NFTokenPage.c_str(),
jss::RippleState.c_str(), jss::RippleState.c_str(),
jss::PayChannel.c_str()}; jss::PayChannel.c_str(),
jss::URIToken.c_str()};
std::sort(v.begin(), v.end()); std::sort(v.begin(), v.end());
return v; return v;
}(); }();

View File

@@ -1230,7 +1230,8 @@ class LedgerRPC_test : public beast::unit_test::suite
env.close(); env.close();
// Lambda to create an uritoken. // Lambda to create an uritoken.
auto mint = [](test::jtx::Account const& account, std::string const& uri) { auto mint = [](test::jtx::Account const& account,
std::string const& uri) {
Json::Value jv; Json::Value jv;
jv[jss::TransactionType] = jss::URITokenMint; jv[jss::TransactionType] = jss::URITokenMint;
jv[jss::Flags] = tfBurnable; jv[jss::Flags] = tfBurnable;
@@ -1246,13 +1247,15 @@ class LedgerRPC_test : public beast::unit_test::suite
std::string const ledgerHash{to_string(env.closed()->info().hash)}; std::string const ledgerHash{to_string(env.closed()->info().hash)};
uint256 const uritokenIndex{keylet::uritoken(alice, Blob(uri.begin(), uri.end())).key}; uint256 const uritokenIndex{
keylet::uritoken(alice, Blob(uri.begin(), uri.end())).key};
{ {
// Request the uritoken using its index. // Request the uritoken using its index.
Json::Value jvParams; Json::Value jvParams;
jvParams[jss::URIToken] = to_string(uritokenIndex); jvParams[jss::uri_token] = to_string(uritokenIndex);
jvParams[jss::ledger_hash] = ledgerHash; jvParams[jss::ledger_hash] = ledgerHash;
Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; Json::Value const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result];
BEAST_EXPECT(jrr[jss::node][sfOwner.jsonName] == alice.human()); BEAST_EXPECT(jrr[jss::node][sfOwner.jsonName] == alice.human());
BEAST_EXPECT(jrr[jss::node][sfURI.jsonName] == strHex(uri)); BEAST_EXPECT(jrr[jss::node][sfURI.jsonName] == strHex(uri));
BEAST_EXPECT(jrr[jss::node][sfFlags.jsonName] == 1); BEAST_EXPECT(jrr[jss::node][sfFlags.jsonName] == 1);
@@ -1260,7 +1263,7 @@ class LedgerRPC_test : public beast::unit_test::suite
{ {
// Request an index that is not a uritoken. // Request an index that is not a uritoken.
Json::Value jvParams; Json::Value jvParams;
jvParams[jss::URIToken] = ledgerHash; jvParams[jss::uri_token] = ledgerHash;
jvParams[jss::ledger_hash] = ledgerHash; jvParams[jss::ledger_hash] = ledgerHash;
Json::Value const jrr = env.rpc( Json::Value const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result]; "json", "ledger_entry", to_string(jvParams))[jss::result];