Bitcoin ABC  0.29.2
P2P Digital Currency
compressor.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2014 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include <compressor.h>
7 
8 #include <pubkey.h>
9 #include <script/standard.h>
10 
11 /*
12  * These check for scripts for which a special case with a shorter encoding is
13  * defined. They are implemented separately from the CScript test, as these test
14  * for exact byte sequence correspondences, and are more strict. For example,
15  * IsToPubKey also verifies whether the public key is valid (as invalid ones
16  * cannot be represented in compressed form).
17  */
18 
19 static bool IsToKeyID(const CScript &script, CKeyID &hash) {
20  if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 &&
21  script[2] == 20 && script[23] == OP_EQUALVERIFY &&
22  script[24] == OP_CHECKSIG) {
23  memcpy(&hash, &script[3], 20);
24  return true;
25  }
26  return false;
27 }
28 
29 static bool IsToScriptID(const CScript &script, CScriptID &hash) {
30  if (script.size() == 23 && script[0] == OP_HASH160 && script[1] == 20 &&
31  script[22] == OP_EQUAL) {
32  memcpy(&hash, &script[2], 20);
33  return true;
34  }
35  return false;
36 }
37 
38 static bool IsToPubKey(const CScript &script, CPubKey &pubkey) {
39  if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG &&
40  (script[1] == 0x02 || script[1] == 0x03)) {
41  pubkey.Set(&script[1], &script[34]);
42  return true;
43  }
44  if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG &&
45  script[1] == 0x04) {
46  pubkey.Set(&script[1], &script[66]);
47  // if not fully valid, a case that would not be compressible
48  return pubkey.IsFullyValid();
49  }
50  return false;
51 }
52 
53 bool CompressScript(const CScript &script, std::vector<uint8_t> &out) {
54  CKeyID keyID;
55  if (IsToKeyID(script, keyID)) {
56  out.resize(21);
57  out[0] = 0x00;
58  memcpy(&out[1], &keyID, 20);
59  return true;
60  }
61  CScriptID scriptID;
62  if (IsToScriptID(script, scriptID)) {
63  out.resize(21);
64  out[0] = 0x01;
65  memcpy(&out[1], &scriptID, 20);
66  return true;
67  }
68  CPubKey pubkey;
69  if (IsToPubKey(script, pubkey)) {
70  out.resize(33);
71  memcpy(&out[1], &pubkey[1], 32);
72  if (pubkey[0] == 0x02 || pubkey[0] == 0x03) {
73  out[0] = pubkey[0];
74  return true;
75  } else if (pubkey[0] == 0x04) {
76  out[0] = 0x04 | (pubkey[64] & 0x01);
77  return true;
78  }
79  }
80  return false;
81 }
82 
83 unsigned int GetSpecialScriptSize(unsigned int nSize) {
84  if (nSize == 0 || nSize == 1) {
85  return 20;
86  }
87  if (nSize == 2 || nSize == 3 || nSize == 4 || nSize == 5) {
88  return 32;
89  }
90  return 0;
91 }
92 
93 bool DecompressScript(CScript &script, unsigned int nSize,
94  const std::vector<uint8_t> &in) {
95  switch (nSize) {
96  case 0x00:
97  script.resize(25);
98  script[0] = OP_DUP;
99  script[1] = OP_HASH160;
100  script[2] = 20;
101  memcpy(&script[3], in.data(), 20);
102  script[23] = OP_EQUALVERIFY;
103  script[24] = OP_CHECKSIG;
104  return true;
105  case 0x01:
106  script.resize(23);
107  script[0] = OP_HASH160;
108  script[1] = 20;
109  memcpy(&script[2], in.data(), 20);
110  script[22] = OP_EQUAL;
111  return true;
112  case 0x02:
113  case 0x03:
114  script.resize(35);
115  script[0] = 33;
116  script[1] = nSize;
117  memcpy(&script[2], in.data(), 32);
118  script[34] = OP_CHECKSIG;
119  return true;
120  case 0x04:
121  case 0x05:
122  uint8_t vch[33] = {};
123  vch[0] = nSize - 2;
124  memcpy(&vch[1], in.data(), 32);
125  CPubKey pubkey{vch};
126  if (!pubkey.Decompress()) {
127  return false;
128  }
129  assert(pubkey.size() == 65);
130  script.resize(67);
131  script[0] = 65;
132  memcpy(&script[1], pubkey.begin(), 65);
133  script[66] = OP_CHECKSIG;
134  return true;
135  }
136  return false;
137 }
138 
139 // Amount compression:
140 // * If the amount is 0, output 0
141 // * first, divide the amount (in base units) by the largest power of 10
142 // possible; call the exponent e (e is max 9)
143 // * if e<9, the last digit of the resulting number cannot be 0; store it as d,
144 // and drop it (divide by 10)
145 // * call the result n
146 // * output 1 + 10*(9*n + d - 1) + e
147 // * if e==9, we only know the resulting number is not zero, so output 1 + 10*(n
148 // - 1) + 9
149 // (this is decodable, as d is in [1-9] and e is in [0-9])
150 uint64_t CompressAmount(Amount amt) {
151  uint64_t n = amt / SATOSHI;
152  if (n == 0) {
153  return 0;
154  }
155  int e = 0;
156  while (((n % 10) == 0) && e < 9) {
157  n /= 10;
158  e++;
159  }
160  if (e < 9) {
161  int d = (n % 10);
162  assert(d >= 1 && d <= 9);
163  n /= 10;
164  return 1 + (n * 9 + d - 1) * 10 + e;
165  } else {
166  return 1 + (n - 1) * 10 + 9;
167  }
168 }
169 
171  // x = 0 OR x = 1+10*(9*n + d - 1) + e OR x = 1+10*(n - 1) + 9
172  if (x == 0) {
173  return Amount::zero();
174  }
175  x--;
176  // x = 10*(9*n + d - 1) + e
177  int e = x % 10;
178  x /= 10;
179  uint64_t n = 0;
180  if (e < 9) {
181  // x = 9*n + d - 1
182  int d = (x % 9) + 1;
183  x /= 9;
184  // x = n
185  n = x * 10 + d;
186  } else {
187  n = x + 1;
188  }
189  while (e) {
190  n *= 10;
191  e--;
192  }
193  return int64_t(n) * SATOSHI;
194 }
static constexpr Amount SATOSHI
Definition: amount.h:143
A reference to a CKey: the Hash160 of its serialized public key.
Definition: pubkey.h:22
An encapsulated public key.
Definition: pubkey.h:31
bool IsFullyValid() const
fully validate whether this is a valid public key (more expensive than IsValid())
Definition: pubkey.cpp:256
void Set(const T pbegin, const T pend)
Initialize a public key using begin/end iterators to byte data.
Definition: pubkey.h:78
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:431
A reference to a CScript: the Hash160 of its serialization (see script.h)
Definition: standard.h:24
size_type size() const
Definition: prevector.h:386
value_type * data()
Definition: prevector.h:610
iterator begin()
Definition: prevector.h:390
void resize(size_type new_size)
Definition: prevector.h:416
static bool IsToPubKey(const CScript &script, CPubKey &pubkey)
Definition: compressor.cpp:38
static bool IsToKeyID(const CScript &script, CKeyID &hash)
Definition: compressor.cpp:19
Amount DecompressAmount(uint64_t x)
Definition: compressor.cpp:170
static bool IsToScriptID(const CScript &script, CScriptID &hash)
Definition: compressor.cpp:29
uint64_t CompressAmount(Amount amt)
Compress amount.
Definition: compressor.cpp:150
unsigned int GetSpecialScriptSize(unsigned int nSize)
Definition: compressor.cpp:83
bool CompressScript(const CScript &script, std::vector< uint8_t > &out)
Definition: compressor.cpp:53
bool DecompressScript(CScript &script, unsigned int nSize, const std::vector< uint8_t > &in)
Definition: compressor.cpp:93
@ OP_CHECKSIG
Definition: script.h:163
@ OP_EQUAL
Definition: script.h:119
@ OP_DUP
Definition: script.h:98
@ OP_HASH160
Definition: script.h:160
@ OP_EQUALVERIFY
Definition: script.h:120
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32
assert(!tx.IsCoinBase())