#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace xrpl { /** Type-specific prefix for calculating ledger indices. The identifier for a given object within the ledger is calculated based on some object-specific parameters. To ensure that different types of objects have different indices, even if they happen to use the same set of parameters, we use "tagged hashing" by adding a type-specific prefix. @note These values are part of the protocol and *CANNOT* be arbitrarily changed. If they were, on-ledger objects may no longer be able to be located or addressed. Additions to this list are OK, but changing existing entries to assign them a different values should never be needed. Entries that are removed should be moved to the bottom of the enum and marked as [[deprecated]] to prevent accidental reuse. */ enum class LedgerNameSpace : std::uint16_t { ACCOUNT = 'a', DIR_NODE = 'd', TRUST_LINE = 'r', OFFER = 'o', OWNER_DIR = 'O', BOOK_DIR = 'B', SKIP_LIST = 's', ESCROW = 'u', AMENDMENTS = 'f', FEE_SETTINGS = 'e', TICKET = 'T', SIGNER_LIST = 'S', XRP_PAYMENT_CHANNEL = 'x', CHECK = 'C', DEPOSIT_PREAUTH = 'p', DEPOSIT_PREAUTH_CREDENTIALS = 'P', NEGATIVE_UNL = 'N', NFTOKEN_OFFER = 'q', NFTOKEN_BUY_OFFERS = 'h', NFTOKEN_SELL_OFFERS = 'i', AMM = 'A', BRIDGE = 'H', XCHAIN_CLAIM_ID = 'Q', XCHAIN_CREATE_ACCOUNT_CLAIM_ID = 'K', DID = 'I', ORACLE = 'R', MPTOKEN_ISSUANCE = '~', MPTOKEN = 't', CREDENTIAL = 'D', PERMISSIONED_DOMAIN = 'm', DELEGATE = 'E', VAULT = 'V', LOAN_BROKER = 'l', // lower-case L LOAN = 'L', // No longer used or supported. Left here to reserve the space // to avoid accidental reuse. CONTRACT [[deprecated]] = 'c', GENERATOR [[deprecated]] = 'g', NICKNAME [[deprecated]] = 'n', }; template static uint256 indexHash(LedgerNameSpace space, Args const&... args) { return sha512Half(safe_cast(space), args...); } uint256 getBookBase(Book const& book) { XRPL_ASSERT(isConsistent(book), "xrpl::getBookBase : input is consistent"); auto const index = book.domain ? indexHash( LedgerNameSpace::BOOK_DIR, book.in.currency, book.out.currency, book.in.account, book.out.account, *(book.domain)) : indexHash( LedgerNameSpace::BOOK_DIR, book.in.currency, book.out.currency, book.in.account, book.out.account); // Return with quality 0. auto k = keylet::quality({ltDIR_NODE, index}, 0); return k.key; } uint256 getQualityNext(uint256 const& uBase) { static constexpr uint256 nextQuality( "0000000000000000000000000000000000000000000000010000000000000000"); return uBase + nextQuality; } std::uint64_t getQuality(uint256 const& uBase) { // VFALCO [base_uint] This assumes a certain storage format return boost::endian::big_to_native(((std::uint64_t*)uBase.end())[-1]); } uint256 getTicketIndex(AccountID const& account, std::uint32_t ticketSeq) { return indexHash(LedgerNameSpace::TICKET, account, std::uint32_t(ticketSeq)); } uint256 getTicketIndex(AccountID const& account, SeqProxy ticketSeq) { XRPL_ASSERT(ticketSeq.isTicket(), "xrpl::getTicketIndex : valid input"); return getTicketIndex(account, ticketSeq.value()); } MPTID makeMptID(std::uint32_t sequence, AccountID const& account) { MPTID u; sequence = boost::endian::native_to_big(sequence); memcpy(u.data(), &sequence, sizeof(sequence)); memcpy(u.data() + sizeof(sequence), account.data(), sizeof(account)); return u; } //------------------------------------------------------------------------------ namespace keylet { Keylet account(AccountID const& id) noexcept { return Keylet{ltACCOUNT_ROOT, indexHash(LedgerNameSpace::ACCOUNT, id)}; } Keylet child(uint256 const& key) noexcept { return {ltCHILD, key}; } Keylet const& skip() noexcept { static Keylet const ret{ltLEDGER_HASHES, indexHash(LedgerNameSpace::SKIP_LIST)}; return ret; } Keylet skip(LedgerIndex ledger) noexcept { return { ltLEDGER_HASHES, indexHash( LedgerNameSpace::SKIP_LIST, std::uint32_t(static_cast(ledger) >> 16))}; } Keylet const& amendments() noexcept { static Keylet const ret{ltAMENDMENTS, indexHash(LedgerNameSpace::AMENDMENTS)}; return ret; } Keylet const& fees() noexcept { static Keylet const ret{ltFEE_SETTINGS, indexHash(LedgerNameSpace::FEE_SETTINGS)}; return ret; } Keylet const& negativeUNL() noexcept { static Keylet const ret{ltNEGATIVE_UNL, indexHash(LedgerNameSpace::NEGATIVE_UNL)}; return ret; } Keylet book_t::operator()(Book const& b) const { return {ltDIR_NODE, getBookBase(b)}; } Keylet line(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept { // There is code in SetTrust that calls us with id0 == id1, to allow users // to locate and delete such "weird" trustlines. If we remove that code, we // could enable this assert: // XRPL_ASSERT(id0 != id1, "xrpl::keylet::line : accounts must be // different"); // A trust line is shared between two accounts; while we typically think // of this as an "issuer" and a "holder" the relationship is actually fully // bidirectional. // // So that we can generate a unique ID for a trust line, regardess of which // side of the line we're looking at, we define a "canonical" order for the // two accounts (smallest then largest) and hash them in that order: auto const accounts = std::minmax(id0, id1); return { ltRIPPLE_STATE, indexHash(LedgerNameSpace::TRUST_LINE, accounts.first, accounts.second, currency)}; } Keylet offer(AccountID const& id, std::uint32_t seq) noexcept { return {ltOFFER, indexHash(LedgerNameSpace::OFFER, id, seq)}; } Keylet quality(Keylet const& k, std::uint64_t q) noexcept { XRPL_ASSERT(k.type == ltDIR_NODE, "xrpl::keylet::quality : valid input type"); // Indexes are stored in big endian format: they print as hex as stored. // Most significant bytes are first and the least significant bytes // represent adjacent entries. We place the quality, in big endian format, // in the 8 right most bytes; this way, incrementing goes to the next entry // for indexes. uint256 x = k.key; // FIXME This is ugly and we can and should do better... ((std::uint64_t*)x.end())[-1] = boost::endian::native_to_big(q); return {ltDIR_NODE, x}; } Keylet next_t::operator()(Keylet const& k) const { XRPL_ASSERT(k.type == ltDIR_NODE, "xrpl::keylet::next_t::operator() : valid input type"); return {ltDIR_NODE, getQualityNext(k.key)}; } Keylet ticket_t::operator()(AccountID const& id, std::uint32_t ticketSeq) const { return {ltTICKET, getTicketIndex(id, ticketSeq)}; } Keylet ticket_t::operator()(AccountID const& id, SeqProxy ticketSeq) const { return {ltTICKET, getTicketIndex(id, ticketSeq)}; } // This function is presently static, since it's never accessed from anywhere // else. If we ever support multiple pages of signer lists, this would be the // keylet used to locate them. static Keylet signers(AccountID const& account, std::uint32_t page) noexcept { return {ltSIGNER_LIST, indexHash(LedgerNameSpace::SIGNER_LIST, account, page)}; } Keylet signers(AccountID const& account) noexcept { return signers(account, 0); } Keylet check(AccountID const& id, std::uint32_t seq) noexcept { return {ltCHECK, indexHash(LedgerNameSpace::CHECK, id, seq)}; } Keylet depositPreauth(AccountID const& owner, AccountID const& preauthorized) noexcept { return {ltDEPOSIT_PREAUTH, indexHash(LedgerNameSpace::DEPOSIT_PREAUTH, owner, preauthorized)}; } // Credentials should be sorted here, use credentials::makeSorted Keylet depositPreauth( AccountID const& owner, std::set> const& authCreds) noexcept { std::vector hashes; hashes.reserve(authCreds.size()); for (auto const& o : authCreds) hashes.emplace_back(sha512Half(o.first, o.second)); return { ltDEPOSIT_PREAUTH, indexHash(LedgerNameSpace::DEPOSIT_PREAUTH_CREDENTIALS, owner, hashes)}; } //------------------------------------------------------------------------------ Keylet unchecked(uint256 const& key) noexcept { return {ltANY, key}; } Keylet ownerDir(AccountID const& id) noexcept { return {ltDIR_NODE, indexHash(LedgerNameSpace::OWNER_DIR, id)}; } Keylet page(uint256 const& key, std::uint64_t index) noexcept { if (index == 0) return {ltDIR_NODE, key}; return {ltDIR_NODE, indexHash(LedgerNameSpace::DIR_NODE, key, index)}; } Keylet escrow(AccountID const& src, std::uint32_t seq) noexcept { return {ltESCROW, indexHash(LedgerNameSpace::ESCROW, src, seq)}; } Keylet payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept { return {ltPAYCHAN, indexHash(LedgerNameSpace::XRP_PAYMENT_CHANNEL, src, dst, seq)}; } Keylet nftpage_min(AccountID const& owner) { std::array buf{}; std::memcpy(buf.data(), owner.data(), owner.size()); return {ltNFTOKEN_PAGE, uint256{buf}}; } Keylet nftpage_max(AccountID const& owner) { uint256 id = nft::pageMask; std::memcpy(id.data(), owner.data(), owner.size()); return {ltNFTOKEN_PAGE, id}; } Keylet nftpage(Keylet const& k, uint256 const& token) { XRPL_ASSERT(k.type == ltNFTOKEN_PAGE, "xrpl::keylet::nftpage : valid input type"); return {ltNFTOKEN_PAGE, (k.key & ~nft::pageMask) + (token & nft::pageMask)}; } Keylet nftoffer(AccountID const& owner, std::uint32_t seq) { return {ltNFTOKEN_OFFER, indexHash(LedgerNameSpace::NFTOKEN_OFFER, owner, seq)}; } Keylet nft_buys(uint256 const& id) noexcept { return {ltDIR_NODE, indexHash(LedgerNameSpace::NFTOKEN_BUY_OFFERS, id)}; } Keylet nft_sells(uint256 const& id) noexcept { return {ltDIR_NODE, indexHash(LedgerNameSpace::NFTOKEN_SELL_OFFERS, id)}; } Keylet amm(Asset const& issue1, Asset const& issue2) noexcept { auto const& [minI, maxI] = std::minmax(issue1.get(), issue2.get()); return amm( indexHash(LedgerNameSpace::AMM, minI.account, minI.currency, maxI.account, maxI.currency)); } Keylet amm(uint256 const& id) noexcept { return {ltAMM, id}; } Keylet delegate(AccountID const& account, AccountID const& authorizedAccount) noexcept { return {ltDELEGATE, indexHash(LedgerNameSpace::DELEGATE, account, authorizedAccount)}; } Keylet bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType) { // A door account can support multiple bridges. On the locking chain // there can only be one bridge per lockingChainCurrency. On the issuing // chain there can only be one bridge per issuingChainCurrency. auto const& issue = bridge.issue(chainType); return {ltBRIDGE, indexHash(LedgerNameSpace::BRIDGE, bridge.door(chainType), issue.currency)}; } Keylet xChainClaimID(STXChainBridge const& bridge, std::uint64_t seq) { return { ltXCHAIN_OWNED_CLAIM_ID, indexHash( LedgerNameSpace::XCHAIN_CLAIM_ID, bridge.lockingChainDoor(), bridge.lockingChainIssue(), bridge.issuingChainDoor(), bridge.issuingChainIssue(), seq)}; } Keylet xChainCreateAccountClaimID(STXChainBridge const& bridge, std::uint64_t seq) { return { ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID, indexHash( LedgerNameSpace::XCHAIN_CREATE_ACCOUNT_CLAIM_ID, bridge.lockingChainDoor(), bridge.lockingChainIssue(), bridge.issuingChainDoor(), bridge.issuingChainIssue(), seq)}; } Keylet did(AccountID const& account) noexcept { return {ltDID, indexHash(LedgerNameSpace::DID, account)}; } Keylet oracle(AccountID const& account, std::uint32_t const& documentID) noexcept { return {ltORACLE, indexHash(LedgerNameSpace::ORACLE, account, documentID)}; } Keylet mptIssuance(std::uint32_t seq, AccountID const& issuer) noexcept { return mptIssuance(makeMptID(seq, issuer)); } Keylet mptIssuance(MPTID const& issuanceID) noexcept { return {ltMPTOKEN_ISSUANCE, indexHash(LedgerNameSpace::MPTOKEN_ISSUANCE, issuanceID)}; } Keylet mptoken(MPTID const& issuanceID, AccountID const& holder) noexcept { return mptoken(mptIssuance(issuanceID).key, holder); } Keylet mptoken(uint256 const& issuanceKey, AccountID const& holder) noexcept { return {ltMPTOKEN, indexHash(LedgerNameSpace::MPTOKEN, issuanceKey, holder)}; } Keylet credential(AccountID const& subject, AccountID const& issuer, Slice const& credType) noexcept { return {ltCREDENTIAL, indexHash(LedgerNameSpace::CREDENTIAL, subject, issuer, credType)}; } Keylet vault(AccountID const& owner, std::uint32_t seq) noexcept { return vault(indexHash(LedgerNameSpace::VAULT, owner, seq)); } Keylet loanbroker(AccountID const& owner, std::uint32_t seq) noexcept { return loanbroker(indexHash(LedgerNameSpace::LOAN_BROKER, owner, seq)); } Keylet loan(uint256 const& loanBrokerID, std::uint32_t loanSeq) noexcept { return loan(indexHash(LedgerNameSpace::LOAN, loanBrokerID, loanSeq)); } Keylet permissionedDomain(AccountID const& account, std::uint32_t seq) noexcept { return {ltPERMISSIONED_DOMAIN, indexHash(LedgerNameSpace::PERMISSIONED_DOMAIN, account, seq)}; } Keylet permissionedDomain(uint256 const& domainID) noexcept { return {ltPERMISSIONED_DOMAIN, domainID}; } } // namespace keylet } // namespace xrpl