Bitcoin ABC  0.26.3
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  auto [prefix, payload] = cashaddr::Decode(addr, expectedPrefix);
116 
117  if (prefix != expectedPrefix) {
118  return {};
119  }
120 
121  if (payload.empty()) {
122  return {};
123  }
124 
125  std::vector<uint8_t> data;
126  data.reserve(payload.size() * 5 / 8);
127  if (!ConvertBits<5, 8, false>([&](uint8_t c) { data.push_back(c); },
128  begin(payload), end(payload))) {
129  return {};
130  }
131 
132  // Decode type and size from the version.
133  uint8_t version = data[0];
134  if (version & 0x80) {
135  // First bit is reserved.
136  return {};
137  }
138 
139  auto type = CashAddrType((version >> 3) & 0x1f);
140  uint32_t hash_size = 20 + 4 * (version & 0x03);
141  if (version & 0x04) {
142  hash_size *= 2;
143  }
144 
145  // Check that we decoded the exact number of bytes we expected.
146  if (data.size() != hash_size + 1) {
147  return {};
148  }
149 
150  // Pop the version.
151  data.erase(data.begin());
152  return {type, std::move(data)};
153 }
154 
156  if (content.hash.size() != 20) {
157  // Only 20 bytes hash are supported now.
158  return CNoDestination{};
159  }
160 
161  uint160 hash;
162  std::copy(begin(content.hash), end(content.hash), hash.begin());
163 
164  switch (content.type) {
165  case PUBKEY_TYPE:
166  return PKHash(hash);
167  case SCRIPT_TYPE:
168  return ScriptHash(hash);
169  default:
170  return CNoDestination{};
171  }
172 }
173 
174 // PackCashAddrContent allows for testing PackAddrData in unittests due to
175 // template definitions.
176 std::vector<uint8_t> PackCashAddrContent(const CashAddrContent &content) {
177  return PackAddrData(content.hash, content.type);
178 }
std::string EncodeCashAddr(const CTxDestination &dst, const CChainParams &params)
Definition: cashaddrenc.cpp:91
CashAddrContent DecodeCashAddrContent(const std::string &addr, const std::string &expectedPrefix)
CTxDestination DecodeCashAddrDestination(const CashAddrContent &content)
std::vector< uint8_t > PackCashAddrContent(const CashAddrContent &content)
CTxDestination DecodeCashAddr(const std::string &addr, const CChainParams &params)
CashAddrType
Definition: cashaddrenc.h:14
@ PUBKEY_TYPE
Definition: cashaddrenc.h:14
@ SCRIPT_TYPE
Definition: cashaddrenc.h:14
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition: chainparams.h:74
const std::string & CashAddrPrefix() const
Definition: chainparams.h:126
uint8_t * begin()
Definition: uint256.h:83
160-bit opaque blob.
Definition: uint256.h:115
std::pair< std::string, data > Decode(const std::string &str, const std::string &default_prefix)
Decode a cashaddr string.
Definition: cashaddr.cpp:214
std::string Encode(const std::string &prefix, const data &payload)
Encode a cashaddr string.
Definition: cashaddr.cpp:198
const char * prefix
Definition: rest.cpp:821
boost::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:93
CashAddrType type
Definition: cashaddrenc.h:17
std::vector< uint8_t > hash
Definition: cashaddrenc.h:18