-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathutil.scrypt
249 lines (213 loc) · 8.61 KB
/
util.scrypt
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
library Util {
// number of bytes to denote some numeric value
static const int DataLen = 1;
// number of bytes to denote input sequence
static const int InputSeqLen = 4;
// number of bytes to denote output value
static const int OutputValueLen = 8;
// number of bytes to denote a public key (compressed)
static const int PubKeyLen = 33;
// number of bytes to denote a public key hash
static const int PubKeyHashLen = 20;
// The following arguments can be generated using sample code at
// https://gist.github.com/scrypt-sv/f6882be580780a88984cee75dd1564c4.js
static const PrivKey privKey = PrivKey(0x621de38d9af72be8585d19584e3954d3fd0dc9752bb9f9fb28c4f9ed7c1e40ea);
static const PubKey pubKey = PubKey(b'02773aca113a3217b67a95d5b78b69bb6386ed443ea5decf0ba92c00d179291921');
// invK is the modular inverse of k, the ephemeral key
static const int invK = 0xa2103f96554aba49bbf581738d3b5a38c5a44b6238ffb54cfcca65b8c87ddc08;
// r is x coordinate of R, which is kG
static const int r = 0xf0fc43da25095812fcddde7d7cd353990c62b078e1493dc603961af25dfc6b60;
// rBigEndian is the signed magnitude representation of r, in big endian
static const bytes rBigEndian = b'00f0fc43da25095812fcddde7d7cd353990c62b078e1493dc603961af25dfc6b60';
// convert signed integer `n` to unsigned integer of `l` bytes, in little endian
static function toLEUnsigned(int n, int l) : bytes {
// one extra byte to accommodate possible negative sign byte
bytes m = num2bin(n, l + 1);
// remove sign byte
return m[0 : len(m) - 1];
}
// convert signed integer `n` to unsigned integer of `l` bytes, in big endian
static function toBEUnsigned(int n, static const int l) : bytes {
bytes m = toLEUnsigned(n, l);
return reverseBytes(m,l);
}
// convert bytes to unsigned integer, in sign-magnitude little endian
static function fromLEUnsigned(bytes b) : int {
// append positive sign byte. Util does not hurt even when sign bit is already positive
return unpack(b + b'00');
}
// build P2PKH script from public key hash
static function buildPublicKeyHashScript(Ripemd160 pubKeyHash) : bytes {
return OpCode.OP_DUP + OpCode.OP_HASH160 + pack(Util.PubKeyHashLen /* "OP_PUSHDATA0" */) + pubKeyHash + OpCode.OP_EQUALVERIFY + OpCode.OP_CHECKSIG;
}
// build false OPRETURN script from data payload
static function buildOpreturnScript(bytes data) : bytes {
return OpCode.OP_FALSE + OpCode.OP_RETURN + data;
}
// build P2PKH script from public key
static function pubKeyToP2PKH(PubKey pubKey) : bytes {
return Util.buildPublicKeyHashScript(hash160(pubKey));
}
// build a tx output from its script and satoshi amount
static function buildOutput(bytes outputScript, int outputSatoshis) : bytes {
return num2bin(outputSatoshis, Util.OutputValueLen) + Util.writeVarint(outputScript);
}
// wrapper for OP_PUSH_TX with customized sighash type
static function checkPreimageSigHashType(SigHashPreimage txPreimage, SigHashType sigHashType) : bool {
return Tx.checkPreimageAdvanced(txPreimage, privKey, pubKey, invK, r, rBigEndian, sigHashType);
}
// get the byte at the given index
static function getElemAt(bytes b, int idx) : bytes {
return b[idx : idx + 1];
}
// set the byte at the given index
static function setElemAt(bytes b, int idx, bytes byteValue) : bytes {
return b[: idx] + byteValue + b[idx + 1 :];
}
/*
* VarInt (variable integer) is used to encode fields of variable length in a bitcoin transaction
* https://learnmeabitcoin.com/technical/varint
*/
// read a VarInt field from the beginning of 'b'
static function readVarint(bytes b) : bytes {
int l = 0;
bytes ret = b'';
bytes header = b[0 : 1];
if (header == b'fd') {
l = Util.fromLEUnsigned(b[1 : 3]);
ret = b[3 : 3 + l];
}
else if (header == b'fe') {
l = Util.fromLEUnsigned(b[1 : 5]);
ret = b[5 : 5 + l];
}
else if (header == b'ff') {
l = Util.fromLEUnsigned(b[1 : 9]);
ret = b[9 : 9 + l];
}
else {
l = Util.fromLEUnsigned(b[0 : 1]);
ret = b[1 : 1 + l];
}
return ret;
}
// number of bytes of the VarInt field read from the beginning of 'b'
static function readVarintLen(bytes b) : int {
int len = 0;
bytes header = b[0 : 1];
if (header == b'fd') {
len = 3 + Util.fromLEUnsigned(b[1 : 3]);
}
else if (header == b'fe') {
len = 5 + Util.fromLEUnsigned(b[1 : 5]);
}
else if (header == b'ff') {
len = 9 + Util.fromLEUnsigned(b[1 : 9]);
}
else {
len = 1 + Util.fromLEUnsigned(b[0 : 1]);
}
return len;
}
// convert 'b' to a VarInt field, including the preceding length
static function writeVarint(bytes b) : bytes {
int n = len(b);
bytes header = b'';
if (n < 0xfd) {
header = Util.toLEUnsigned(n, 1);
}
else if (n < 0x10000) {
header = b'fd' + Util.toLEUnsigned(n, 2);
}
else if (n < 0x100000000) {
header = b'fe' + Util.toLEUnsigned(n, 4);
}
else if (n < 0x10000000000000000) {
header = b'ff' + Util.toLEUnsigned(n, 8);
}
return header + b;
}
/*
* util functions to parse every field of a sighash preimage
* Note: only to be used after preimage is validated
* spec is at https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md
*/
static function nVersion(SigHashPreimage preimage) : bytes {
return preimage[: 4];
}
static function hashPrevouts(SigHashPreimage preimage) : bytes {
return preimage[4 : 36];
}
static function hashSequence(SigHashPreimage preimage) : bytes {
return preimage[36 : 68];
}
static function outpoint(SigHashPreimage preimage) : bytes {
return preimage[68 : 104];
}
// scriptCode is just scriptPubKey if there is no CODESEPARATOR in the latter
static function scriptCode(SigHashPreimage preimage) : bytes {
return Util.readVarint(preimage[104 :]);
}
static function valueRaw(SigHashPreimage preimage) : bytes {
int l = len(preimage);
return preimage[l - 52 : l - 44];
}
static function value(SigHashPreimage preimage) : int {
return Util.fromLEUnsigned(Util.valueRaw(preimage));
}
static function nSequenceRaw(SigHashPreimage preimage) : bytes {
int l = len(preimage);
return preimage[l - 44 : l - 40];
}
static function nSequence(SigHashPreimage preimage) : int {
return Util.fromLEUnsigned(Util.nSequenceRaw(preimage));
}
static function hashOutputs(SigHashPreimage preimage) : bytes {
int l = len(preimage);
return preimage[l - 40 : l - 8];
}
static function nLocktimeRaw(SigHashPreimage preimage) : bytes {
int l = len(preimage);
return preimage[l - 8 : l - 4];
}
static function nLocktime(SigHashPreimage preimage) : int {
return Util.fromLEUnsigned(Util.nLocktimeRaw(preimage));
}
static function sigHashType(SigHashPreimage preimage) : SigHashType {
int l = len(preimage);
return SigHashType(preimage[l - 4 : l - 3]);
}
// Writes variable amount of data respecting minimal push rules
static function writeVarMinimalPushdata(bytes b) : bytes {
int n = len(b);
bytes header = b'';
// Reference: https://github.com/moneybutton/bsv/blob/bsv-legacy/lib/script/script.js#L1083
if (n == 0) {
}
else if (n == 1) {
int rawInt = Util.fromLEUnsigned(b);
if (rawInt >= 1 && rawInt <= 16) {
// If value is between 1 and 16 then use OP_1, OP_2...OP_16 to encode
header = Util.toLEUnsigned(80 + rawInt, 1);
}
else if (n == 1 && rawInt == 0x81) {
// Use OP_1NEGATE
header = Util.toLEUnsigned(79, 1);
}
}
else if (n < 76) {
// Use direct push
header = Util.toLEUnsigned(n, 1) + b;
}
else if (n <= 255) {
header = b'4c' + Util.toLEUnsigned(n, 1) + b;
}
else if (n <= 65535) {
header = b'4d' + Util.toLEUnsigned(n, 2) + b;
}
else {
header = b'4e' + Util.toLEUnsigned(n, 4) + b;
}
return header;
}
}