Endogenous Oracle: Specification
Jan 16, 2024
import pandas as pd
import hashlib
from binascii import unhexlify, hexlify
df = pd.read_csv('bitcoin_blocks.csv')
class Block:
def __init__(self, version, previous_block_hash, merkle_root, time, bits , nonce):
self.version = version
self.previous_block_hash = previous_block_hash
self.merkle_root = merkle_root
self.time = time
self.bits = bits
self.nonce = nonce
def __repr__(self):
return f'Block(version={self.version}, previous_block_hash={self.previous_block_hash}, merkle_root={self.merkle_root}, time={self.time}, bits={self.bits}, nonce={self.nonce})'
def toHash(self):
headerHex = self.version + self.previous_block_hash + self.merkle_root + self.time + self.bits + self.nonce
headerBin = unhexlify(headerHex)
headerHash = hashlib.sha256(hashlib.sha256(headerBin).digest()).digest()
return hexlify(headerHash).decode('utf-8')
# def toHex(i):
# return hex(i)
# def formatHex(h):
# return hex(int(h, 16))[2:]
def toTimestamp(x):
return int(pd.to_datetime(x).timestamp())
def toTarget(b):
big = bytearray(int(b, 16).to_bytes(4, 'little')).hex()
x = int(big[:2], 16)
y = int(big[2:], 16)
return y* 2**(8*(x - 3))
def toDifficulty(t):
return 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / t
def readBlock(row):
return Block(bytearray(int(row.version).to_bytes(4, 'little')).hex(),
bytearray(int(row.previous_block_hash, 16).to_bytes(32, 'little')).hex(),
bytearray(int(row.merkle_root, 16).to_bytes(32, 'little')).hex(),
bytearray(toTimestamp(row.time).to_bytes(4, 'little')).hex(),
bytearray(int(row.bits, 16).to_bytes(4, 'little')).hex(),
bytearray(int(row.nonce).to_bytes(4, 'little')).hex())
def flatten(xss):
return [x for xs in xss for x in xs]
class Contract:
def __init__(self, batchLength, startBlockNumber, startBlockHash, startTime):
self.startBlockNumber = startBlockNumber
self.batchLength = batchLength
self.storedBlocks = [{'hash': startBlockHash,
'accumulatedDifficulty': 0,
'timestamp': startTime}]
def __repr__(self):
return f'''
Contract
startBlockNumber: {self.startBlockNumber}
batchLength: {self.batchLength}
storedBlocks: {self.storedBlocks}
'''
def submit(self, data, start):
assert start > self.startBlockNumber
assert (start - self.startBlockNumber - 1) % self.batchLength == 0
index = (start - self.startBlockNumber - 1) // self.batchLength
assert len(self.storedBlocks) > index
prevHash = self.storedBlocks[index]['hash']
accumulatedDifficulty = self.storedBlocks[-1]['accumulatedDifficulty']
accumulatedDifficultyNew = self.storedBlocks[index]['accumulatedDifficulty']
blocks = []
for batch in data:
assert len(batch) == self.batchLength
for block in batch:
assert prevHash == block.previous_block_hash
blockHash = block.toHash()
target = toTarget(block.bits)
assert target >= int(bytearray(int(blockHash, 16).to_bytes(32, 'little')).hex(), 16)
accumulatedDifficultyNew += toDifficulty(target)
prevHash = blockHash
blocks.append({'hash': blockHash,
'accumulatedDifficulty': accumulatedDifficultyNew,
'timestamp': int(bytearray(int(block.time, 16).to_bytes(4, 'little')).hex(), 16)})
assert accumulatedDifficultyNew > accumulatedDifficulty
self.storedBlocks = self.storedBlocks[: index + 1] + blocks
def getBlock(self, blockNumber):
assert blockNumber >= self.startBlockNumber
assert (blockNumber - self.startBlockNumber) % self.batchLength == 0
index = (blockNumber - self.startBlockNumber) // self.batchLength
assert len(self.storedBlocks) > index
return self.storedBlocks[index]
if __name__ == '__main__':
row0 = df.iloc[0]
block0 = readBlock(row0)
contract = Contract(10, int(row0.height), block0.toHash(), 1704068978)
data = [[], []]
for i in range(1, 11):
row = df.iloc[i]
data[0].append(readBlock(row))
for i in range(11, 21):
row = df.iloc[i]
data[1].append(readBlock(row))
contract.submit(data, 823786 + 1)
data = [[]]
for i in range(21, 31):
row = df.iloc[i]
data[0].append(readBlock(row))
contract.submit(data, 823786 + 1 + 20)
data = [[], []]
for i in range(21, 31):
row = df.iloc[i]
data[0].append(readBlock(row))
for i in range(31, 41):
row = df.iloc[i]
data[1].append(readBlock(row))
contract.submit(data, 823786 + 1 + 20)