Refactor AccountID (RIPD-953):

All AccountID functionality is removed from RippleAddress and
replaced with free functions. The AccountID to string conversion
cache is factored out as an explicit type with an instance in
the Application object. New base58 conversion functions are used,
with no dependence on OpenSSL.

All types and free functions related to AccountID are consolidated
into one header file. Routines to operate on "tokens" are also
introduced and consolidated into a single header file.

A token one of the cryptographic primitives used in Ripple:

    Secret Seed
    Server Public Key
    Server Secret Key
    Account ID
    Account Public Key
    Account Private Key

    and these deprecated primitives:

    Account Family Seed
    Account Family Generator
This commit is contained in:
Vinnie Falco
2015-06-18 11:05:18 -07:00
parent 63d438c979
commit 2f485672fa
109 changed files with 1901 additions and 1545 deletions

View File

@@ -0,0 +1,239 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/protocol/AccountID.h>
#include <ripple/protocol/AnyPublicKey.h>
#include <ripple/protocol/digest.h>
#include <ripple/protocol/tokens.h>
#include <cstring>
namespace ripple {
std::string
toBase58 (AccountID const& v)
{
return base58EncodeToken(
TOKEN_ACCOUNT_ID,
v.data(), v.size());
}
template<>
boost::optional<AccountID>
parseBase58 (std::string const& s)
{
auto const result =
decodeBase58Token(
s, TOKEN_ACCOUNT_ID);
if (result.empty())
return boost::none;
AccountID id;
if (result.size() != id.size())
return boost::none;
std::memcpy(id.data(),
result.data(), result.size());
return id;
}
boost::optional<AccountID>
deprecatedParseBitcoinAccountID (std::string const& s)
{
auto const result =
decodeBase58TokenBitcoin(
s, TOKEN_ACCOUNT_ID);
if (result.empty())
return boost::none;
AccountID id;
if (result.size() != id.size())
return boost::none;
std::memcpy(id.data(),
result.data(), result.size());
return id;
}
bool
deprecatedParseBase58 (AccountID& account,
Json::Value const& jv)
{
if (! jv.isString())
return false;
auto const result =
parseBase58<AccountID>(jv.asString());
if (! result)
return false;
account = *result;
return true;
}
template<>
boost::optional<AccountID>
parseHex (std::string const& s)
{
if (s.size() != 40)
return boost::none;
AccountID id;
if (! id.SetHex(s, true))
return boost::none;
return id;
}
template<>
boost::optional<AccountID>
parseHexOrBase58 (std::string const& s)
{
auto result =
parseHex<AccountID>(s);
if (! result)
result = parseBase58<AccountID>(s);
return result;
}
//------------------------------------------------------------------------------
/*
Calculation of the Account ID
The AccountID is a 160-bit identifier that uniquely
distinguishes an account. The account may or may not
exist in the ledger. Even for accounts that are not in
the ledger, cryptographic operations may be performed
which affect the ledger. For example, designating an
account not in the ledger as a regular key for an
account that is in the ledger.
Why did we use half of SHA512 for most things but then
SHA256 followed by RIPEMD160 for account IDs? Why didn't
we do SHA512 half then RIPEMD160? Or even SHA512 then RIPEMD160?
For that matter why RIPEMD160 at all why not just SHA512 and keep
only 160 bits?
Answer (David Schwartz):
The short answer is that we kept Bitcoin's behavior.
The longer answer was that:
1) Using a single hash could leave ripple
vulnerable to length extension attacks.
2) Only RIPEMD160 is generally considered safe at 160 bits.
Any of those schemes would have been acceptable. However,
the one chosen avoids any need to defend the scheme chosen.
(Against any criticism other than unnecessary complexity.)
"The historical reason was that in the very early days,
we wanted to give people as few ways to argue that we were
less secure than Bitcoin. So where there was no good reason
to change something, it was not changed."
*/
AccountID
calcAccountID (AnyPublicKey const& pk)
{
ripesha_hasher rsh;
rsh(pk.data(), pk.size());
auto const d = static_cast<
ripesha_hasher::result_type>(rsh);
AccountID id;
static_assert(sizeof(d) == sizeof(id), "");
std::memcpy(id.data(), d.data(), d.size());
return id;
}
AccountID const&
xrpAccount()
{
static AccountID const account(0);
return account;
}
AccountID const&
noAccount()
{
static AccountID const account(1);
return account;
}
bool
to_issuer (AccountID& issuer, std::string const& s)
{
if (s.size () == (160 / 4))
{
issuer.SetHex (s);
return true;
}
auto const account =
parseBase58<AccountID>(s);
if (! account)
return false;
issuer = *account;
return true;
}
//------------------------------------------------------------------------------
/* VFALCO NOTE
An alternate implementation could use a pair of insert-only
hash maps that each use a single large memory allocation
to store a fixed size hash table and all of the AccountID/string
pairs laid out in memory (wouldn't use std::string here just a
length prefixed or zero terminated array). Possibly using
boost::intrusive as the basis for the unordered container.
This would cut down to one allocate/free cycle per swap of
the map.
*/
AccountIDCache::AccountIDCache(
std::size_t capacity)
: capacity_(capacity)
{
m1_.reserve(capacity_);
}
std::string
AccountIDCache::toBase58(
AccountID const& id) const
{
std::lock_guard<
std::mutex> lock(mutex_);
auto iter = m1_.find(id);
if (iter != m1_.end())
return iter->second;
iter = m0_.find(id);
std::string result;
if (iter != m0_.end())
{
result = iter->second;
// Can use insert-only hash maps if
// we didn't erase from here.
m0_.erase(iter);
}
else
{
result =
ripple::toBase58(id);
}
if (m1_.size() >= capacity_)
{
m0_ = std::move(m1_);
m1_.clear();
m1_.reserve(capacity_);
}
m1_.emplace(id, result);
return result;
}
} // ripple

View File

@@ -95,7 +95,7 @@ toString (AnyPublicKey const& pk)
{
Blob buffer;
buffer.reserve (1 + pk.size ());
buffer.push_back (VER_NODE_PUBLIC);
buffer.push_back (TOKEN_NODE_PUBLIC);
auto const data = pk.data ();
buffer.insert (buffer.end (), data, data + pk.size ());
return Base58::encodeWithCheck (buffer);

View File

@@ -64,12 +64,6 @@ getAccountRootIndex (AccountID const& account)
account);
}
uint256
getAccountRootIndex (const RippleAddress & account)
{
return getAccountRootIndex (account.getAccountID ());
}
uint256
getGeneratorIndex (AccountID const& uGeneratorID)
{
@@ -203,13 +197,6 @@ Keylet account_t::operator()(
getAccountRootIndex(id) };
}
Keylet account_t::operator()(
RippleAddress const& ra) const
{
return { ltACCOUNT_ROOT,
getAccountRootIndex(ra.getAccountID()) };
}
Keylet child (uint256 const& key)
{
return { ltCHILD, key };

View File

@@ -26,10 +26,12 @@
#include <ripple/crypto/GenerateDeterministicKey.h>
#include <ripple/crypto/RandomNumbers.h>
#include <ripple/crypto/RFC1751.h>
#include <ripple/protocol/digest.h>
#include <ripple/protocol/JsonFields.h>
#include <ripple/protocol/RippleAddress.h>
#include <ripple/protocol/Serializer.h>
#include <ripple/protocol/RipplePublicKey.h>
#include <ripple/protocol/types.h>
#include <beast/unit_test/suite.h>
#include <ed25519-donna/ed25519.h>
#include <openssl/ripemd.h>
@@ -83,19 +85,19 @@ bool verifySignature (Blob const& pubkey, uint256 const& hash, Blob const& sig,
RippleAddress::RippleAddress ()
: mIsValid (false)
{
nVersion = VER_NONE;
nVersion = TOKEN_NONE;
}
void RippleAddress::clear ()
{
nVersion = VER_NONE;
nVersion = TOKEN_NONE;
vchData.clear ();
mIsValid = false;
}
bool RippleAddress::isSet () const
{
return nVersion != VER_NONE;
return nVersion != TOKEN_NONE;
}
//
@@ -145,7 +147,7 @@ RippleAddress RippleAddress::createNodePublic (std::string const& strPublic)
RipplePublicKey
RippleAddress::toPublicKey() const
{
assert (nVersion == VER_NODE_PUBLIC);
assert (nVersion == TOKEN_NODE_PUBLIC);
return RipplePublicKey (vchData.begin(), vchData.end());
}
@@ -159,10 +161,10 @@ NodeID RippleAddress::getNodeID () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - getNodeID");
case VER_NODE_PUBLIC: {
case TOKEN_NODE_PUBLIC: {
// Note, we are encoding the left.
NodeID node;
node.copyFrom(Hash160 (vchData));
@@ -178,10 +180,10 @@ Blob const& RippleAddress::getNodePublic () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - getNodePublic");
case VER_NODE_PUBLIC:
case TOKEN_NODE_PUBLIC:
return vchData;
default:
@@ -193,10 +195,10 @@ std::string RippleAddress::humanNodePublic () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - humanNodePublic");
case VER_NODE_PUBLIC:
case TOKEN_NODE_PUBLIC:
return ToString ();
default:
@@ -207,7 +209,7 @@ std::string RippleAddress::humanNodePublic () const
bool RippleAddress::setNodePublic (std::string const& strPublic)
{
mIsValid = SetString (
strPublic, VER_NODE_PUBLIC, Base58::getRippleAlphabet ());
strPublic, TOKEN_NODE_PUBLIC, Base58::getRippleAlphabet ());
return mIsValid;
}
@@ -216,7 +218,7 @@ void RippleAddress::setNodePublic (Blob const& vPublic)
{
mIsValid = true;
SetData (VER_NODE_PUBLIC, vPublic);
SetData (TOKEN_NODE_PUBLIC, vPublic);
}
bool RippleAddress::verifyNodePublic (
@@ -250,10 +252,10 @@ Blob const& RippleAddress::getNodePrivateData () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - getNodePrivateData");
case VER_NODE_PRIVATE:
case TOKEN_NODE_PRIVATE:
return vchData;
default:
@@ -265,10 +267,10 @@ uint256 RippleAddress::getNodePrivate () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source = getNodePrivate");
case VER_NODE_PRIVATE:
case TOKEN_NODE_PRIVATE:
return uint256 (vchData);
default:
@@ -280,10 +282,10 @@ std::string RippleAddress::humanNodePrivate () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - humanNodePrivate");
case VER_NODE_PRIVATE:
case TOKEN_NODE_PRIVATE:
return ToString ();
default:
@@ -294,7 +296,7 @@ std::string RippleAddress::humanNodePrivate () const
bool RippleAddress::setNodePrivate (std::string const& strPrivate)
{
mIsValid = SetString (
strPrivate, VER_NODE_PRIVATE, Base58::getRippleAlphabet ());
strPrivate, TOKEN_NODE_PRIVATE, Base58::getRippleAlphabet ());
return mIsValid;
}
@@ -303,14 +305,14 @@ void RippleAddress::setNodePrivate (Blob const& vPrivate)
{
mIsValid = true;
SetData (VER_NODE_PRIVATE, vPrivate);
SetData (TOKEN_NODE_PRIVATE, vPrivate);
}
void RippleAddress::setNodePrivate (uint256 hash256)
{
mIsValid = true;
SetData (VER_NODE_PRIVATE, hash256);
SetData (TOKEN_NODE_PRIVATE, hash256);
}
void RippleAddress::signNodePrivate (uint256 const& hash, Blob& vchSig) const
@@ -321,131 +323,6 @@ void RippleAddress::signNodePrivate (uint256 const& hash, Blob& vchSig) const
throw std::runtime_error ("Signing failed.");
}
//
// AccountID
//
AccountID RippleAddress::getAccountID () const
{
switch (nVersion)
{
case VER_NONE:
throw std::runtime_error ("unset source - getAccountID");
case VER_ACCOUNT_ID:
return AccountID(vchData);
case VER_ACCOUNT_PUBLIC: {
// Note, we are encoding the left.
// TODO(tom): decipher this comment.
AccountID account;
account.copyFrom (Hash160 (vchData));
return account;
}
default:
throw badSourceError (nVersion);
}
}
using StaticLockType = std::mutex;
using StaticScopedLockType = std::lock_guard <StaticLockType>;
static StaticLockType s_lock;
static hash_map <Blob, std::string> rncMapOld, rncMapNew;
void RippleAddress::clearCache ()
{
StaticScopedLockType sl (s_lock);
rncMapOld.clear ();
rncMapNew.clear ();
}
std::string RippleAddress::humanAccountID () const
{
switch (nVersion)
{
case VER_NONE:
throw std::runtime_error ("unset source - humanAccountID");
case VER_ACCOUNT_ID:
{
std::string ret;
{
StaticScopedLockType sl (s_lock);
auto it = rncMapNew.find (vchData);
if (it != rncMapNew.end ())
{
// Found in new map, nothing to do
ret = it->second;
}
else
{
it = rncMapOld.find (vchData);
if (it != rncMapOld.end ())
{
ret = it->second;
rncMapOld.erase (it);
}
else
ret = ToString ();
if (rncMapNew.size () >= 128000)
{
rncMapOld = std::move (rncMapNew);
rncMapNew.clear ();
rncMapNew.reserve (128000);
}
rncMapNew[vchData] = ret;
}
}
return ret;
}
case VER_ACCOUNT_PUBLIC:
{
RippleAddress accountID;
(void) accountID.setAccountID (getAccountID ());
return accountID.ToString ();
}
default:
throw badSourceError (nVersion);
}
}
bool RippleAddress::setAccountID (
std::string const& strAccountID, Base58::Alphabet const& alphabet)
{
if (strAccountID.empty ())
{
setAccountID (AccountID ());
mIsValid = true;
}
else
{
mIsValid = SetString (strAccountID, VER_ACCOUNT_ID, alphabet);
}
return mIsValid;
}
void RippleAddress::setAccountID (AccountID const& hash160)
{
mIsValid = true;
SetData (VER_ACCOUNT_ID, hash160);
}
//
// AccountPublic
//
@@ -464,14 +341,14 @@ Blob const& RippleAddress::getAccountPublic () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - getAccountPublic");
case VER_ACCOUNT_ID:
case TOKEN_ACCOUNT_ID:
throw std::runtime_error ("public not available from account id");
break;
case VER_ACCOUNT_PUBLIC:
case TOKEN_ACCOUNT_PUBLIC:
return vchData;
default:
@@ -483,13 +360,13 @@ std::string RippleAddress::humanAccountPublic () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - humanAccountPublic");
case VER_ACCOUNT_ID:
case TOKEN_ACCOUNT_ID:
throw std::runtime_error ("public not available from account id");
case VER_ACCOUNT_PUBLIC:
case TOKEN_ACCOUNT_PUBLIC:
return ToString ();
default:
@@ -500,7 +377,7 @@ std::string RippleAddress::humanAccountPublic () const
bool RippleAddress::setAccountPublic (std::string const& strPublic)
{
mIsValid = SetString (
strPublic, VER_ACCOUNT_PUBLIC, Base58::getRippleAlphabet ());
strPublic, TOKEN_ACCOUNT_PUBLIC, Base58::getRippleAlphabet ());
return mIsValid;
}
@@ -509,7 +386,7 @@ void RippleAddress::setAccountPublic (Blob const& vPublic)
{
mIsValid = true;
SetData (VER_ACCOUNT_PUBLIC, vPublic);
SetData (TOKEN_ACCOUNT_PUBLIC, vPublic);
}
void RippleAddress::setAccountPublic (RippleAddress const& generator, int seq)
@@ -541,14 +418,6 @@ bool RippleAddress::accountPublicVerify (
fullyCanonical);
}
RippleAddress RippleAddress::createAccountID (AccountID const& account)
{
RippleAddress na;
na.setAccountID (account);
return na;
}
//
// AccountPrivate
//
@@ -567,10 +436,10 @@ uint256 RippleAddress::getAccountPrivate () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - getAccountPrivate");
case VER_ACCOUNT_PRIVATE:
case TOKEN_ACCOUNT_SECRET:
return uint256::fromVoid (vchData.data() + (vchData.size() - 32));
default:
@@ -581,7 +450,7 @@ uint256 RippleAddress::getAccountPrivate () const
bool RippleAddress::setAccountPrivate (std::string const& strPrivate)
{
mIsValid = SetString (
strPrivate, VER_ACCOUNT_PRIVATE, Base58::getRippleAlphabet ());
strPrivate, TOKEN_ACCOUNT_SECRET, Base58::getRippleAlphabet ());
return mIsValid;
}
@@ -589,13 +458,13 @@ bool RippleAddress::setAccountPrivate (std::string const& strPrivate)
void RippleAddress::setAccountPrivate (Blob const& vPrivate)
{
mIsValid = true;
SetData (VER_ACCOUNT_PRIVATE, vPrivate);
SetData (TOKEN_ACCOUNT_SECRET, vPrivate);
}
void RippleAddress::setAccountPrivate (uint256 hash256)
{
mIsValid = true;
SetData (VER_ACCOUNT_PRIVATE, hash256);
SetData (TOKEN_ACCOUNT_SECRET, hash256);
}
void RippleAddress::setAccountPrivate (
@@ -689,10 +558,10 @@ Blob const& RippleAddress::getGenerator () const
// returns the public generator
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - getGenerator");
case VER_FAMILY_GENERATOR:
case TOKEN_FAMILY_GENERATOR:
// Do nothing.
return vchData;
@@ -705,10 +574,10 @@ std::string RippleAddress::humanGenerator () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - humanGenerator");
case VER_FAMILY_GENERATOR:
case TOKEN_FAMILY_GENERATOR:
return ToString ();
default:
@@ -719,7 +588,7 @@ std::string RippleAddress::humanGenerator () const
void RippleAddress::setGenerator (Blob const& vPublic)
{
mIsValid = true;
SetData (VER_FAMILY_GENERATOR, vPublic);
SetData (TOKEN_FAMILY_GENERATOR, vPublic);
}
RippleAddress RippleAddress::createGeneratorPublic (RippleAddress const& naSeed)
@@ -737,10 +606,10 @@ uint128 RippleAddress::getSeed () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - getSeed");
case VER_FAMILY_SEED:
case TOKEN_FAMILY_SEED:
return uint128 (vchData);
default:
@@ -752,10 +621,10 @@ std::string RippleAddress::humanSeed1751 () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - humanSeed1751");
case VER_FAMILY_SEED:
case TOKEN_FAMILY_SEED:
{
std::string strHuman;
std::string strLittle;
@@ -780,10 +649,10 @@ std::string RippleAddress::humanSeed () const
{
switch (nVersion)
{
case VER_NONE:
case TOKEN_NONE:
throw std::runtime_error ("unset source - humanSeed");
case VER_FAMILY_SEED:
case TOKEN_FAMILY_SEED:
return ToString ();
default:
@@ -809,7 +678,7 @@ int RippleAddress::setSeed1751 (std::string const& strHuman1751)
bool RippleAddress::setSeed (std::string const& strSeed)
{
mIsValid = SetString (strSeed, VER_FAMILY_SEED, Base58::getRippleAlphabet ());
mIsValid = SetString (strSeed, TOKEN_FAMILY_SEED, Base58::getRippleAlphabet ());
return mIsValid;
}
@@ -820,8 +689,10 @@ bool RippleAddress::setSeedGeneric (std::string const& strText)
bool bResult = true;
uint128 uSeed;
if (parseBase58<AccountID>(strText))
return false;
if (strText.empty ()
|| naTemp.setAccountID (strText)
|| naTemp.setAccountPublic (strText)
|| naTemp.setAccountPrivate (strText)
|| naTemp.setNodePublic (strText)
@@ -853,7 +724,7 @@ void RippleAddress::setSeed (uint128 hash128)
{
mIsValid = true;
SetData (VER_FAMILY_SEED, hash128);
SetData (TOKEN_FAMILY_SEED, hash128);
}
void RippleAddress::setSeedRandom ()
@@ -978,4 +849,20 @@ KeyPair generateKeysFromSeed (KeyType type, RippleAddress const& seed)
return result;
}
// DEPRECATED
AccountID
calcAccountID (RippleAddress const& publicKey)
{
auto const& pk =
publicKey.getAccountPublic();
ripesha_hasher rsh;
rsh(pk.data(), pk.size());
auto const d = static_cast<
ripesha_hasher::result_type>(rsh);
AccountID id;
static_assert(sizeof(d) == sizeof(id), "");
std::memcpy(id.data(), d.data(), d.size());
return id;
}
} // ripple

View File

@@ -19,6 +19,7 @@
#include <BeastConfig.h>
#include <ripple/protocol/STAccount.h>
#include <ripple/protocol/types.h>
namespace ripple {
@@ -31,12 +32,11 @@ std::string STAccount::getText () const
{
AccountID u;
RippleAddress a;
if (!getValueH160 (u))
if (! getValueH160 (u))
return STBlob::getText ();
a.setAccountID (u);
return a.humanAccountID ();
// VFALCO This should use getApp().accountIDCache()
// Maybe pass the cache in?
return toBase58(u);
}
STAccount*
@@ -55,20 +55,4 @@ bool STAccount::isValueH160 () const
return peekValue ().size () == (160 / 8);
}
RippleAddress STAccount::getValueNCA () const
{
RippleAddress a;
AccountID account;
if (getValueH160 (account))
a.setAccountID (account);
return a;
}
void STAccount::setValueNCA (RippleAddress const& nca)
{
setValueH160 (nca.getAccountID ());
}
} // ripple

View File

@@ -123,31 +123,6 @@ bool STLedgerEntry::isThreaded () const
return isFieldPresent (sfPreviousTxnID);
}
bool STLedgerEntry::hasOneOwner () const
{
return (type_ != ltACCOUNT_ROOT) && (getFieldIndex (sfAccount) != -1);
}
bool STLedgerEntry::hasTwoOwners () const
{
return type_ == ltRIPPLE_STATE;
}
RippleAddress STLedgerEntry::getOwner () const
{
return getFieldAccount (sfAccount);
}
RippleAddress STLedgerEntry::getFirstOwner () const
{
return RippleAddress::createAccountID (getFieldAmount (sfLowLimit).getIssuer ());
}
RippleAddress STLedgerEntry::getSecondOwner () const
{
return RippleAddress::createAccountID (getFieldAmount (sfHighLimit).getIssuer ());
}
uint256 STLedgerEntry::getThreadedTransaction () const
{
return getFieldH256 (sfPreviousTxnID);

View File

@@ -347,19 +347,21 @@ STObject::startMultiSigningData () const
return s;
}
// VFALCO This should not be a member,
// and the function shouldn't even exist
void
STObject::finishMultiSigningData (
RippleAddress const& signingForID,
RippleAddress const& signingID,
AccountID const& signingForID,
AccountID const& signingID,
Serializer& s) const
{
s.add160 (signingForID.getAccountID ());
s.add160 (signingID.getAccountID ());
s.add160 (signingForID);
s.add160 (signingID);
}
Serializer
STObject::getMultiSigningData (
RippleAddress const& signingForID, RippleAddress const& signingID) const
AccountID const& signingForID, AccountID const& signingID) const
{
Serializer s (startMultiSigningData ());
finishMultiSigningData (signingForID, signingID, s);
@@ -586,26 +588,7 @@ uint256 STObject::getFieldH256 (SField const& field) const
return getFieldByValue <STHash256> (field);
}
RippleAddress STObject::getFieldAccount (SField const& field) const
{
const STBase* rf = peekAtPField (field);
if (!rf)
throw std::runtime_error ("Field not found");
SerializedTypeID id = rf->getSType ();
if (id == STI_NOTPRESENT) return RippleAddress ();
const STAccount* cf = dynamic_cast<const STAccount*> (rf);
if (!cf)
throw std::runtime_error ("Wrong field type");
return cf->getValueNCA ();
}
AccountID STObject::getFieldAccount160 (SField const& field) const
AccountID STObject::getAccountID (SField const& field) const
{
auto rf = peekAtPField (field);
if (!rf)
@@ -715,7 +698,7 @@ void STObject::setFieldV256 (SField const& field, STVector256 const& v)
setFieldUsingSetValue <STVector256> (field, v);
}
void STObject::setFieldAccount (SField const& field, AccountID const& v)
void STObject::setAccountID (SField const& field, AccountID const& v)
{
STBase* rf = getPField (field, true);

View File

@@ -31,6 +31,7 @@
#include <ripple/protocol/STParsedJSON.h>
#include <ripple/protocol/STPathSet.h>
#include <ripple/protocol/TxFormats.h>
#include <ripple/protocol/types.h>
#include <ripple/protocol/impl/STVar.h>
#include <beast/module/core/text/LexicalCast.h>
#include <cassert>
@@ -544,13 +545,14 @@ static boost::optional<detail::STVar> parseLeaf (
// set it, otherwise, we assume it's an AccountID
if (!uAccount.SetHexExact (account.asString ()))
{
RippleAddress a;
if (!a.setAccountID (account.asString ()))
auto const a = parseBase58<AccountID>(
account.asString());
if (! a)
{
error = invalid_data (element_name, "account");
return ret;
}
uAccount = a.getAccountID ();
uAccount = *a;
}
}
@@ -586,13 +588,14 @@ static boost::optional<detail::STVar> parseLeaf (
if (!uIssuer.SetHexExact (issuer.asString ()))
{
RippleAddress a;
if (!a.setAccountID (issuer.asString ()))
auto const a = parseBase58<AccountID>(
issuer.asString());
if (! a)
{
error = invalid_data (element_name, "issuer");
return ret;
}
uIssuer = a.getAccountID ();
uIssuer = *a;
}
}
@@ -623,18 +626,17 @@ static boost::optional<detail::STVar> parseLeaf (
try
{
AccountID account;
if (!account.SetHexExact (strValue))
// VFALCO This needs careful auditing
auto const account =
parseHexOrBase58<AccountID>(
strValue);
if (! account)
{
RippleAddress a;
if (!a.setAccountID (strValue))
{
error = invalid_data (json_name, fieldName);
return ret;
}
account.copyFrom (a.getAccountID ());
error = invalid_data (json_name, fieldName);
return ret;
}
ret = detail::make_stvar <STAccount> (field, account);
ret = detail::make_stvar <STAccount>(
field, *account);
}
catch (...)
{

View File

@@ -25,6 +25,7 @@
#include <ripple/protocol/STAccount.h>
#include <ripple/protocol/STArray.h>
#include <ripple/protocol/TxFlags.h>
#include <ripple/protocol/types.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/json/to_string.h>
@@ -120,36 +121,29 @@ STTx::getFullText () const
return ret;
}
std::vector<RippleAddress>
boost::container::flat_set<AccountID>
STTx::getMentionedAccounts () const
{
std::vector<RippleAddress> accounts;
boost::container::flat_set<AccountID> list;
for (auto const& it : *this)
{
if (auto sa = dynamic_cast<STAccount const*> (&it))
{
auto const na = sa->getValueNCA ();
if (std::find (accounts.cbegin (), accounts.cend (), na) == accounts.cend ())
accounts.push_back (na);
AccountID id;
assert(sa->isValueH160());
if (sa->getValueH160(id))
list.insert(id);
}
else if (auto sa = dynamic_cast<STAmount const*> (&it))
{
auto const& issuer = sa->getIssuer ();
if (isXRP (issuer))
continue;
RippleAddress na;
na.setAccountID (issuer);
if (std::find (accounts.cbegin (), accounts.cend (), na) == accounts.cend ())
accounts.push_back (na);
if (! isXRP (issuer))
list.insert(issuer);
}
}
return accounts;
return list;
}
static Blob getSigningData (STTx const& that)
@@ -226,11 +220,6 @@ void STTx::setSigningPubKey (RippleAddress const& naSignPubKey)
setFieldVL (sfSigningPubKey, naSignPubKey.getAccountPublic ());
}
void STTx::setSourceAccount (RippleAddress const& naSource)
{
setFieldAccount (sfAccount, naSource);
}
Json::Value STTx::getJson (int) const
{
Json::Value ret = STObject::getJson (0);
@@ -269,6 +258,7 @@ std::string STTx::getMetaSQL (std::uint32_t inLedger,
return getMetaSQL (s, inLedger, TXN_SQL_VALIDATED, escapedMetaData);
}
// VFALCO This could be a free function elsewhere
std::string
STTx::getMetaSQL (Serializer rawTxn,
std::uint32_t inLedger, char status, std::string const& escapedMetaData) const
@@ -281,7 +271,7 @@ STTx::getMetaSQL (Serializer rawTxn,
return str (boost::format (bfTrans)
% to_string (getTransactionID ()) % format->getName ()
% getSourceAccount ().humanAccountID ()
% toBase58(getAccountID(sfAccount))
% getSequence () % inLedger % status % rTxn % escapedMetaData);
}
@@ -338,7 +328,7 @@ STTx::checkMultiSign () const
Serializer const dataStart (startMultiSigningData ());
// We also use the sfAccount field inside the loop. Get it once.
RippleAddress const txnAccountID = getFieldAccount (sfAccount);
auto const txnAccountID = getAccountID (sfAccount);
// Determine whether signatures must be full canonical.
ECDSA const fullyCanonical = (getFlags() & tfFullyCanonicalSig)
@@ -398,17 +388,16 @@ STTx::checkMultiSign () const
*/
// We use this std::set to detect this form of double-signing.
std::set<RippleAddress> firstLevelSigners;
std::set<AccountID> firstLevelSigners;
// SigningFors must be in sorted order by AccountID.
RippleAddress lastSigningForID;
lastSigningForID.setAccountID ("");
AccountID lastSigningForID = zero;
// Every signature must verify or we reject the transaction.
for (auto const& signingFor : multiSigners)
{
RippleAddress const signingForID =
signingFor.getFieldAccount (sfAccount);
auto const signingForID =
signingFor.getAccountID (sfAccount);
// SigningFors must be in order by account ID. No duplicates allowed.
if (lastSigningForID >= signingForID)
@@ -440,13 +429,12 @@ STTx::checkMultiSign () const
}
// SingingAccounts must be in sorted order by AccountID.
RippleAddress lastSigningAcctID;
lastSigningAcctID.setAccountID ("");
AccountID lastSigningAcctID = zero;
for (auto const& signingAcct : signingAccounts)
{
RippleAddress const signingAcctID =
signingAcct.getFieldAccount (sfAccount);
auto const signingAcctID =
signingAcct.getAccountID (sfAccount);
// None of the multi-signers may sign for themselves.
if (signingForID == signingAcctID)

View File

@@ -22,15 +22,11 @@
#include <ripple/protocol/SystemParameters.h>
#include <ripple/protocol/RippleAddress.h>
#include <ripple/protocol/UintTypes.h>
#include <ripple/protocol/types.h>
#include <beast/utility/static_initializer.h>
namespace ripple {
std::string to_string(AccountID const& account)
{
return RippleAddress::createAccountID (account).humanAccountID ();
}
std::string to_string(Currency const& currency)
{
// Characters we are willing to allow in the ASCII representation of a
@@ -111,38 +107,12 @@ Currency to_currency(std::string const& code)
return currency;
}
bool to_issuer(AccountID& issuer, std::string const& s)
{
if (s.size () == (160 / 4))
{
issuer.SetHex (s);
return true;
}
RippleAddress address;
bool success = address.setAccountID (s);
if (success)
issuer = address.getAccountID ();
return success;
}
AccountID const& xrpAccount()
{
static AccountID const account(0);
return account;
}
Currency const& xrpCurrency()
{
static Currency const currency(0);
return currency;
}
AccountID const& noAccount()
{
static AccountID const account(1);
return account;
}
Currency const& noCurrency()
{
static Currency const currency(1);

View File

@@ -25,12 +25,11 @@
namespace ripple {
static_assert(sizeof(decltype(
openssl_ripemd160_hasher::ctx_)) ==
sizeof(RIPEMD160_CTX), "");
openssl_ripemd160_hasher::openssl_ripemd160_hasher()
{
static_assert(sizeof(decltype(
openssl_ripemd160_hasher::ctx_)) ==
sizeof(RIPEMD160_CTX), "");
auto const ctx = reinterpret_cast<
RIPEMD160_CTX*>(ctx_);
RIPEMD160_Init(ctx);
@@ -54,12 +53,11 @@ openssl_ripemd160_hasher::operator result_type() noexcept
return digest;
}
static_assert(sizeof(decltype(
openssl_sha256_hasher::ctx_)) ==
sizeof(SHA256_CTX), "");
openssl_sha256_hasher::openssl_sha256_hasher()
{
static_assert(sizeof(decltype(
openssl_sha256_hasher::ctx_)) ==
sizeof(SHA256_CTX), "");
auto const ctx = reinterpret_cast<
SHA256_CTX*>(ctx_);
SHA256_Init(ctx);

View File

@@ -0,0 +1,331 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/protocol/tokens.h>
#include <ripple/protocol/digest.h>
#include <cassert>
#include <cstring>
#include <memory>
#include <beast/cxx14/type_traits.h> // <type_traits>
#include <utility>
#include <vector>
namespace ripple {
static char rippleAlphabet[] =
"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
static char bitcoinAlphabet[] =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
//------------------------------------------------------------------------------
template <class Hasher>
static
typename Hasher::result_type
digest (void const* data, std::size_t size) noexcept
{
Hasher h;
h(data, size);
return static_cast<
typename Hasher::result_type>(h);
}
template <class Hasher, class T, std::size_t N,
class = std::enable_if_t<sizeof(T) == 1>>
static
typename Hasher::result_type
digest (std::array<T, N> const& v)
{
return digest<Hasher>(v.data(), v.size());
}
// Computes a double digest (e.g. digest of the digest)
template <class Hasher, class... Args>
static
typename Hasher::result_type
digest2 (Args const&... args)
{
return digest<Hasher>(
digest<Hasher>(args...));
}
/* Calculate a 4-byte checksum of the data
The checksum is calculated as the first 4 bytes
of the SHA256 digest of the message. This is added
to the base58 encoding of identifiers to detect
user error in data entry.
@note This checksum algorithm is part of the client API
*/
void
checksum (void* out,
void const* message,
std::size_t size)
{
auto const h =
digest2<sha256_hasher>(message, size);
std::memcpy(out, h.data(), 4);
}
//------------------------------------------------------------------------------
// Code from Bitcoin: https://github.com/bitcoin/bitcoin
// Copyright (c) 2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
// Modified from the original
//
// WARNING Do not call this directly, use
// encodeBase58Token instead since it
// calculates the size of buffer needed.
static
std::string
encodeBase58(
void const* message, std::size_t size,
void *temp, char const* const alphabet)
{
auto pbegin = reinterpret_cast<
unsigned char const*>(message);
auto const pend = pbegin + size;
// Skip & count leading zeroes.
int zeroes = 0;
while (pbegin != pend && *pbegin == 0)
{
pbegin++;
zeroes++;
}
auto const b58begin = reinterpret_cast<
unsigned char*>(temp);
// log(256) / log(58), rounded up.
auto const b58end = b58begin +
size * (138 / 100 + 1);
std::fill(b58begin, b58end, 0);
while (pbegin != pend)
{
int carry = *pbegin;
// Apply "b58 = b58 * 256 + ch".
for (auto iter = b58end; iter != b58begin; --iter)
{
carry += 256 * (iter[-1]);
iter[-1] = carry % 58;
carry /= 58;
}
assert(carry == 0);
pbegin++;
}
// Skip leading zeroes in base58 result.
auto iter = b58begin;
while (iter != b58end && *iter == 0)
++iter;
// Translate the result into a string.
std::string str;
str.reserve(zeroes + (b58end - iter));
str.assign(zeroes, alphabet[0]);
while (iter != b58end)
str += alphabet[*(iter++)];
return str;
}
/* Base-58 encode a Ripple Token
Ripple Tokens have a one-byte prefx indicating
the type of token, followed by the data for the
token, and finally a 4-byte checksum.
Tokens include the following:
Wallet Seed
Account Public Key
Account ID
@param temp A pointer to storage of not
less than 2*(size+6) bytes
*/
std::string
base58EncodeToken (std::uint8_t type,
void const* token, std::size_t size)
{
char buf[1024];
// expanded token includes type + checksum
auto const expanded = 1 + size + 4;
// add scratch, log(256) / log(58), rounded up.
auto const needed = expanded +
size * (138 / 100 + 1);
std::unique_ptr<
char[]> pbuf;
char* temp;
if (needed > sizeof(buf))
{
pbuf.reset(new char[needed]);
temp = pbuf.get();
}
else
{
temp = buf;
}
// Lay the data out as
// <type><token><checksum>
temp[0] = type;
std::memcpy(temp + 1, token, size);
checksum(temp + 1 + size, temp, 1 + size);
return encodeBase58(temp, expanded,
temp + expanded, rippleAlphabet);
}
//------------------------------------------------------------------------------
// Code from Bitcoin: https://github.com/bitcoin/bitcoin
// Copyright (c) 2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
// Modified from the original
template <class InverseArray>
static
std::string
decodeBase58 (std::string const& s,
InverseArray const& inv)
{
auto psz = s.c_str();
auto remain = s.size();
// Skip and count leading zeroes
int zeroes = 0;
while (remain > 0 && inv[*psz] == 0)
{
++zeroes;
++psz;
--remain;
}
// Allocate enough space in big-endian base256 representation.
// log(58) / log(256), rounded up.
std::vector<unsigned char> b256(
remain * 733 / 1000 + 1);
while (remain > 0)
{
auto carry = inv[*psz];
if (carry == -1)
return {};
// Apply "b256 = b256 * 58 + carry".
for (auto iter = b256.rbegin();
iter != b256.rend(); ++iter)
{
carry += 58 * *iter;
*iter = carry % 256;
carry /= 256;
}
assert(carry == 0);
++psz;
--remain;
}
// Skip leading zeroes in b256.
auto iter = std::find_if(
b256.begin(), b256.end(),[](unsigned char c)
{ return c != 0; });
std::string result;
result.reserve (zeroes + (b256.end() - iter));
result.assign (zeroes, 0x00);
while (iter != b256.end())
result.push_back(*(iter++));
return result;
}
/* Base58 decode a Ripple token
The type and checksum are are checked
and removed from the returned result.
*/
template <class InverseArray>
static
std::string
decodeBase58Token (std::string const& s,
int type, InverseArray const& inv)
{
auto result = decodeBase58(s, inv);
if (result.empty())
return result;
// Reject zero length tokens
if (result.size() < 6)
return {};
if (result[0] != type)
return {};
std::array<char, 4> guard;
checksum(guard.data(),
result.data(), result.size() - 4);
if (std::memcmp(guard.data(),
result.data() +
result.size() - 4, 4) != 0)
return {};
result.resize(result.size() - 4);
// Erase the type byte
// VFALCO This might cause problems later
result.erase(result.begin());
return result;
}
//------------------------------------------------------------------------------
// Maps characters to their base58 digit
class InverseAlphabet
{
private:
std::array<int, 256> map_;
public:
explicit
InverseAlphabet(std::string const& digits)
{
map_.fill(-1);
int i = 0;
for(auto const c : digits)
map_[static_cast<
unsigned char>(c)] = i++;
}
int
operator[](char c) const
{
return map_[static_cast<
unsigned char>(c)];
}
};
static InverseAlphabet rippleInverse(rippleAlphabet);
static InverseAlphabet bitcoinInverse(bitcoinAlphabet);
std::string
decodeBase58Token(
std::string const& s, int type)
{
return decodeBase58Token(
s, type, rippleInverse);
}
std::string
decodeBase58TokenBitcoin(
std::string const& s, int type)
{
return decodeBase58Token(
s, type, bitcoinInverse);
}
} // ripple