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