-
-
Save eorituz/a5b751522e1986aa733d23953ff69045 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import collections | |
import random | |
import json | |
import hashlib | |
def hexhash(x): | |
return '0x' + hashlib.sha224(str(x).encode('ASCII', errors='backslashreplace')).hexdigest()[:6] | |
TransferEvent = collections.namedtuple('TransferEvent', 'sender, receiver, amount') | |
class Accounts(object): | |
initial_supply = 0 | |
def __init__(self, num_accounts=0, copy_from=None): | |
self.balances = dict() | |
if copy_from: | |
self.balances = copy_from.balances.copy() | |
else: | |
self._gen_accounts(num_accounts) | |
self.initial_supply = self.supply | |
def _gen_accounts(self, num): | |
for i in range(num): | |
k = hexhash(i) | |
v = random.randint(1, 100) | |
self.balances[k] = v | |
@property | |
def supply(self): | |
return sum(abs(value) for value in self.balances.values()) | |
def transfer(self, sender, receiver, amount): | |
self.balances[sender] -= amount | |
self.balances[receiver] += amount | |
assert self.supply == self.initial_supply | |
def random_transfer(self): | |
"generates a valid random transfer" | |
while True: | |
sender = random.choice(list(self.balances.keys())) | |
if not self.balances[sender]: | |
continue | |
receiver = random.choice(list(self.balances.keys())) | |
if sender == receiver: | |
continue | |
amount = random.randint(1, self.balances[sender]) | |
self.transfer(sender, receiver, amount) | |
return TransferEvent(sender, receiver, amount) | |
class Block(object): | |
def __init__(self, prevblock=None, num_accounts=0): | |
if not prevblock: # genesis block | |
self.accounts = Accounts(num_accounts=num_accounts) | |
self.prevhash = hexhash(-1) | |
self.number = 0 | |
else: | |
self.accounts = Accounts(copy_from=prevblock.accounts) | |
self.number = prevblock.number + 1 | |
self.prevhash = prevblock.hash | |
self.transfers = [] | |
self.prevblock = prevblock | |
def copy_transfers(self, other, fraction=0.5): | |
assert isinstance(other, Block) | |
for t in other.transfers[:int(len(other.transfers) * fraction)]: | |
self.transfers.append(t) | |
self.accounts.transfer(t.sender, t.receiver, t.amount) | |
@property | |
def hash(self): | |
return hexhash(repr(self.__dict__)) | |
def random_transfers(self, num): | |
for i in range(num): | |
self.transfers.append(self.accounts.random_transfer()) | |
def serialize(self, include_balances=False): | |
s = dict(number=self.number, | |
hash=self.hash, | |
prevhash=self.prevhash, | |
transfers=[dict(x._asdict()) for x in self.transfers] | |
) | |
if include_balances or self.number == 0: | |
s['balances'] = self.accounts.balances | |
return s | |
def gen_chain(height, p_revert, num_accounts, max_transfers): | |
head = Block(num_accounts=num_accounts) | |
chain = [head] | |
while head.number < height: | |
if head.number > 0 and random.random() < p_revert: | |
head = head.prevblock | |
else: | |
head = Block(prevblock=head) | |
# check if the last block is a sibling (same block height) | |
if len(chain) > 2 and chain[-1].number == head.number: | |
sibling = chain[-1] | |
# include some of its txs | |
head.copy_transfers(sibling, 0.5) | |
head.random_transfers(random.randint(0, max_transfers / 2)) | |
else: | |
head.random_transfers(random.randint(0, max_transfers)) | |
chain.append(head) | |
return chain | |
def longest_revert(chain): | |
heighest = 0 | |
longest = 0 | |
for block in chain: | |
heighest = max(heighest, block.number) | |
longest = max(longest, heighest - block.number) | |
return longest | |
random.seed(43) | |
chain = gen_chain(height=10, p_revert=0.5, num_accounts=100, max_transfers=10) | |
serialized_blocks = [b.serialize(include_balances=False) for b in chain] | |
# random.shuffle(serialized_blocks) | |
print(json.dumps(serialized_blocks, indent=4, sort_keys=True)) | |
print('blocks: {} max reverted:{}'.format(len(chain), longest_revert(chain))) | |
txs = [] | |
for block in set(chain): | |
txs.extend(block.transfers) | |
print('total transfers:{} unique transfers:{}'.format(len(txs), len(set(txs)))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment