Merge remote-tracking branch 'Transia-RnD-rippled/feature-p256' into develop

# Conflicts:
#	include/xrpl/protocol/Indexes.h
#	include/xrpl/protocol/KeyType.h
#	include/xrpl/protocol/PublicKey.h
#	src/libxrpl/protocol/Indexes.cpp
#	src/libxrpl/protocol/PublicKey.cpp
#	src/libxrpl/protocol/SecretKey.cpp
This commit is contained in:
Denis Angell
2026-09-13 19:25:42 -04:00
26 changed files with 1943 additions and 29 deletions

View File

@@ -82,4 +82,10 @@ base64Encode(std::string_view s)
std::string
base64Decode(std::string_view data);
/** Decode a base64url-encoded string (RFC 4648 S5).
Converts '-' to '+' and '_' to '/', adds padding, then decodes.
*/
std::string
base64urlDecode(std::string_view data);
} // namespace xrpl

View File

@@ -408,6 +408,8 @@ contract(uint256 const& contractID)
Keylet
contractData(AccountID const& owner, AccountID const& contractAccount) noexcept;
Keylet
passkeyList(AccountID const& account) noexcept;
} // namespace keylet
// Everything below is deprecated and should be removed in favor of keylets:

View File

@@ -9,6 +9,7 @@ enum class KeyType {
Secp256k1 = 0,
Ed25519 = 1,
Dilithium = 2,
P256 = 3,
};
inline std::optional<KeyType>
@@ -23,6 +24,9 @@ keyTypeFromString(std::string const& s)
if (s == "dilithium")
return KeyType::Dilithium;
if (s == "p256")
return KeyType::P256;
return {};
}
@@ -38,6 +42,9 @@ to_string(KeyType type)
if (type == KeyType::Dilithium)
return "dilithium";
if (type == KeyType::P256)
return "p256";
return "INVALID";
}

View File

@@ -41,6 +41,7 @@ namespace xrpl {
* secp256k1
* ed25519
* dilithium
* p256
*
* secp256k1 public keys consist of a 33 byte
* compressed public key, with the lead byte equal
@@ -57,9 +58,10 @@ class PublicKey
protected:
// Minimum / standard public key size (secp256k1, ed25519).
static constexpr std::size_t kSize = 33;
// Buffer sized for the largest supported key (dilithium = 1312 bytes).
// Actual length is tracked in size_.
std::uint8_t buf_[1312]{};
// Buffer sized for the largest supported key (dilithium = 1312 bytes;
// uncompressed p256 = 65 bytes). Actual length is tracked in size_.
static constexpr std::size_t kMaxSize = 1312;
std::uint8_t buf_[kMaxSize]{};
std::size_t size_ = 0;
public:
@@ -137,7 +139,7 @@ operator<<(std::ostream& os, PublicKey const& pk);
inline bool
operator==(PublicKey const& lhs, PublicKey const& rhs)
{
return std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0;
return lhs.size() == rhs.size() && std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0;
}
inline bool

View File

@@ -151,3 +151,4 @@ XRPL_FIX (Cleanup3_5_0, Supported::Yes, VoteBehavior::DefaultN
XRPL_FEATURE(ConfidentialMPTKeyRotation, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(TokenPaychan, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(Quantum, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(Passkey, Supported::Yes, VoteBehavior::DefaultNo)

View File

@@ -693,6 +693,13 @@ LEDGER_ENTRY(ltSPONSORSHIP, 0x0090, Sponsorship, sponsorship, ({
{sfOwnerNode, SoeRequired},
{sfSponseeNode, SoeRequired},
}))
LEDGER_ENTRY(ltPASSKEY_LIST, 0x0091, PasskeyList, passkey_list, ({
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfOwnerNode, SoeRequired},
{sfOwner, SoeRequired},
{sfPasskeys, SoeRequired},
}))
#undef EXPAND
#undef LEDGER_ENTRY_DUPLICATE

View File

@@ -471,3 +471,10 @@ UNTYPED_SFIELD(sfParameters, ARRAY, 35)
TYPED_SFIELD(sfParameterValue, DATA, 1, SField::kSmdDefault)
TYPED_SFIELD(sfParameterType, DATATYPE, 1)
TYPED_SFIELD(sfContractJson, JSON, 1)
TYPED_SFIELD(sfSignCount, UINT32, 87)
TYPED_SFIELD(sfPasskeyID, VL, 50)
TYPED_SFIELD(sfAuthenticatorData, VL, 51)
TYPED_SFIELD(sfClientDataJSON, VL, 52)
UNTYPED_SFIELD(sfPasskeySignature, OBJECT, 43, SField::kSmdDefault, SField::kNotSigning)
UNTYPED_SFIELD(sfPasskey, OBJECT, 44)
UNTYPED_SFIELD(sfPasskeys, ARRAY, 36)

View File

@@ -1310,3 +1310,11 @@ TRANSACTION(ttUNL_MODIFY, 102, UNLModify,
{sfLedgerSequence, SoeRequired},
{sfUNLModifyValidator, SoeRequired},
}))
TRANSACTION(ttPASSKEY_LIST_SET, 103, PasskeyListSet,
({
.delegable = Delegation::Delegable,
.amendment = featurePasskey,
}),
({
{sfPasskeys, SoeRequired},
}))

View File

@@ -237,4 +237,14 @@ sha512HalfS(Args const&... args)
return static_cast<sha512_half_hasher_s::result_type>(h);
}
template <class... Args>
sha256_hasher::result_type
sha256(Args const&... args)
{
xrpl::sha256_hasher h;
using beast::hash_append;
hash_append(h, args...);
return static_cast<typename sha256_hasher::result_type>(h);
}
} // namespace xrpl

View File

@@ -0,0 +1,218 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
class PasskeyListBuilder;
/**
* @brief Ledger Entry: PasskeyList
*
* Type: ltPASSKEY_LIST (0x0091)
* RPC Name: passkey_list
*
* Immutable wrapper around SLE providing type-safe field access.
* Use PasskeyListBuilder to construct new ledger entries.
*/
class PasskeyList : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltPASSKEY_LIST;
/**
* @brief Construct a PasskeyList ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit PasskeyList(SLE::const_pointer sle)
: LedgerEntryBase(std::move(sle))
{
// Verify ledger entry type
if (sle_->getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for PasskeyList");
}
}
// Ledger entry-specific field getters
/**
* @brief Get sfPreviousTxnID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_->at(sfPreviousTxnID);
}
/**
* @brief Get sfPreviousTxnLgrSeq (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_->at(sfPreviousTxnLgrSeq);
}
/**
* @brief Get sfOwnerNode (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_->at(sfOwnerNode);
}
/**
* @brief Get sfOwner (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getOwner() const
{
return this->sle_->at(sfOwner);
}
/**
* @brief Get sfPasskeys (SoeRequired)
* @note This is an untyped field (unknown).
* @return The field value.
*/
[[nodiscard]]
STArray const&
getPasskeys() const
{
return this->sle_->getFieldArray(sfPasskeys);
}
};
/**
* @brief Builder for PasskeyList ledger entries.
*
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses STObject internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class PasskeyListBuilder : public LedgerEntryBuilderBase<PasskeyListBuilder>
{
public:
/**
* @brief Construct a new PasskeyListBuilder with required fields.
* @param previousTxnID The sfPreviousTxnID field value.
* @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value.
* @param ownerNode The sfOwnerNode field value.
* @param owner The sfOwner field value.
* @param passkeys The sfPasskeys field value.
*/
PasskeyListBuilder(std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_ACCOUNT::type::value_type> const& owner,STArray const& passkeys)
: LedgerEntryBuilderBase<PasskeyListBuilder>(ltPASSKEY_LIST)
{
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
setOwnerNode(ownerNode);
setOwner(owner);
setPasskeys(passkeys);
}
/**
* @brief Construct a PasskeyListBuilder from an existing SLE object.
* @param sle The existing ledger entry to copy from.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
PasskeyListBuilder(SLE::const_pointer sle)
{
if (sle->at(sfLedgerEntryType) != ltPASSKEY_LIST)
{
throw std::runtime_error("Invalid ledger entry type for PasskeyList");
}
object_ = *sle;
}
/**
* @brief Ledger entry-specific field setters
*/
/**
* @brief Set sfPreviousTxnID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
PasskeyListBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* @brief Set sfPreviousTxnLgrSeq (SoeRequired)
* @return Reference to this builder for method chaining.
*/
PasskeyListBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* @brief Set sfOwnerNode (SoeRequired)
* @return Reference to this builder for method chaining.
*/
PasskeyListBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* @brief Set sfOwner (SoeRequired)
* @return Reference to this builder for method chaining.
*/
PasskeyListBuilder&
setOwner(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOwner] = value;
return *this;
}
/**
* @brief Set sfPasskeys (SoeRequired)
* @return Reference to this builder for method chaining.
*/
PasskeyListBuilder&
setPasskeys(STArray const& value)
{
object_.setFieldArray(sfPasskeys, value);
return *this;
}
/**
* @brief Build and return the completed PasskeyList wrapper.
* @param index The ledger entry index.
* @return The constructed ledger entry wrapper.
*/
PasskeyList
build(uint256 const& index)
{
return PasskeyList{std::make_shared<SLE>(std::move(object_), index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -0,0 +1,131 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class PasskeyListSetBuilder;
/**
* @brief Transaction: PasskeyListSet
*
* Type: ttPASSKEY_LIST_SET (92)
* Delegable: Delegation::Delegable
* Amendment: featurePasskey
* Privileges: Privilege::NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use PasskeyListSetBuilder to construct new transactions.
*/
class PasskeyListSet : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttPASSKEY_LIST_SET;
/**
* @brief Construct a PasskeyListSet transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit PasskeyListSet(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for PasskeyListSet");
}
}
// Transaction-specific field getters
/**
* @brief Get sfPasskeys (SoeRequired)
* @note This is an untyped field.
* @return The field value.
*/
[[nodiscard]]
STArray const&
getPasskeys() const
{
return this->tx_->getFieldArray(sfPasskeys);
}
};
/**
* @brief Builder for PasskeyListSet transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class PasskeyListSetBuilder : public TransactionBuilderBase<PasskeyListSetBuilder>
{
public:
/**
* @brief Construct a new PasskeyListSetBuilder with required fields.
* @param account The account initiating the transaction.
* @param passkeys The sfPasskeys field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
PasskeyListSetBuilder(SF_ACCOUNT::type::value_type account,
STArray const& passkeys, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<PasskeyListSetBuilder>(ttPASSKEY_LIST_SET, account, sequence, fee)
{
setPasskeys(passkeys);
}
/**
* @brief Construct a PasskeyListSetBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
PasskeyListSetBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttPASSKEY_LIST_SET)
{
throw std::runtime_error("Invalid transaction type for PasskeyListSetBuilder");
}
object_ = *tx;
}
/**
* @brief Transaction-specific field setters
*/
/**
* @brief Set sfPasskeys (SoeRequired)
* @return Reference to this builder for method chaining.
*/
PasskeyListSetBuilder&
setPasskeys(STArray const& value)
{
object_.setFieldArray(sfPasskeys, value);
return *this;
}
/**
* @brief Build and return the PasskeyListSet wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
PasskeyListSet
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return PasskeyListSet{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -0,0 +1,42 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/Transactor.h>
namespace xrpl {
class SetPasskeyList : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Blocker;
explicit SetPasskeyList(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
using PasskeyListSet = SetPasskeyList;
} // namespace xrpl