-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathbtc_header.cairo
183 lines (162 loc) · 6.46 KB
/
btc_header.cairo
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
# %builtins range_check bitwise
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.uint256 import Uint256, uint256_lt
from utils import swap_endianness_64, get_target, prepare_hash
from sha256.sha256_contract import compute_sha256
from utils.array_comparison import arr_eq
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin
struct BTCHeader:
member version : felt
member previous : felt*
member merkle_root : felt*
member time : felt
member bits : felt
member nonce : felt
member data : felt*
end
# version : 4 bytes
# previous hash : 32 bytes
# merkle root : 32 bytes
# time : 4 bytes
# bits : 4 bytes
# nonce : 4 bytes
# Assuming data is the header packed as an array of 4 bytes
func prepare_header{range_check_ptr, bitwise_ptr : BitwiseBuiltin*}(data : felt*) -> (
res : BTCHeader
):
alloc_locals
let (previous : felt*) = alloc()
let (merkle_root : felt*) = alloc()
let (version) = swap_endianness_64(data[0], 4)
let (prev0) = swap_endianness_64(data[7] * 2 ** 32 + data[8], 8)
let (prev1) = swap_endianness_64(data[5] * 2 ** 32 + data[6], 8)
let (prev2) = swap_endianness_64(data[3] * 2 ** 32 + data[4], 8)
let (prev3) = swap_endianness_64(data[1] * 2 ** 32 + data[2], 8)
assert previous[0] = prev0
assert previous[1] = prev1
assert previous[2] = prev2
assert previous[3] = prev3
let (merkle0) = swap_endianness_64(data[15] * 2 ** 32 + data[16], 8)
let (merkle1) = swap_endianness_64(data[13] * 2 ** 32 + data[14], 8)
let (merkle2) = swap_endianness_64(data[11] * 2 ** 32 + data[12], 8)
let (merkle3) = swap_endianness_64(data[09] * 2 ** 32 + data[10], 8)
assert merkle_root[0] = merkle0
assert merkle_root[1] = merkle1
assert merkle_root[2] = merkle2
assert merkle_root[3] = merkle3
let (time) = swap_endianness_64(data[17], 4)
let (bits) = swap_endianness_64(data[18], 4)
let (nonce) = swap_endianness_64(data[19], 4)
local header : BTCHeader = BTCHeader(version, previous, merkle_root, time, bits, nonce, data)
return (header)
end
func process_header{range_check_ptr, bitwise_ptr : BitwiseBuiltin*}(
header : BTCHeader, prev_header_hash : felt*
) -> (current_header_hash : felt*):
alloc_locals
# WIP: Compute SHA256 of serialized header (big endian)
let header_bytes = header.data
let (tmp1, tmp2) = compute_sha256(header_bytes, 80)
let (spliced_tmp) = prepare_hash(Uint256(tmp1, tmp2))
let (tmpout1, tmpout2) = compute_sha256(spliced_tmp, 32) # Second hash
# TODO Cairo way to do endianness
local out1
local out2
%{
data = f'{ids.tmpout1:032x}{ids.tmpout2:032x}'
data = "".join(data[::-1])
ids.out2 = int(data[:32], 16)
ids.out1 = int(data[32:], 16)
%}
let (local curr_header_hash : felt*) = alloc()
assert curr_header_hash[0] = out1
assert curr_header_hash[1] = out2
# Verify previous block header with provided hash
let (prev_hash_eq) = arr_eq(prev_header_hash, 2, curr_header_hash, 2)
# assert prev_hash_eq = 1
# TODO: Verify difficulty target
# - Parse bits into target and convert to Uint256
let (target) = get_target(header.bits)
%{
print(hex(ids.out1), hex(ids.out2))
print(hex(ids.target.low), hex(ids.target.high))
%}
let hash = Uint256(out1, out2)
let (res) = uint256_lt(hash, target)
assert res = 1
# TODO: Verify difficulty target interval using timestamps
# TODO: Return current header hash
return (curr_header_hash)
end
# NOTE: Uncomment builtin directive on line 1 of this file to run with Makefile
func main{range_check_ptr, bitwise_ptr : BitwiseBuiltin*}():
alloc_locals
# Block 0
let (header_data0 : felt*) = alloc()
assert header_data0[0] = 16777216
assert header_data0[1] = 0
assert header_data0[2] = 0
assert header_data0[3] = 0
assert header_data0[4] = 0
assert header_data0[5] = 0
assert header_data0[6] = 0
assert header_data0[7] = 0
assert header_data0[8] = 0
assert header_data0[9] = 1000599037
assert header_data0[10] = 2054886066
assert header_data0[11] = 2059873342
assert header_data0[12] = 1735823201
assert header_data0[13] = 2143820739
assert header_data0[14] = 2290766130
assert header_data0[15] = 983546026
assert header_data0[16] = 1260281418
assert header_data0[17] = 699096905
assert header_data0[18] = 4294901789
assert header_data0[19] = 497822588
let (block_header0) = prepare_header(header_data0)
%{
print(f'version = {ids.block_header0.version:x}')
print('previous', list(map(hex, memory.get_range(ids.block_header0.previous, 4))))
print('merkle', list(map(hex, memory.get_range(ids.block_header0.merkle_root, 4))))
print(f'time = {ids.block_header0.time:x}')
print(f'bits = {ids.block_header0.bits:x}')
print(f'nonce = {ids.block_header0.nonce:x}')
%}
# Block 1
let (header_data1 : felt*) = alloc()
assert header_data1[0] = 16777216
assert header_data1[1] = 1877117962
assert header_data1[2] = 3069293426
assert header_data1[3] = 3248923206
assert header_data1[4] = 2925786959
assert header_data1[5] = 2468250469
assert header_data1[6] = 3780774044
assert header_data1[7] = 1758861568
assert header_data1[8] = 0
assert header_data1[9] = 2552254973
assert header_data1[10] = 508274500
assert header_data1[11] = 3149817870
assert header_data1[12] = 535696487
assert header_data1[13] = 2074190787
assert header_data1[14] = 1410070449
assert header_data1[15] = 3451258600
assert header_data1[16] = 1461927438
assert header_data1[17] = 1639736905
assert header_data1[18] = 4294901789
assert header_data1[19] = 31679129
let (block_header1) = prepare_header(header_data1)
%{
print(f'version = {ids.block_header1.version:x}')
print('previous', list(map(hex, memory.get_range(ids.block_header1.previous, 4))))
print('merkle', list(map(hex, memory.get_range(ids.block_header1.merkle_root, 4))))
print(f'time = {ids.block_header1.time:x}')
print(f'bits = {ids.block_header1.bits:x}')
print(f'nonce = {ids.block_header1.nonce:x}')
%}
let (zero_hash : felt*) = alloc()
assert zero_hash[0] = 0
assert zero_hash[1] = 0
let (block_hash0) = process_header(block_header0, zero_hash)
let (block_hash1) = process_header(block_header1, block_hash0)
return ()
end