-
Notifications
You must be signed in to change notification settings - Fork 411
/
btcChain.se
139 lines (105 loc) · 4.94 KB
/
btcChain.se
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
inset('constants.se')
# btcChain is required by btcrelay and is a separate file to improve
# clarity: it has ancestor management and its
# main method is inMainChain() which is tested by test_btcChain
macro NUM_ANCESTOR_DEPTHS: 8
# list for internal usage only that allows a 32 byte blockHash to be looked up
# with a 32bit int
# This is not designed to be used for anything else, eg it contains all block
# hashes and nothing can be assumed about which blocks are on the main chain
data internalBlock[2^50]
# counter for next available slot in internalBlock
# 0 means no blocks stored yet and is used for the special of storing 1st block
# which cannot compute Bitcoin difficulty since it doesn't have the 2016th parent
data ibIndex
# save the ancestors for a block, as well as updating the height
# note: this is internal/private so make it into a macro
macro m_saveAncestors($blockHashArg, $hashPrevBlockArg):
with $blockHash = $blockHashArg:
with $hashPrevBlock = $hashPrevBlockArg:
self.internalBlock[self.ibIndex] = $blockHash
m_setIbIndex($blockHash, self.ibIndex)
self.ibIndex += 1
m_setHeight($blockHash, m_getHeight($hashPrevBlock) + 1)
# 8 indexes into internalBlock can be stored inside one ancestor (32 byte) word
$ancWord = 0
# the first ancestor is the index to hashPrevBlock, and write it to ancWord
$prevIbIndex = m_getIbIndex($hashPrevBlock)
m_mwrite32(ref($ancWord), $prevIbIndex)
# update ancWord with the remaining indexes
with $i = 1:
while $i < NUM_ANCESTOR_DEPTHS:
with $depth = m_getAncDepth($i):
if m_getHeight($blockHash) % $depth == 1:
m_mwrite32(ref($ancWord) + 4*$i, $prevIbIndex)
else:
m_mwrite32(ref($ancWord) + 4*$i, m_getAncestor($hashPrevBlock, $i))
$i += 1
# write the ancestor word to storage
self.block[$blockHash]._ancestor = $ancWord
# private (to prevent leeching)
# returns 1 if 'txBlockHash' is in the main chain, ie not a fork
# otherwise returns 0
def priv_inMainChain__(txBlockHash):
if msg.sender != self:
~invalid()
txBlockHeight = m_getHeight(txBlockHash)
# By assuming that a block with height 0 does not exist, we can do
# this optimization and immediate say that txBlockHash is not in the main chain.
# However, the consequence is that
# the genesis block must be at height 1 instead of 0 [see setInitialParent()]
if !txBlockHeight:
return(0)
return(self.priv_fastGetBlockHash__(txBlockHeight) == txBlockHash)
# private (to prevent leeching)
# callers must ensure 2 things:
# * blockHeight is greater than 0 (otherwise infinite loop since
# minimum height is 1)
# * blockHeight is less than the height of heaviestBlock, otherwise the
# heaviestBlock is returned
def priv_fastGetBlockHash__(blockHeight):
if msg.sender != self:
~invalid()
blockHash = self.heaviestBlock
anc_index = NUM_ANCESTOR_DEPTHS - 1
while m_getHeight(blockHash) > blockHeight:
while m_getHeight(blockHash) - blockHeight < m_getAncDepth(anc_index) && anc_index > 0:
anc_index -= 1
blockHash = self.internalBlock[m_getAncestor(blockHash, anc_index)]
return(blockHash)
#
# macros
#
# a block's _ancestor storage slot contains 8 indexes into internalBlock, so
# this macro returns the index that can be used to lookup the desired ancestor
# eg. for combined usage, self.internalBlock[m_getAncestor(someBlock, 2)] will
# return the block hash of someBlock's 3rd ancestor
macro m_getAncestor($blockHash, $whichAncestor):
div(sload(ref(self.block[$blockHash]._ancestor)) * 2**(32*$whichAncestor), BYTES_28)
# index should be 0 to 7, so this returns 1, 5, 25 ... 78125
macro m_getAncDepth($index):
5**$index
# write $int32 to memory at $addrLoc
# This is useful for writing 32bit ints inside one 32 byte word
macro m_mwrite32($addrLoc, $int32):
with $addr = $addrLoc:
with $fourBytes = $int32:
mstore8($addr, byte(28, $fourBytes))
mstore8($addr + 1, byte(29, $fourBytes))
mstore8($addr + 2, byte(30, $fourBytes))
mstore8($addr + 3, byte(31, $fourBytes))
# write $int24 to memory at $addrLoc
# This is useful for writing 24bit ints inside one 32 byte word
macro m_mwrite24($addrLoc, $int24):
with $addr = $addrLoc:
with $threeBytes = $int24:
mstore8($addr, byte(29, $threeBytes))
mstore8($addr + 1, byte(30, $threeBytes))
mstore8($addr + 2, byte(31, $threeBytes))
# write $int16 to memory at $addrLoc
# This is useful for writing 16bit ints inside one 32 byte word
macro m_mwrite16($addrLoc, $int16):
with $addr = $addrLoc:
with $twoBytes = $int16:
mstore8($addr, byte(30, $twoBytes))
mstore8($addr + 1, byte(31, $twoBytes))