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
|
# Copyright (C) 2016-2018 The OpenTimestamps developers
#
# This file is part of python-opentimestamps.
#
# It is subject to the license terms in the LICENSE file found in the top-level
# directory of this distribution.
#
# No part of python-opentimestamps including this file, may be copied,
# modified, propagated, or distributed except according to the terms contained
# in the LICENSE file.
from opentimestamps.core.timestamp import Timestamp, cat_sha256d
from opentimestamps.core.op import OpPrepend
from opentimestamps.core.notary import BitcoinBlockHeaderAttestation
def __make_btc_block_merkle_tree(blk_txids):
assert len(blk_txids) > 0
digests = blk_txids
while len(digests) > 1:
# The famously broken Satoshi algorithm: if the # of digests at this
# level is odd, double the last one.
if len(digests) % 2:
digests.append(digests[-1].msg)
next_level = []
for i in range(0,len(digests), 2):
next_level.append(cat_sha256d(digests[i], digests[i + 1]))
digests = next_level
return digests[0]
def make_timestamp_from_block(digest, block, blockheight, *, max_tx_size=1000):
"""Make a timestamp for a message in a block
Every transaction within the block is serialized and checked to see if the
raw serialized bytes contain the message. If one or more transactions do,
the smallest transaction is used to create a timestamp proof for that
specific message to the block header.
To limit the maximum size of proof, transactions larger than `max_tx_size`
are ignored.
Returns a timestamp for that message on success, None on failure.
"""
# Note how strategy changes if we add SHA256 midstate support
len_smallest_tx_found = max_tx_size + 1
commitment_tx = None
prefix = None
suffix = None
for tx in block.vtx:
serialized_tx = tx.serialize(params={'include_witness':False})
if len(serialized_tx) > len_smallest_tx_found:
continue
try:
i = serialized_tx.index(digest)
except ValueError:
continue
# Found it!
commitment_tx = tx
prefix = serialized_tx[0:i]
suffix = serialized_tx[i + len(digest):]
len_smallest_tx_found = len(serialized_tx)
if len_smallest_tx_found > max_tx_size:
return None
digest_timestamp = Timestamp(digest)
# Add the commitment ops necessary to go from the digest to the txid op
prefix_stamp = digest_timestamp.ops.add(OpPrepend(prefix))
txid_stamp = cat_sha256d(prefix_stamp, suffix)
assert commitment_tx.GetTxid() == txid_stamp.msg
# Create the txid list, with our commitment txid op in the appropriate
# place
block_txid_stamps = []
for tx in block.vtx:
if tx.GetTxid() != txid_stamp.msg:
block_txid_stamps.append(Timestamp(tx.GetTxid()))
else:
block_txid_stamps.append(txid_stamp)
# Build the merkle tree
merkleroot_stamp = __make_btc_block_merkle_tree(block_txid_stamps)
assert merkleroot_stamp.msg == block.hashMerkleRoot
attestation = BitcoinBlockHeaderAttestation(blockheight)
merkleroot_stamp.attestations.add(attestation)
return digest_timestamp
|