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