|
| 1 | +inset('constants.se') |
| 2 | + |
| 3 | +# btcChain is required by btcrelay and is a separate file to improve |
| 4 | +# clarity: it has ancestor management and its |
| 5 | +# main method is inMainChain() which is tested by test_btcChain |
| 6 | + |
| 7 | +macro NUM_ANCESTOR_DEPTHS: 8 |
| 8 | + |
| 9 | +# list for internal usage only that allows a 32 byte blockHash to be looked up |
| 10 | +# with a 32bit int |
| 11 | +# This is not designed to be used for anything else, eg it contains all block |
| 12 | +# hashes and nothing can be assumed about which blocks are on the main chain |
| 13 | +data internalBlock[2^50] |
| 14 | + |
| 15 | +# counter for next available slot in internalBlock |
| 16 | +# 0 means no blocks stored yet and is used for the special of storing 1st block |
| 17 | +# which cannot compute Bitcoin difficulty since it doesn't have the 2016th parent |
| 18 | +data ibIndex |
| 19 | + |
| 20 | + |
| 21 | +# save the ancestors for a block, as well as updating the height |
| 22 | +# ntoe: this is internal/private so make it into a macro |
| 23 | +macro m_saveAncestors($blockHashArg, $hashPrevBlockArg): |
| 24 | + with $blockHash = $blockHashArg: |
| 25 | + with $hashPrevBlock = $blockHashArg: |
| 26 | + self.internalBlock[self.ibIndex] = blockHash |
| 27 | + m_setIbIndex(blockHash, self.ibIndex) |
| 28 | + self.ibIndex += 1 |
| 29 | + |
| 30 | + m_setHeight(blockHash, m_getHeight(hashPrevBlock) + 1) |
| 31 | + |
| 32 | + # 8 indexes into internalBlock can be stored inside one ancestor (32 byte) word |
| 33 | + ancWord = 0 |
| 34 | + |
| 35 | + # the first ancestor is the index to hashPrevBlock, and write it to ancWord |
| 36 | + prevIbIndex = m_getIbIndex(hashPrevBlock) |
| 37 | + m_mwrite32(ref(ancWord), prevIbIndex) |
| 38 | + |
| 39 | + # update ancWord with the remaining indexes |
| 40 | + i = 1 |
| 41 | + while i < NUM_ANCESTOR_DEPTHS: |
| 42 | + depth = m_getAncDepth(i) |
| 43 | + |
| 44 | + if m_getHeight(blockHash) % depth == 1: |
| 45 | + m_mwrite32(ref(ancWord) + 4*i, prevIbIndex) |
| 46 | + else: |
| 47 | + m_mwrite32(ref(ancWord) + 4*i, m_getAncestor(hashPrevBlock, i)) |
| 48 | + i += 1 |
| 49 | + |
| 50 | + # write the ancestor word to storage |
| 51 | + self.block[blockHash]._ancestor = ancWord |
| 52 | + |
| 53 | + |
| 54 | +# returns 1 if 'txBlockHash' is in the main chain, ie not a fork |
| 55 | +# otherwise returns 0 |
| 56 | +def inMainChain(txBlockHash): |
| 57 | + txBlockHeight = m_getHeight(txBlockHash) |
| 58 | + |
| 59 | + # By assuming that a block with height 0 does not exist, we can do |
| 60 | + # this optimization and immediate say that txBlockHash is not in the main chain. |
| 61 | + # However, the consequence is that |
| 62 | + # the genesis block must be at height 1 instead of 0 [see setInitialParent()] |
| 63 | + if !txBlockHeight: |
| 64 | + return(0) |
| 65 | + |
| 66 | + return(self.fastGetBlockHash(txBlockHeight) == txBlockHash) |
| 67 | + |
| 68 | + |
| 69 | +# returns the blockHash at the given blockHeight |
| 70 | +# blockHeight must be greater than 0 (otherwise infinite loop since minimum height is 1) |
| 71 | +def getBlockHash(blockHeight): |
| 72 | + if blockHeight < 1 || blockHeight > m_getHeight(self.heaviestBlock): |
| 73 | + return(0) |
| 74 | + return(self.fastGetBlockHash(blockHeight)) |
| 75 | + |
| 76 | + |
| 77 | +# for internal use only since callers must ensure 2 things: |
| 78 | +# * blockHeight is greater than 0 (otherwise infinite loop since |
| 79 | +# minimum height is 1) |
| 80 | +# * blockHeight is less than the height of heaviestBlock, otherwise the |
| 81 | +# heaviestBlock is returned |
| 82 | +def fastGetBlockHash(blockHeight): |
| 83 | + blockHash = self.heaviestBlock |
| 84 | + anc_index = NUM_ANCESTOR_DEPTHS - 1 |
| 85 | + |
| 86 | + while m_getHeight(blockHash) > blockHeight: |
| 87 | + while m_getHeight(blockHash) - blockHeight < m_getAncDepth(anc_index) && anc_index > 0: |
| 88 | + anc_index -= 1 |
| 89 | + blockHash = self.internalBlock[m_getAncestor(blockHash, anc_index)] |
| 90 | + |
| 91 | + return(blockHash) |
| 92 | + |
| 93 | + |
| 94 | +# |
| 95 | +# macros |
| 96 | +# |
| 97 | + |
| 98 | + |
| 99 | +# a block's _ancestor storage slot contains 8 indexes into internalBlock, so |
| 100 | +# this macro returns the index that can be used to lookup the desired ancestor |
| 101 | +# eg. for combined usage, self.internalBlock[m_getAncestor(someBlock, 2)] will |
| 102 | +# return the block hash of someBlock's 3rd ancestor |
| 103 | +macro m_getAncestor($blockHash, $whichAncestor): |
| 104 | + div(sload(ref(self.block[$blockHash]._ancestor)) * 2**(32*$whichAncestor), BYTES_28) |
| 105 | + |
| 106 | + |
| 107 | +# index should be 0 to 7, so this returns 1, 5, 25 ... 78125 |
| 108 | +macro m_getAncDepth($index): |
| 109 | + 5**$index |
| 110 | + |
| 111 | + |
| 112 | +# write $int32 to memory at $addrLoc |
| 113 | +# This is useful for writing 32bit ints inside one 32 byte word |
| 114 | +macro m_mwrite32($addrLoc, $int32): |
| 115 | + with $addr = $addrLoc: |
| 116 | + with $fourBytes = $int32: |
| 117 | + mstore8($addr, byte(28, $fourBytes)) |
| 118 | + mstore8($addr + 1, byte(29, $fourBytes)) |
| 119 | + mstore8($addr + 2, byte(30, $fourBytes)) |
| 120 | + mstore8($addr + 3, byte(31, $fourBytes)) |
| 121 | + |
| 122 | + |
| 123 | +# write $int24 to memory at $addrLoc |
| 124 | +# This is useful for writing 24bit ints inside one 32 byte word |
| 125 | +macro m_mwrite24($addrLoc, $int24): |
| 126 | + with $addr = $addrLoc: |
| 127 | + with $threeBytes = $int24: |
| 128 | + mstore8($addr, byte(29, $threeBytes)) |
| 129 | + mstore8($addr + 1, byte(30, $threeBytes)) |
| 130 | + mstore8($addr + 2, byte(31, $threeBytes)) |
| 131 | + |
| 132 | + |
| 133 | +# write $int16 to memory at $addrLoc |
| 134 | +# This is useful for writing 16bit ints inside one 32 byte word |
| 135 | +macro m_mwrite16($addrLoc, $int16): |
| 136 | + with $addr = $addrLoc: |
| 137 | + with $twoBytes = $int16: |
| 138 | + mstore8($addr, byte(30, $twoBytes)) |
| 139 | + mstore8($addr + 1, byte(31, $twoBytes)) |
| 140 | + |
| 141 | + |
| 142 | +# log ancestors |
| 143 | +# def logAnc(blockHash): |
| 144 | +# log(11111) |
| 145 | +# log(blockHash) |
| 146 | +# i = 0 |
| 147 | +# while i < NUM_ANCESTOR_DEPTHS: |
| 148 | +# anc = m_getAncestor(blockHash, i) |
| 149 | +# # anc = self.block[blockHash]._ancestor[i] |
| 150 | +# log(anc) |
| 151 | +# i += 1 |
| 152 | +# log(22222) |
0 commit comments