diff --git a/beacon_chain_impl/bls.py b/beacon_chain_impl/bls.py new file mode 100644 index 0000000..7a26edd --- /dev/null +++ b/beacon_chain_impl/bls.py @@ -0,0 +1,107 @@ +from hashlib import blake2s +blake = lambda x: blake2s(x).digest() +from py_ecc.optimized_bn128 import G1, G2, add, multiply, FQ, FQ2, pairing, \ + normalize, field_modulus, b, b2, is_on_curve, curve_order + +def compress_G1(pt): + x, y = normalize(pt) + return x.n + 2**255 * (y.n % 2) + +def decompress_G1(p): + if p == 0: + return (FQ(1), FQ(1), FQ(0)) + x = p % 2**255 + y_mod_2 = p // 2**255 + y = pow((x**3 + b.n) % field_modulus, (field_modulus+1)//4, field_modulus) + assert pow(y, 2, field_modulus) == (x**3 + b.n) % field_modulus + if y%2 != y_mod_2: + y = field_modulus - y + return (FQ(x), FQ(y), FQ(1)) + +# 16th root of unity +hex_root = FQ2([21573744529824266246521972077326577680729363968861965890554801909984373949499, + 16854739155576650954933913186877292401521110422362946064090026408937773542853]) + +assert hex_root ** 8 != FQ2([1,0]) +assert hex_root ** 16 == FQ2([1,0]) + +def sqrt_fq2(x): + y = x ** ((field_modulus ** 2 + 15) // 32) + while y**2 != x: + y *= hex_root + return y + +def hash_to_G2(m): + k2 = m + while 1: + k1 = blake(k2) + k2 = blake(k1) + x1 = int.from_bytes(k1, 'big') % field_modulus + x2 = int.from_bytes(k2, 'big') % field_modulus + x = FQ2([x1, x2]) + xcb = x**3 + b2 + if xcb ** ((field_modulus ** 2 - 1) // 2) == FQ2([1,0]): + break + y = sqrt_fq2(xcb) + return multiply((x, y, FQ2([1,0])), 2*field_modulus-curve_order) + +def compress_G2(pt): + assert is_on_curve(pt, b2) + x, y = normalize(pt) + return (x.coeffs[0] + 2**255 * (y.coeffs[0] % 2), x.coeffs[1]) + +def decompress_G2(p): + x1 = p[0] % 2**255 + y1_mod_2 = p[0] // 2**255 + x2 = p[1] + x = FQ2([x1, x2]) + if x == FQ2([0, 0]): + return FQ2([1,0]), FQ2([1,0]), FQ2([0,0]) + y = sqrt_fq2(x**3 + b2) + if y.coeffs[0] % 2 != y1_mod_2: + y = y * -1 + assert is_on_curve((x, y, FQ2([1,0])), b2) + return x, y, FQ2([1,0]) + +def sign(m, k): + return compress_G2(multiply(hash_to_G2(m), k)) + +def privtopub(k): + return compress_G1(multiply(G1, k)) + +def verify(m, pub, sig): + return pairing(decompress_G2(sig), G1) == pairing(hash_to_G2(m), decompress_G1(pub)) + +def aggregate_sigs(sigs): + o = FQ2([1,0]), FQ2([1,0]), FQ2([0,0]) + for s in sigs: + o = add(o, decompress_G2(s)) + return compress_G2(o) + +def aggregate_pubs(pubs): + o = FQ(1), FQ(1), FQ(0) + for p in pubs: + o = add(o, decompress_G1(p)) + return compress_G1(o) + +for x in (1, 5, 124, 735, 127409812145, 90768492698215092512159, 0): + print('Testing with privkey %d' % x) + p1 = multiply(G1, x) + p2 = multiply(G2, x) + msg = str(x).encode('utf-8') + msghash = hash_to_G2(msg) + assert normalize(decompress_G1(compress_G1(p1))) == normalize(p1) + assert normalize(decompress_G2(compress_G2(p2))) == normalize(p2) + assert normalize(decompress_G2(compress_G2(msghash))) == normalize(msghash) + sig = sign(msg, x) + pub = privtopub(x) + assert verify(msg, pub, sig) + +print('Testing signature aggregation') +msg = b'cow' +keys = [1, 5, 124, 735, 127409812145, 90768492698215092512159, 0] +sigs = [sign(msg, k) for k in keys] +pubs = [privtopub(k) for k in keys] +aggsig = aggregate_sigs(sigs) +aggpub = aggregate_pubs(pubs) +assert verify(msg, aggpub, aggsig) diff --git a/beacon_chain_impl/full_pos.py b/beacon_chain_impl/full_pos.py new file mode 100644 index 0000000..992753d --- /dev/null +++ b/beacon_chain_impl/full_pos.py @@ -0,0 +1,68 @@ +from hashlib import blake2s +blake = lambda x: blake2s(x).digest() +from ethereum.utils import normalize_address, hash32, trie_root, \ + big_endian_int, address, int256, encode_hex, decode_hex, encode_int, \ + big_endian_to_int +from rlp.sedes import big_endian_int, Binary, binary, CountableList +import rlp +import bls +import random + +class BeaconBlock(rlp.Serializable): + + fields = [ + ('parent_hash', hash32), + ('skip_count', int256), + ('randao_reveal', hash32), + ('attestation_bitmask', binary), + ('attestation_aggregate_sig', int256), + ('ffg_signer_list', binary), + ('ffg_aggregate_sig', int256), + ('main_chain_ref', hash32), + ('state_hash', hash32), + ('height', int256), + ('sig', int256) + ] + + def __init__(self, + parent_hash=b'\x00'*32, skip_count=0, randao_reveal=b'\x00'*32, + attestation_bitmask=b'', attestation_aggregate_sig=0, + ffg_signer_list=b'', ffg_aggregate_sig=0, main_chain_ref=b'\x00'*32, + state_hash=b'\x00'*32, height=0, sig=0): + # at the beginning of a method, locals() is a dict of all arguments + fields = {k: v for k, v in locals().items() if k != 'self'} + super(BlockHeader, self).__init__(**fields) + +def quick_sample(seed, validator_count, sample_count): + k = 0 + while 256**k < n: + k += 1 + o = []; source = seed; pos = 0 + while len(o) < sample_count: + if pos + k > 32: + source = blake(source) + pos = 0 + m = big_endian_to_int(source[pos:pos+k]) + if n * (m // n + 1) <= 256**k: + o.append(m % n) + pos += k + return o + +privkeys = [int.from_bytes(blake2s(str(i).encode('utf-8'))) for i in range(3000)] + +def mock_make_child(parent_state, skips, ): + attest + + fields = [ + ('parent_hash', hash32), + ('skip_count', int256), + ('randao_reveal', hash32), + ('attestation_bitmask', binary), + ('attestation_aggregate_sig', int256), + ('ffg_signer_list', binary), + ('ffg_aggregate_sig', int256), + ('main_chain_ref', hash32), + ('state_hash', hash32), + ('height', int256), + ('sig', int256) + ] diff --git a/clock_disparity/node.py b/clock_disparity/node.py deleted file mode 100644 index 5ca4a7b..0000000 --- a/clock_disparity/node.py +++ /dev/null @@ -1,151 +0,0 @@ -import os -from binascii import hexlify -from Crypto.Hash import keccak -import random - -def to_hex(s): - return hexlify(s).decode('utf-8') - -memo = {} - -def sha3(x): - if x not in memo: - memo[x] = keccak.new(digest_bits=256, data=x).digest() - return memo[x] - -def hash_to_int(h): - o = 0 - for c in h: - o = (o << 8) + c - return o - -NOTARIES = 40 -BASE_TS_DIFF = 1 -SKIP_TS_DIFF = 6 -SAMPLE = 9 -MIN_SAMPLE = 5 -POWDIFF = 50 * NOTARIES -SHARDS = 12 - -def checkpow(work, nonce): - # Discrete log PoW, lolz - # Quadratic nonresidues only - return pow(work, nonce, 65537) * POWDIFF < 65537 * 2 and pow(nonce, 32768, 65537) == 65536 - -class MainChainBlock(): - def __init__(self, parent, pownonce, ts): - self.parent_hash = parent.hash if parent else (b'\x00' * 32) - assert isinstance(self.parent_hash, bytes) - self.hash = sha3(self.parent_hash + str(pownonce).encode('utf-8')) - self.ts = ts - if parent: - assert checkpow(parent.pownonce, pownonce) - assert self.ts >= parent.ts - self.pownonce = pownonce - self.number = 0 if parent is None else parent.number + 1 - - -main_genesis = MainChainBlock(None, 59049, 0) - -class Node(): - - def __init__(self, _id, network, sleepy=False, careless=False, ts=0): - self.blocks = { - main_genesis.hash: main_genesis - } - self.main_chain = [main_genesis.hash] - self.timequeue = [] - self.parentqueue = {} - self.children = {} - self.ts = ts - self.id = _id - self.network = network - self.used_parents = {} - self.processed = {} - self.sleepy = sleepy - self.careless = careless - - def broadcast(self, x): - if self.sleepy and self.ts: - return - self.network.broadcast(self, x) - self.on_receive(x) - - def log(self, words, lvl=3, all=False): - #if "Tick:" != words[:5] or self.id == 0: - if (self.id == 0 or all) and lvl >= 2: - print(self.id, words) - - def on_receive(self, obj, reprocess=False): - if obj.hash in self.processed and not reprocess: - return - self.processed[obj.hash] = True - if isinstance(obj, MainChainBlock): - return self.on_receive_main_block(obj) - - def add_to_timequeue(self, obj): - i = 0 - while i < len(self.timequeue) and self.timequeue[i].ts < obj.ts: - i += 1 - self.timequeue.insert(i, obj) - - def add_to_multiset(self, _set, k, v): - if k not in _set: - _set[k] = [] - _set[k].append(v) - - def change_head(self, chain, new_head): - chain.extend([None] * (new_head.number + 1 - len(chain))) - i, c = new_head.number, new_head.hash - while c != chain[i]: - chain[i] = c - c = self.blocks[c].parent_hash - i -= 1 - for i in range(len(chain)): - assert self.blocks[chain[i]].number == i - assert self.blocks[chain[i]].ts <= self.ts - - def process_children(self, h): - if h in self.parentqueue: - for b in self.parentqueue[h]: - self.on_receive(b, reprocess=True) - del self.parentqueue[h] - - def on_receive_main_block(self, block): - # Parent not yet received - if block.parent_hash not in self.blocks: - self.add_to_multiset(self.parentqueue, block.parent_hash, block) - return None - if block.ts > self.ts: - self.add_to_timequeue(block) - return None - self.log("Processing main chain block %s" % to_hex(block.hash[:4])) - self.blocks[block.hash] = block - # Reorg the main chain if new head - if block.number > self.blocks[self.main_chain[-1]].number: - reorging = (block.parent_hash != self.main_chain[-1]) - self.change_head(self.main_chain, block) - # Add child record - self.add_to_multiset(self.children, block.parent_hash, block.hash) - # Final steps - self.process_children(block.hash) - self.network.broadcast(self, block) - - def is_descendant(self, a, b): - a, b = self.blocks[a], self.blocks[b] - while b.number > a.number: - b = self.blocks[b.parent_hash] - return a.hash == b.hash - - def tick(self): - self.ts += 0.1 - self.log("Tick: %.1f" % self.ts, lvl=1) - # Process time queue - while len(self.timequeue) and self.timequeue[0].ts <= self.ts: - self.on_receive(self.timequeue.pop(0)) - # Attempt to mine a main chain block - pownonce = random.randrange(65537) - mchead = self.blocks[self.main_chain[-1]] - if checkpow(mchead.pownonce, pownonce): - assert self.ts >= mchead.ts - self.broadcast(MainChainBlock(mchead, pownonce, self.ts)) diff --git a/clock_disparity/test.py b/clock_disparity/test.py index a942e9a..549c95e 100644 --- a/clock_disparity/test.py +++ b/clock_disparity/test.py @@ -1,16 +1,18 @@ from networksim import NetworkSimulator -from node import Node, NOTARIES, MainChainBlock, main_genesis +from pos_node import Node, NOTARIES, Block, genesis from distributions import normal_distribution net = NetworkSimulator(latency=12) -notaries = [Node(i, net, ts=max(normal_distribution(50, 50)(), 0)) for i in range(NOTARIES)] +notaries = [Node(i, net, ts=max(normal_distribution(200, 200)(), 0) * 0.1, sleepy=i%4==0) for i in range(NOTARIES)] net.agents = notaries net.generate_peers() -for i in range(4000): +for i in range(100000): net.tick() for n in notaries: + print("Local timestamp: %.1f, timequeue len %d" % (n.ts, len(n.timequeue))) print("Main chain head: %d" % n.blocks[n.main_chain[-1]].number) - print("Total main chain blocks received: %d" % (len([b for b in n.blocks.values() if isinstance(b, MainChainBlock)]) - 1)) + print("Total main chain blocks received: %d" % (len([b for b in n.blocks.values() if isinstance(b, Block)]) - 1)) + print("Notarized main chain blocks received: %d" % (len([b for b in n.blocks.values() if isinstance(b, Block) and n.is_notarized(b)]) - 1)) import matplotlib.pyplot as plt import networkx as nx @@ -18,15 +20,22 @@ import random G=nx.Graph() -#positions = {main_genesis.hash: 0, beacon_genesis.hash: 0} +#positions = {genesis.hash: 0, beacon_genesis.hash: 0} #queue = [ for b in n.blocks.values(): + for en in notaries: + if isinstance(b, Block) and b.hash in en.processed and b.hash not in en.blocks: + assert (not en.have_ancestry(b.hash)) or b.ts > en.ts if b.number > 0: - if isinstance(b, MainChainBlock): - G.add_edge(b.hash, b.parent_hash, color='b') + if isinstance(b, Block): + if n.is_notarized(b): + G.add_edge(b.hash, b.parent_hash, color='b') + else: + G.add_edge(b.hash, b.parent_hash, color='#dddddd') -cache = {main_genesis.hash: 0} + +cache = {genesis.hash: 0} def mkoffset(b): if b.hash not in cache: diff --git a/sharding_fork_choice_poc/beacon_chain_node.py b/sharding_fork_choice_poc/beacon_chain_node.py deleted file mode 100644 index 2860f2e..0000000 --- a/sharding_fork_choice_poc/beacon_chain_node.py +++ /dev/null @@ -1,363 +0,0 @@ -import os -from binascii import hexlify -from Crypto.Hash import keccak -import random - -def to_hex(s): - return hexlify(s).decode('utf-8') - -memo = {} - -def sha3(x): - if x not in memo: - memo[x] = keccak.new(digest_bits=256, data=x).digest() - return memo[x] - -def hash_to_int(h): - o = 0 - for c in h: - o = (o << 8) + c - return o - -NOTARIES = 40 -BASE_TS_DIFF = 1 -SKIP_TS_DIFF = 6 -SAMPLE = 9 -MIN_SAMPLE = 5 -POWDIFF = 50 * NOTARIES -SHARDS = 12 - -def checkpow(work, nonce): - # Discrete log PoW, lolz - # Quadratic nonresidues only - return pow(work, nonce, 65537) * POWDIFF < 65537 * 2 and pow(nonce, 32768, 65537) == 65536 - -class MainChainBlock(): - def __init__(self, parent, pownonce, ts): - self.parent_hash = parent.hash if parent else (b'\x00' * 32) - assert isinstance(self.parent_hash, bytes) - self.hash = sha3(self.parent_hash + str(pownonce).encode('utf-8')) - self.ts = ts - if parent: - assert checkpow(parent.pownonce, pownonce) - assert self.ts >= parent.ts - self.pownonce = pownonce - self.number = 0 if parent is None else parent.number + 1 - - -# Not a full RANDAO; stub for now -class BeaconBlock(): - def __init__(self, parent, proposer, ts, sigs, main_chain_ref): - self.contents = os.urandom(32) - self.parent_hash = parent.hash if parent else (b'\x11' * 32) - self.hash = sha3(self.parent_hash + self.contents) - self.ts = ts - self.sigs = sigs - self.number = parent.number + 1 if parent else 0 - self.main_chain_ref = main_chain_ref.hash if main_chain_ref else parent.main_chain_ref - - if parent: - i = parent.child_proposers.index(proposer) - assert self.ts >= parent.ts + BASE_TS_DIFF + i * SKIP_TS_DIFF - assert len(sigs) >= parent.notary_req - for sig in sigs: - assert sig.target_hash == self.parent_hash - - # Calculate child proposers - v = hash_to_int(sha3(self.contents)) - self.child_proposers = [] - while v > 0: - self.child_proposers.append(v % NOTARIES) - v //= NOTARIES - - # Calculate notaries - first = parent and proposer == parent.child_proposers[0] - self.notary_req = 0 if first else MIN_SAMPLE - v = hash_to_int(sha3(self.contents + b':n')) - self.notaries = [] - for i in range(SAMPLE if first else SAMPLE): - self.notaries.append(v % NOTARIES) - v //= NOTARIES - - # Calculate shard proposers - v = hash_to_int(sha3(self.contents + b':s')) - self.shard_proposers = [] - for i in range(SHARDS): - self.shard_proposers.append(v % NOTARIES) - v //= NOTARIES - - -class Sig(): - def __init__(self, proposer, target): - self.proposer = proposer - self.target_hash = target.hash - self.hash = os.urandom(32) - assert self.proposer in target.notaries - -class ShardCollation(): - def __init__(self, shard_id, parent, proposer, beacon_ref, ts): - self.proposer = proposer - self.parent_hash = parent.hash if parent else (bytes([40 + shard_id]) * 32) - self.hash = sha3(self.parent_hash + str(self.proposer).encode('utf-8') + beacon_ref.hash) - self.ts = ts - self.shard_id = shard_id - self.number = parent.number + 1 if parent else 0 - self.beacon_ref = beacon_ref.hash - - if parent: - assert self.shard_id == parent.shard_id - assert self.proposer == beacon_ref.shard_proposers[self.shard_id] - assert self.ts >= parent.ts - - assert self.ts >= beacon_ref.ts - -main_genesis = MainChainBlock(None, 59049, 0) -beacon_genesis = BeaconBlock(None, 1, 0, [], main_genesis) -shard_geneses = [ShardCollation(i, None, 0, beacon_genesis, 0) for i in range(SHARDS)] - -class BlockMakingRequest(): - def __init__(self, parent, ts): - self.parent = parent - self.ts = ts - self.hash = os.urandom(32) - -class Node(): - - def __init__(self, _id, network, sleepy=False, careless=False): - self.blocks = { - beacon_genesis.hash: beacon_genesis, - main_genesis.hash: main_genesis - } - for s in shard_geneses: - self.blocks[s.hash] = s - self.sigs = {} - self.beacon_chain = [beacon_genesis.hash] - self.main_chain = [main_genesis.hash] - self.shard_chains = [[g.hash] for g in shard_geneses] - self.timequeue = [] - self.parentqueue = {} - self.children = {} - self.ts = 0 - self.id = _id - self.network = network - self.used_parents = {} - self.processed = {} - self.sleepy = sleepy - self.careless = careless - - def broadcast(self, x): - if self.sleepy and self.ts: - return - #self.log("Broadcasting %s %s" % ("block" if isinstance(x, BeaconBlock) else "sig", to_hex(x.hash[:4]))) - self.network.broadcast(self, x) - self.on_receive(x) - - def log(self, words, lvl=3, all=False): - #if "Tick:" != words[:5] or self.id == 0: - if (self.id == 0 or all) and lvl >= 2: - print(self.id, words) - - def on_receive(self, obj, reprocess=False): - if obj.hash in self.processed and not reprocess: - return - self.processed[obj.hash] = True - #self.log("Processing %s %s" % ("block" if isinstance(obj, BeaconBlock) else "sig", to_hex(obj.hash[:4]))) - if isinstance(obj, BeaconBlock): - return self.on_receive_beacon_block(obj) - elif isinstance(obj, MainChainBlock): - return self.on_receive_main_block(obj) - elif isinstance(obj, ShardCollation): - return self.on_receive_shard_collation(obj) - elif isinstance(obj, Sig): - return self.on_receive_sig(obj) - elif isinstance(obj, BlockMakingRequest): - if self.beacon_chain[-1] == obj.parent: - mc_ref = self.blocks[obj.parent] - for i in range(2): - if mc_ref.number == 0: - break - #mc_ref = self.blocks[mc_ref].parent_hash - x = BeaconBlock(self.blocks[obj.parent], self.id, self.ts, - self.sigs[obj.parent] if obj.parent in self.sigs else [], - self.blocks[self.main_chain[-1]]) - self.log("Broadcasting block %s" % to_hex(x.hash[:4])) - self.broadcast(x) - - def add_to_timequeue(self, obj): - i = 0 - while i < len(self.timequeue) and self.timequeue[i].ts < obj.ts: - i += 1 - self.timequeue.insert(i, obj) - - def add_to_multiset(self, _set, k, v): - if k not in _set: - _set[k] = [] - _set[k].append(v) - - def change_head(self, chain, new_head): - chain.extend([None] * (new_head.number + 1 - len(chain))) - i, c = new_head.number, new_head.hash - while c != chain[i]: - chain[i] = c - c = self.blocks[c].parent_hash - i -= 1 - for i in range(len(chain)): - assert self.blocks[chain[i]].number == i - - def recalculate_head(self, chain, condition): - while not condition(self.blocks[chain[-1]]): - chain.pop() - descendant_queue = [chain[-1]] - new_head = chain[-1] - while len(descendant_queue): - first = descendant_queue.pop(0) - if first in self.children: - for c in self.children[first]: - if condition(self.blocks[c]): - descendant_queue.append(c) - if self.blocks[first].number > self.blocks[new_head].number: - new_head = first - self.change_head(chain, self.blocks[new_head]) - for i in range(len(chain)): - assert condition(self.blocks[chain[i]]) - - def process_children(self, h): - if h in self.parentqueue: - for b in self.parentqueue[h]: - self.on_receive(b, reprocess=True) - del self.parentqueue[h] - - def on_receive_main_block(self, block): - # Parent not yet received - if block.parent_hash not in self.blocks: - self.add_to_multiset(self.parentqueue, block.parent_hash, block) - return None - self.log("Processing main chain block %s" % to_hex(block.hash[:4])) - self.blocks[block.hash] = block - # Reorg the main chain if new head - if block.number > self.blocks[self.main_chain[-1]].number: - reorging = (block.parent_hash != self.main_chain[-1]) - self.change_head(self.main_chain, block) - if reorging: - self.recalculate_head(self.beacon_chain, - lambda b: isinstance(b, BeaconBlock) and b.main_chain_ref in self.main_chain) - for i in range(SHARDS): - self.recalculate_head(self.shard_chains[i], - lambda b: isinstance(b, ShardCollation) and b.shard_id == i and b.beacon_ref in self.beacon_chain) - # Add child record - self.add_to_multiset(self.children, block.parent_hash, block.hash) - # Final steps - self.process_children(block.hash) - self.network.broadcast(self, block) - - def is_descendant(self, a, b): - a, b = self.blocks[a], self.blocks[b] - while b.number > a.number: - b = self.blocks[b.parent_hash] - return a.hash == b.hash - - def change_beacon_head(self, new_head): - self.log("Changed beacon head: %s" % new_head.number) - reorging = (new_head.parent_hash != self.beacon_chain[-1]) - self.change_head(self.beacon_chain, new_head) - if reorging: - for i in range(SHARDS): - self.recalculate_head(self.shard_chains[i], - lambda b: isinstance(b, ShardCollation) and b.shard_id == i and b.beacon_ref in self.beacon_chain) - # Produce shard collations? - for s in range(SHARDS): - if self.id == new_head.shard_proposers[s]: - sc = ShardCollation(s, self.blocks[self.shard_chains[s][-1]], self.id, new_head, self.ts) - assert sc.beacon_ref == new_head.hash - assert self.is_descendant(self.blocks[sc.parent_hash].beacon_ref, new_head.hash) - self.broadcast(sc) - for c in self.shard_chains[s]: - assert self.blocks[c].shard_id == s and self.blocks[c].beacon_ref in self.beacon_chain - - def on_receive_beacon_block(self, block): - # Parent not yet received - if block.parent_hash not in self.blocks: - self.add_to_multiset(self.parentqueue, block.parent_hash, block) - return - # Main chain parent not yet received - if block.main_chain_ref not in self.blocks: - self.add_to_multiset(self.parentqueue, block.main_chain_ref, block) - return - # Too early - if block.ts > self.ts: - self.add_to_timequeue(block) - return - # Check consistency of cross-link reference - assert self.is_descendant(self.blocks[block.parent_hash].main_chain_ref, block.main_chain_ref) - # Add the block - self.log("Processing beacon block %s" % to_hex(block.hash[:4])) - self.blocks[block.hash] = block - # Am I a notary, and is the block building on the head? Then broadcast a signature. - if block.parent_hash == self.beacon_chain[-1] or self.careless: - if self.id in block.notaries: - self.broadcast(Sig(self.id, block)) - # Check for sigs, add to head?, make a block? - if len(self.sigs.get(block.hash, [])) >= block.notary_req: - if block.number > self.blocks[self.beacon_chain[-1]].number and block.main_chain_ref in self.main_chain: - self.change_beacon_head(block) - if self.id in self.blocks[block.hash].child_proposers: - my_index = self.blocks[block.hash].child_proposers.index(self.id) - target_ts = block.ts + BASE_TS_DIFF + my_index * SKIP_TS_DIFF - self.add_to_timequeue(BlockMakingRequest(block.hash, target_ts)) - # Add child record - self.add_to_multiset(self.children, block.parent_hash, block.hash) - # Final steps - self.process_children(block.hash) - self.network.broadcast(self, block) - - def on_receive_sig(self, sig): - self.add_to_multiset(self.sigs, sig.target_hash, sig) - # Add to head? Make a block? - if sig.target_hash in self.blocks and len(self.sigs[sig.target_hash]) == self.blocks[sig.target_hash].notary_req: - block = self.blocks[sig.target_hash] - if block.number > self.blocks[self.beacon_chain[-1]].number and block.main_chain_ref in self.main_chain: - self.change_beacon_head(block) - if self.id in block.child_proposers: - my_index = block.child_proposers.index(self.id) - target_ts = block.ts + BASE_TS_DIFF + my_index * SKIP_TS_DIFF - self.log("Making block request for %.1f" % target_ts) - self.add_to_timequeue(BlockMakingRequest(block.hash, target_ts)) - # Rebroadcast - self.network.broadcast(self, sig) - - def on_receive_shard_collation(self, block): - # Parent not yet received - if block.parent_hash not in self.blocks: - self.add_to_multiset(self.parentqueue, block.parent_hash, block) - return None - # Beacon ref not yet received - if block.beacon_ref not in self.blocks: - self.add_to_multiset(self.parentqueue, block.beacon_ref, block) - return None - # Check consistency of cross-link reference - assert self.is_descendant(self.blocks[block.parent_hash].beacon_ref, block.beacon_ref) - self.log("Processing shard collation %s" % to_hex(block.hash[:4])) - self.blocks[block.hash] = block - # Set head if needed - if block.number > self.blocks[self.shard_chains[block.shard_id][-1]].number and block.beacon_ref in self.beacon_chain: - self.change_head(self.shard_chains[block.shard_id], block) - # Add child record - self.add_to_multiset(self.children, block.parent_hash, block.hash) - # Final steps - self.process_children(block.hash) - self.network.broadcast(self, block) - - def tick(self): - if self.ts == 0: - if self.id in beacon_genesis.notaries: - self.broadcast(Sig(self.id, beacon_genesis)) - self.ts += 0.1 - self.log("Tick: %.1f" % self.ts, lvl=1) - # Process time queue - while len(self.timequeue) and self.timequeue[0].ts <= self.ts: - self.on_receive(self.timequeue.pop(0)) - # Attempt to mine a main chain block - pownonce = random.randrange(65537) - mchead = self.blocks[self.main_chain[-1]] - if checkpow(mchead.pownonce, pownonce): - self.broadcast(MainChainBlock(mchead, pownonce, self.ts))