-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBTC.sol
More file actions
299 lines (276 loc) · 11.9 KB
/
BTC.sol
File metadata and controls
299 lines (276 loc) · 11.9 KB
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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
// Bitcoin transaction parsing library
// Copyright 2016 rain <https://keybase.io/rain>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// https://en.bitcoin.it/wiki/Protocol_documentation#tx
//
// Raw Bitcoin transaction structure:
//
// field | size | type | description
// version | 4 | int32 | transaction version number
// n_tx_in | 1-9 | var_int | number of transaction inputs
// tx_in | 41+ | tx_in[] | list of transaction inputs
// n_tx_out | 1-9 | var_int | number of transaction outputs
// tx_out | 9+ | tx_out[] | list of transaction outputs
// lock_time | 4 | uint32 | block number / timestamp at which tx locked
//
// Transaction input (tx_in) structure:
//
// field | size | type | description
// previous | 36 | outpoint | Previous output transaction reference
// script_len | 1-9 | var_int | Length of the signature script
// sig_script | ? | uchar[] | Script for confirming transaction authorization
// sequence | 4 | uint32 | Sender transaction version
//
// OutPoint structure:
//
// field | size | type | description
// hash | 32 | char[32] | The hash of the referenced transaction
// index | 4 | uint32 | The index of this output in the referenced transaction
//
// Transaction output (tx_out) structure:
//
// field | size | type | description
// value | 8 | int64 | Transaction value (Satoshis)
// pk_script_len | 1-9 | var_int | Length of the public key script
// pk_script | ? | uchar[] | Public key as a Bitcoin script.
//
// Variable integers (var_int) can be encoded differently depending
// on the represented value, to save space. Variable integers always
// precede an array of a variable length data type (e.g. tx_in).
//
// Variable integer encodings as a function of represented value:
//
// value | bytes | format
// <0xFD (253) | 1 | uint8
// <=0xFFFF (65535)| 3 | 0xFD followed by length as uint16
// <=0xFFFF FFFF | 5 | 0xFE followed by length as uint32
// - | 9 | 0xFF followed by length as uint64
//
// Public key scripts `pk_script` are set on the output and can
// take a number of forms. The regular transaction script is
// called 'pay-to-pubkey-hash' (P2PKH):
//
// OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
//
// OP_x are Bitcoin script opcodes. The bytes representation (including
// the 0x14 20-byte stack push) is:
//
// 0x76 0xA9 0x14 <pubKeyHash> 0x88 0xAC
//
// The <pubKeyHash> is the ripemd160 hash of the sha256 hash of
// the public key, preceded by a network version byte. (21 bytes total)
//
// Network version bytes: 0x00 (mainnet); 0x6f (testnet); 0x34 (namecoin)
//
// The Bitcoin address is derived from the pubKeyHash. The binary form is the
// pubKeyHash, plus a checksum at the end. The checksum is the first 4 bytes
// of the (32 byte) double sha256 of the pubKeyHash. (25 bytes total)
// This is converted to base58 to form the publicly used Bitcoin address.
// Mainnet P2PKH transaction scripts are to addresses beginning with '1'.
//
// P2SH ('pay to script hash') scripts only supply a script hash. The spender
// must then provide the script that would allow them to redeem this output.
// This allows for arbitrarily complex scripts to be funded using only a
// hash of the script, and moves the onus on providing the script from
// the spender to the redeemer.
//
// The P2SH script format is simple:
//
// OP_HASH160 <scriptHash> OP_EQUAL
//
// 0xA9 0x14 <scriptHash> 0x87
//
// The <scriptHash> is the ripemd160 hash of the sha256 hash of the
// redeem script. The P2SH address is derived from the scriptHash.
// Addresses are the scriptHash with a version prefix of 5, encoded as
// Base58check. These addresses begin with a '3'.
pragma solidity ^0.4.11;
// parse a raw bitcoin transaction byte array
library BTC {
// Convert a variable integer into something useful and return it and
// the index to after it.
function parseVarInt(bytes txBytes, uint pos) returns (uint, uint) {
// the first byte tells us how big the integer is
var ibit = uint8(txBytes[pos]);
pos += 1; // skip ibit
if (ibit < 0xfd) {
return (ibit, pos);
} else if (ibit == 0xfd) {
return (getBytesLE(txBytes, pos, 16), pos + 2);
} else if (ibit == 0xfe) {
return (getBytesLE(txBytes, pos, 32), pos + 4);
} else if (ibit == 0xff) {
return (getBytesLE(txBytes, pos, 64), pos + 8);
}
}
// convert little endian bytes to uint
function getBytesLE(bytes data, uint pos, uint bits) returns (uint) {
if (bits == 8) {
return uint8(data[pos]);
} else if (bits == 16) {
return uint16(data[pos])
+ uint16(data[pos + 1]) * 2 ** 8;
} else if (bits == 32) {
return uint32(data[pos])
+ uint32(data[pos + 1]) * 2 ** 8
+ uint32(data[pos + 2]) * 2 ** 16
+ uint32(data[pos + 3]) * 2 ** 24;
} else if (bits == 64) {
return uint64(data[pos])
+ uint64(data[pos + 1]) * 2 ** 8
+ uint64(data[pos + 2]) * 2 ** 16
+ uint64(data[pos + 3]) * 2 ** 24
+ uint64(data[pos + 4]) * 2 ** 32
+ uint64(data[pos + 5]) * 2 ** 40
+ uint64(data[pos + 6]) * 2 ** 48
+ uint64(data[pos + 7]) * 2 ** 56;
}
}
// scan the full transaction bytes and return the first two output
// values (in satoshis) and addresses (in binary)
function getFirstTwoOutputs(bytes txBytes)
returns (uint, bytes20, uint, bytes20)
{
uint pos;
uint[] memory input_script_lens = new uint[](2);
uint[] memory output_script_lens = new uint[](2);
uint[] memory script_starts = new uint[](2);
uint[] memory output_values = new uint[](2);
bytes20[] memory output_addresses = new bytes20[](2);
pos = 4; // skip version
(input_script_lens, pos) = scanInputs(txBytes, pos, 0);
(output_values, script_starts, output_script_lens, pos) = scanOutputs(txBytes, pos, 2);
for (uint i = 0; i < 2; i++) {
var pkhash = parseOutputScript(txBytes, script_starts[i], output_script_lens[i]);
output_addresses[i] = pkhash;
}
return (output_values[0], output_addresses[0],
output_values[1], output_addresses[1]);
}
// Check whether `btcAddress` is in the transaction outputs *and*
// whether *at least* `value` has been sent to it.
// Check whether `btcAddress` is in the transaction outputs *and*
// whether *at least* `value` has been sent to it.
function checkValueSent(bytes txBytes, bytes20 btcAddress, uint value)
returns (bool,uint)
{
uint pos = 4; // skip version
(, pos) = scanInputs(txBytes, pos, 0); // find end of inputs
// scan *all* the outputs and find where they are
var (output_values, script_starts, output_script_lens,) = scanOutputs(txBytes, pos, 0);
// look at each output and check whether it at least value to btcAddress
for (uint i = 0; i < output_values.length; i++) {
var pkhash = parseOutputScript(txBytes, script_starts[i], output_script_lens[i]);
if (pkhash == btcAddress && output_values[i] >= value) {
return (true,output_values[i]);
}
}
}
// scan the inputs and find the script lengths.
// return an array of script lengths and the end position
// of the inputs.
// takes a 'stop' argument which sets the maximum number of
// outputs to scan through. stop=0 => scan all.
function scanInputs(bytes txBytes, uint pos, uint stop)
returns (uint[], uint)
{
uint n_inputs;
uint halt;
uint script_len;
(n_inputs, pos) = parseVarInt(txBytes, pos);
if (stop == 0 || stop > n_inputs) {
halt = n_inputs;
} else {
halt = stop;
}
uint[] memory script_lens = new uint[](halt);
for (var i = 0; i < halt; i++) {
pos += 36; // skip outpoint
(script_len, pos) = parseVarInt(txBytes, pos);
script_lens[i] = script_len;
pos += script_len + 4; // skip sig_script, seq
}
return (script_lens, pos);
}
// scan the outputs and find the values and script lengths.
// return array of values, array of script lengths and the
// end position of the outputs.
// takes a 'stop' argument which sets the maximum number of
// outputs to scan through. stop=0 => scan all.
function scanOutputs(bytes txBytes, uint pos, uint stop)
returns (uint[], uint[], uint[], uint)
{
uint n_outputs;
uint halt;
uint script_len;
(n_outputs, pos) = parseVarInt(txBytes, pos);
if (stop == 0 || stop > n_outputs) {
halt = n_outputs;
} else {
halt = stop;
}
uint[] memory script_starts = new uint[](halt);
uint[] memory script_lens = new uint[](halt);
uint[] memory output_values = new uint[](halt);
for (var i = 0; i < halt; i++) {
output_values[i] = getBytesLE(txBytes, pos, 64);
pos += 8;
(script_len, pos) = parseVarInt(txBytes, pos);
script_starts[i] = pos;
script_lens[i] = script_len;
pos += script_len;
}
return (output_values, script_starts, script_lens, pos);
}
// Slice 20 contiguous bytes from bytes `data`, starting at `start`
function sliceBytes20(bytes data, uint start) returns (bytes20) {
uint160 slice = 0;
for (uint160 i = 0; i < 20; i++) {
slice += uint160(data[i + start]) << (8 * (19 - i));
}
return bytes20(slice);
}
// returns true if the bytes located in txBytes by pos and
// script_len represent a P2PKH script
function isP2PKH(bytes txBytes, uint pos, uint script_len) returns (bool) {
return (script_len == 25) // 20 byte pubkeyhash + 5 bytes of script
&& (txBytes[pos] == 0x76) // OP_DUP
&& (txBytes[pos + 1] == 0xa9) // OP_HASH160
&& (txBytes[pos + 2] == 0x14) // bytes to push
&& (txBytes[pos + 23] == 0x88) // OP_EQUALVERIFY
&& (txBytes[pos + 24] == 0xac); // OP_CHECKSIG
}
// returns true if the bytes located in txBytes by pos and
// script_len represent a P2SH script
function isP2SH(bytes txBytes, uint pos, uint script_len) returns (bool) {
return (script_len == 23) // 20 byte scripthash + 3 bytes of script
&& (txBytes[pos + 0] == 0xa9) // OP_HASH160
&& (txBytes[pos + 1] == 0x14) // bytes to push
&& (txBytes[pos + 22] == 0x87); // OP_EQUAL
}
// Get the pubkeyhash / scripthash from an output script. Assumes
// pay-to-pubkey-hash (P2PKH) or pay-to-script-hash (P2SH) outputs.
// Returns the pubkeyhash/ scripthash, or zero if unknown output.
function parseOutputScript(bytes txBytes, uint pos, uint script_len)
returns (bytes20)
{
if (isP2PKH(txBytes, pos, script_len)) {
return sliceBytes20(txBytes, pos + 3);
} else if (isP2SH(txBytes, pos, script_len)) {
return sliceBytes20(txBytes, pos + 2);
} else {
return;
}
}
}