rippled
AccountID.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/protocol/AccountID.h>
21 #include <ripple/protocol/PublicKey.h>
22 #include <ripple/protocol/digest.h>
23 #include <ripple/protocol/tokens.h>
24 #include <cstring>
25 
26 namespace ripple {
27 
30 {
32 }
33 
34 template <>
35 boost::optional<AccountID>
37 {
38  auto const result = decodeBase58Token(s, TokenType::AccountID);
39  if (result.empty())
40  return boost::none;
41  AccountID id;
42  if (result.size() != id.size())
43  return boost::none;
44  std::memcpy(id.data(), result.data(), result.size());
45  return id;
46 }
47 
48 boost::optional<AccountID>
50 {
51  auto const result = decodeBase58TokenBitcoin(s, TokenType::AccountID);
52  if (result.empty())
53  return boost::none;
54  AccountID id;
55  if (result.size() != id.size())
56  return boost::none;
57  std::memcpy(id.data(), result.data(), result.size());
58  return id;
59 }
60 
61 bool
63 {
64  if (!jv.isString())
65  return false;
66  auto const result = parseBase58<AccountID>(jv.asString());
67  if (!result)
68  return false;
69  account = *result;
70  return true;
71 }
72 
73 template <>
74 boost::optional<AccountID>
76 {
77  if (s.size() != 40)
78  return boost::none;
79  AccountID id;
80  if (!id.SetHex(s, true))
81  return boost::none;
82  return id;
83 }
84 
85 template <>
86 boost::optional<AccountID>
88 {
89  auto result = parseHex<AccountID>(s);
90  if (!result)
91  result = parseBase58<AccountID>(s);
92  return result;
93 }
94 
95 //------------------------------------------------------------------------------
96 /*
97  Calculation of the Account ID
98 
99  The AccountID is a 160-bit identifier that uniquely
100  distinguishes an account. The account may or may not
101  exist in the ledger. Even for accounts that are not in
102  the ledger, cryptographic operations may be performed
103  which affect the ledger. For example, designating an
104  account not in the ledger as a regular key for an
105  account that is in the ledger.
106 
107  Why did we use half of SHA512 for most things but then
108  SHA256 followed by RIPEMD160 for account IDs? Why didn't
109  we do SHA512 half then RIPEMD160? Or even SHA512 then RIPEMD160?
110  For that matter why RIPEMD160 at all why not just SHA512 and keep
111  only 160 bits?
112 
113  Answer (David Schwartz):
114 
115  The short answer is that we kept Bitcoin's behavior.
116  The longer answer was that:
117  1) Using a single hash could leave ripple
118  vulnerable to length extension attacks.
119  2) Only RIPEMD160 is generally considered safe at 160 bits.
120 
121  Any of those schemes would have been acceptable. However,
122  the one chosen avoids any need to defend the scheme chosen.
123  (Against any criticism other than unnecessary complexity.)
124 
125  "The historical reason was that in the very early days,
126  we wanted to give people as few ways to argue that we were
127  less secure than Bitcoin. So where there was no good reason
128  to change something, it was not changed."
129 */
130 AccountID
132 {
133  ripesha_hasher rsh;
134  rsh(pk.data(), pk.size());
135  auto const d = static_cast<ripesha_hasher::result_type>(rsh);
136  AccountID id;
137  static_assert(sizeof(d) == id.size(), "");
138  std::memcpy(id.data(), d.data(), d.size());
139  return id;
140 }
141 
142 AccountID const&
144 {
145  static AccountID const account(beast::zero);
146  return account;
147 }
148 
149 AccountID const&
151 {
152  static AccountID const account(1);
153  return account;
154 }
155 
156 bool
157 to_issuer(AccountID& issuer, std::string const& s)
158 {
159  if (s.size() == (160 / 4))
160  {
161  issuer.SetHex(s);
162  return true;
163  }
164  auto const account = parseBase58<AccountID>(s);
165  if (!account)
166  return false;
167  issuer = *account;
168  return true;
169 }
170 
171 //------------------------------------------------------------------------------
172 
173 /* VFALCO NOTE
174  An alternate implementation could use a pair of insert-only
175  hash maps that each use a single large memory allocation
176  to store a fixed size hash table and all of the AccountID/string
177  pairs laid out in memory (wouldn't use std::string here just a
178  length prefixed or zero terminated array). Possibly using
179  boost::intrusive as the basis for the unordered container.
180  This would cut down to one allocate/free cycle per swap of
181  the map.
182 */
183 
184 AccountIDCache::AccountIDCache(std::size_t capacity) : capacity_(capacity)
185 {
186  m1_.reserve(capacity_);
187 }
188 
191 {
192  std::lock_guard lock(mutex_);
193  auto iter = m1_.find(id);
194  if (iter != m1_.end())
195  return iter->second;
196  iter = m0_.find(id);
197  std::string result;
198  if (iter != m0_.end())
199  {
200  result = iter->second;
201  // Can use insert-only hash maps if
202  // we didn't erase from here.
203  m0_.erase(iter);
204  }
205  else
206  {
207  result = ripple::toBase58(id);
208  }
209  if (m1_.size() >= capacity_)
210  {
211  m0_ = std::move(m1_);
212  m1_.clear();
213  m1_.reserve(capacity_);
214  }
215  m1_.emplace(id, result);
216  return result;
217 }
218 
219 } // namespace ripple
ripple::PublicKey::data
std::uint8_t const * data() const noexcept
Definition: PublicKey.h:81
std::string
STL class.
ripple::base58EncodeToken
std::string base58EncodeToken(TokenType type, void const *token, std::size_t size)
Definition: tokens.cpp:182
Json::Value::isString
bool isString() const
Definition: json_value.cpp:1009
cstring
ripple::AccountIDCache::capacity_
std::size_t capacity_
Definition: AccountID.h:151
ripple::AccountIDCache::AccountIDCache
AccountIDCache(AccountIDCache const &)=delete
std::string::size
T size(T... args)
ripple::deprecatedParseBitcoinAccountID
boost::optional< AccountID > deprecatedParseBitcoinAccountID(std::string const &s)
Definition: AccountID.cpp:49
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
ripple::parseHex
boost::optional< AccountID > parseHex(std::string const &s)
Parse AccountID from hexadecimal string.
Definition: AccountID.cpp:75
std::lock_guard
STL class.
ripple::base_uint::data
pointer data()
Definition: base_uint.h:103
ripple::TokenType::AccountID
@ AccountID
ripple::base_uint::size
constexpr static std::size_t size()
Definition: base_uint.h:462
ripple::base_uint
Definition: base_uint.h:63
ripple::AccountIDCache::m1_
hash_map< AccountID, std::string > m1_
Definition: AccountID.h:153
ripple::AccountIDCache::mutex_
std::mutex mutex_
Definition: AccountID.h:150
ripple::ripesha_hasher
Returns the RIPEMD-160 digest of the SHA256 hash of the message.
Definition: digest.h:140
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
ripple::PublicKey::size
std::size_t size() const noexcept
Definition: PublicKey.h:87
ripple::calcAccountID
AccountID calcAccountID(PublicKey const &pk)
Definition: AccountID.cpp:131
std::array
STL class.
ripple::base_uint::SetHex
bool SetHex(const char *psz, bool bStrict=false)
Parse a hex string into a base_uint The input can be:
Definition: base_uint.h:406
ripple::xrpAccount
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:143
ripple::AccountIDCache::m0_
hash_map< AccountID, std::string > m0_
Definition: AccountID.h:152
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::parseHexOrBase58
boost::optional< AccountID > parseHexOrBase58(std::string const &s)
Parse AccountID from hex or checked base58 string.
Definition: AccountID.cpp:87
ripple::decodeBase58Token
static std::string decodeBase58Token(std::string const &s, TokenType type, InverseArray const &inv)
Definition: tokens.cpp:256
std::size_t
std::memcpy
T memcpy(T... args)
ripple::deprecatedParseBase58
bool deprecatedParseBase58(AccountID &account, Json::Value const &jv)
Definition: AccountID.cpp:62
ripple::decodeBase58TokenBitcoin
std::string decodeBase58TokenBitcoin(std::string const &s, TokenType type)
Decode a Base58 token using Bitcoin alphabet.
Definition: tokens.cpp:313
ripple::parseBase58
boost::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
Definition: AccountID.cpp:36
ripple::noAccount
AccountID const & noAccount()
A placeholder for empty accounts.
Definition: AccountID.cpp:150
ripple::AccountID
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition: AccountID.h:47
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::AccountIDCache::toBase58
std::string toBase58(AccountID const &) const
Return ripple::toBase58 for the AccountID.
Definition: AccountID.cpp:190
ripple::to_issuer
bool to_issuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
Definition: AccountID.cpp:157
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469