Bitcoin ABC  0.22.13
P2P Digital Currency
cashaddrenc.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2019 The Bitcoin developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 #include <cashaddrenc.h>
5 
6 #include <cashaddr.h>
7 #include <chainparams.h>
8 #include <pubkey.h>
9 #include <script/script.h>
10 #include <util/strencodings.h>
11 
12 #include <boost/variant/static_visitor.hpp>
13 
14 #include <algorithm>
15 
16 namespace {
17 
18 // Convert the data part to a 5 bit representation.
19 template <class T>
20 std::vector<uint8_t> PackAddrData(const T &id, uint8_t type) {
21  uint8_t version_byte(type << 3);
22  size_t size = id.size();
23  uint8_t encoded_size = 0;
24  switch (size * 8) {
25  case 160:
26  encoded_size = 0;
27  break;
28  case 192:
29  encoded_size = 1;
30  break;
31  case 224:
32  encoded_size = 2;
33  break;
34  case 256:
35  encoded_size = 3;
36  break;
37  case 320:
38  encoded_size = 4;
39  break;
40  case 384:
41  encoded_size = 5;
42  break;
43  case 448:
44  encoded_size = 6;
45  break;
46  case 512:
47  encoded_size = 7;
48  break;
49  default:
50  throw std::runtime_error(
51  "Error packing cashaddr: invalid address length");
52  }
53  version_byte |= encoded_size;
54  std::vector<uint8_t> data = {version_byte};
55  data.insert(data.end(), std::begin(id), std::end(id));
56 
57  std::vector<uint8_t> converted;
58  // Reserve the number of bytes required for a 5-bit packed version of a
59  // hash, with version byte. Add half a byte(4) so integer math provides
60  // the next multiple-of-5 that would fit all the data.
61  converted.reserve(((size + 1) * 8 + 4) / 5);
62  ConvertBits<8, 5, true>([&](uint8_t c) { converted.push_back(c); },
63  std::begin(data), std::end(data));
64 
65  return converted;
66 }
67 
68 // Implements encoding of CTxDestination using cashaddr.
69 class CashAddrEncoder : public boost::static_visitor<std::string> {
70 public:
71  explicit CashAddrEncoder(const CChainParams &p) : params(p) {}
72 
73  std::string operator()(const PKHash &id) const {
74  std::vector<uint8_t> data = PackAddrData(id, PUBKEY_TYPE);
75  return cashaddr::Encode(params.CashAddrPrefix(), data);
76  }
77 
78  std::string operator()(const ScriptHash &id) const {
79  std::vector<uint8_t> data = PackAddrData(id, SCRIPT_TYPE);
80  return cashaddr::Encode(params.CashAddrPrefix(), data);
81  }
82 
83  std::string operator()(const CNoDestination &) const { return ""; }
84 
85 private:
86  const CChainParams &params;
87 };
88 
89 } // namespace
90 
91 std::string EncodeCashAddr(const CTxDestination &dst,
92  const CChainParams &params) {
93  return boost::apply_visitor(CashAddrEncoder(params), dst);
94 }
95 
96 std::string EncodeCashAddr(const std::string &prefix,
97  const CashAddrContent &content) {
98  std::vector<uint8_t> data = PackAddrData(content.hash, content.type);
99  return cashaddr::Encode(prefix, data);
100 }
101 
102 CTxDestination DecodeCashAddr(const std::string &addr,
103  const CChainParams &params) {
104  CashAddrContent content =
105  DecodeCashAddrContent(addr, params.CashAddrPrefix());
106  if (content.hash.size() == 0) {
107  return CNoDestination{};
108  }
109 
110  return DecodeCashAddrDestination(content);
111 }
112 
113 CashAddrContent DecodeCashAddrContent(const std::string &addr,
114  const std::string &expectedPrefix) {
115  std::string prefix;
116  std::vector<uint8_t> payload;
117  std::tie(prefix, payload) = cashaddr::Decode(addr, expectedPrefix);
118 
119  if (prefix != expectedPrefix) {
120  return {};
121  }
122 
123  if (payload.empty()) {
124  return {};
125  }
126 
127  std::vector<uint8_t> data;
128  data.reserve(payload.size() * 5 / 8);
129  if (!ConvertBits<5, 8, false>([&](uint8_t c) { data.push_back(c); },
130  begin(payload), end(payload))) {
131  return {};
132  }
133 
134  // Decode type and size from the version.
135  uint8_t version = data[0];
136  if (version & 0x80) {
137  // First bit is reserved.
138  return {};
139  }
140 
141  auto type = CashAddrType((version >> 3) & 0x1f);
142  uint32_t hash_size = 20 + 4 * (version & 0x03);
143  if (version & 0x04) {
144  hash_size *= 2;
145  }
146 
147  // Check that we decoded the exact number of bytes we expected.
148  if (data.size() != hash_size + 1) {
149  return {};
150  }
151 
152  // Pop the version.
153  data.erase(data.begin());
154  return {type, std::move(data)};
155 }
156 
158  if (content.hash.size() != 20) {
159  // Only 20 bytes hash are supported now.
160  return CNoDestination{};
161  }
162 
163  uint160 hash;
164  std::copy(begin(content.hash), end(content.hash), hash.begin());
165 
166  switch (content.type) {
167  case PUBKEY_TYPE:
168  return PKHash(hash);
169  case SCRIPT_TYPE:
170  return ScriptHash(hash);
171  default:
172  return CNoDestination{};
173  }
174 }
175 
176 // PackCashAddrContent allows for testing PackAddrData in unittests due to
177 // template definitions.
178 std::vector<uint8_t> PackCashAddrContent(const CashAddrContent &content) {
179  return PackAddrData(content.hash, content.type);
180 }
CashAddrType type
Definition: cashaddrenc.h:17
CashAddrType
Definition: cashaddrenc.h:14
const char * prefix
Definition: rest.cpp:755
std::vector< uint8_t > hash
Definition: cashaddrenc.h:18
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system...
Definition: chainparams.h:47
CTxDestination DecodeCashAddr(const std::string &addr, const CChainParams &params)
std::vector< uint8_t > PackCashAddrContent(const CashAddrContent &content)
std::pair< std::string, data > Decode(const std::string &str, const std::string &default_prefix)
Decode a cashaddr string.
Definition: cashaddr.cpp:214
uint8_t * begin()
Definition: uint256.h:76
std::string Encode(const std::string &prefix, const data &payload)
Encode a cashaddr string.
Definition: cashaddr.cpp:198
160-bit opaque blob.
Definition: uint256.h:108
CTxDestination DecodeCashAddrDestination(const CashAddrContent &content)
const std::string & CashAddrPrefix() const
Definition: chainparams.h:92
std::string EncodeCashAddr(const CTxDestination &dst, const CChainParams &params)
Definition: cashaddrenc.cpp:91
CashAddrContent DecodeCashAddrContent(const std::string &addr, const std::string &expectedPrefix)
boost::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:87