From 91aaea20f017570c84233ef96850cde6309de8fb Mon Sep 17 00:00:00 2001 From: Vitalik Buterin Date: Tue, 20 Jun 2017 02:36:22 -0400 Subject: [PATCH] Reorganized research repo --- bloom.py | 55 ---- casper.py | 245 ------------------ eip86/eip_86_basic_account.se | 28 ++ forwarder.py | 39 --- {casper => old_casper_poc1}/README.md | 0 {casper => old_casper_poc1}/casper.py | 0 {casper => old_casper_poc1}/distributions.py | 0 {casper => old_casper_poc1}/networksim.py | 0 {casper => old_casper_poc1}/run.py | 0 .../voting_strategy.py | 0 {casper3 => old_casper_poc3}/casper.py | 0 {casper3 => old_casper_poc3}/distributions.py | 0 {casper3 => old_casper_poc3}/networksim.py | 0 .../selfish_mining_test.py | 0 {casper3 => old_casper_poc3}/test.py | 0 ghost.py => pow_research/ghost.py | 0 .../multi_uncle_ghost.py | 0 .../random_circuit.py | 0 .../random_graphs.py | 0 .../selfish_mining_strats.py | 0 {stability => price_analysis}/csvgen.py | 0 .../diff_and_price.csv | 0 {stability => price_analysis}/fit.py | 0 .../price_analyzer.py | 0 .../price_grabber.py | 0 {stability => price_analysis}/spread.py | 0 slasher_v2_sim.py | 23 -- slasher_withholding_exploit.py | 43 --- bintrie.py => trie_research/bintrie.py | 0 .../bintrie_sample.py | 0 30 files changed, 28 insertions(+), 405 deletions(-) delete mode 100644 bloom.py delete mode 100644 casper.py create mode 100644 eip86/eip_86_basic_account.se delete mode 100644 forwarder.py rename {casper => old_casper_poc1}/README.md (100%) rename {casper => old_casper_poc1}/casper.py (100%) rename {casper => old_casper_poc1}/distributions.py (100%) rename {casper => old_casper_poc1}/networksim.py (100%) rename {casper => old_casper_poc1}/run.py (100%) rename {casper => old_casper_poc1}/voting_strategy.py (100%) rename {casper3 => old_casper_poc3}/casper.py (100%) rename {casper3 => old_casper_poc3}/distributions.py (100%) rename {casper3 => old_casper_poc3}/networksim.py (100%) rename casper_sm/test.py => old_casper_poc3/selfish_mining_test.py (100%) rename {casper3 => old_casper_poc3}/test.py (100%) rename ghost.py => pow_research/ghost.py (100%) rename multi_uncle_ghost.py => pow_research/multi_uncle_ghost.py (100%) rename random_circuit.py => pow_research/random_circuit.py (100%) rename random_graphs.py => pow_research/random_graphs.py (100%) rename selfish_mining_strats.py => pow_research/selfish_mining_strats.py (100%) rename {stability => price_analysis}/csvgen.py (100%) rename {stability => price_analysis}/diff_and_price.csv (100%) rename {stability => price_analysis}/fit.py (100%) rename price_analyzer.py => price_analysis/price_analyzer.py (100%) rename price_grabber.py => price_analysis/price_grabber.py (100%) rename {stability => price_analysis}/spread.py (100%) delete mode 100644 slasher_v2_sim.py delete mode 100644 slasher_withholding_exploit.py rename bintrie.py => trie_research/bintrie.py (100%) rename bintrie_sample.py => trie_research/bintrie_sample.py (100%) diff --git a/bloom.py b/bloom.py deleted file mode 100644 index ffccf75..0000000 --- a/bloom.py +++ /dev/null @@ -1,55 +0,0 @@ -import random - -try: # - shathree = __import__('sha3').sha3_256 -except: - shathree = __import__('python_sha3').sha3_256 - - -params = { - "size": 256, - "pecks": 32 -} - - -def decode_int(x): - o = 0 - for a in x: - o = o * 256 + ord(a) - return o - - -def sha3(x): - return shathree(x).digest() - - -def bloom_insert(params, bloom, val): - k = decode_int(sha3(val)) * (3**160 + 112) - for i in range(params["pecks"]): - bloom |= 1 << (k % params["size"]) - k //= params["size"] - return bloom - - -def bloom_query(params, bloom, val): - o = bloom_insert(params, 0, val) - return (bloom & o) == o - - -def test_params(size, pecks, objcount): - params = {"size": size, "pecks": pecks} - count = 0 - for i in range(100): - objs = [str(random.randrange(2**40)) for i in range(objcount)] - bloom = 0 - for o in objs: - bloom = bloom_insert(params, bloom, o) - - for o in objs: - assert bloom_query(params, bloom, o) - - for i in range(100): - if bloom_query(params, bloom, str(random.randrange(2**40))): - count += 1 - - print 'False positive rate: %f' % (count / 10000.) diff --git a/casper.py b/casper.py deleted file mode 100644 index 1b9a329..0000000 --- a/casper.py +++ /dev/null @@ -1,245 +0,0 @@ -import copy, random, hashlib -# GhostTable: { block number: { block: validators } } -# The ghost table represents the entire current "view" of a user, and -# every block produced contains the producer's ghost table at the time. - -# Signature slashing rules (not implemented) -# 1. Sign two blocks at the same height -# 2. Sign an invalid block -# 3. Sign a block which fully confirms A at height H, and sign B at height H - -h = [3**50] -ids = [0] - -# Number of validators -NUM_VALIDATORS = 50 -# Block time in ticks (eg. 1 tick = 0.1 seconds) -BLKTIME = 30 -# Disparity in the blocks of nodes -CLOCK_DISPARITY = 10 -# An exponential distribution for latency offset by a minimum -LATENCY_MIN = 4 -LATENCY_BASE = 2 -LATENCY_PROB = 0.25 - - -def assign_hash(): - h[0] = (h[0] * 3) % 2**48 - return h[0] - - -def assign_id(): - ids[0] += 1 - return ids[0] - 1 - - -def latency_distribution_sample(): - v = LATENCY_BASE - while random.random() > LATENCY_PROB: - v *= 2 - return v + LATENCY_MIN - - -def clock_offset_distribution_sample(): - return random.randrange(-CLOCK_DISPARITY, CLOCK_DISPARITY) - - -# A signature represents the entire "view" of a signer, -# where the view is the set of blocks that the signer -# considered most likely to be valid at the time that -# they were produced -class Signature(): - - def __init__(self, signer, view): - self.signer = signer - self.view = copy.deepcopy(view) - - -# A ghost table represents the view that a user had of the signatures -# available at the time that the block was produced. -class GhostTable(): - - def __init__(self): - self.confirmed = [] - self.unconfirmed = [] - - def process_signature(self, sig): - # Process every block height in the signature - for i in range(len(self.confirmed), len(sig.view)): - # A ghost table entry at a height is a mapping of - # block hash -> signers - if i >= len(self.unconfirmed): - self.unconfirmed.append({}) - cur_entry = self.unconfirmed[i] - # If the block hash is not yet in the ghost table, add it, and - # initialize it with an empty signer set - if sig.view[i] not in cur_entry: - cur_entry[sig.view[i]] = {} - # Add the signer - cur_entry[sig.view[i]][sig.signer] = True - # If it has 67% signatures, finalize - if len(cur_entry[sig.view[i]]) > NUM_VALIDATORS * 2 / 3: - # prevgt = block_map[sig.view[i]].gt - prevgt = self - print 'confirmed', block_map[sig.view[i]].height, sig.view[i] - # Update blocks between the previous confirmation and the - # current confirmation based on the newly confirmed block's - # ghost table - for j in range(len(self.confirmed), i): - # At each intermediate height, add the block for which we - # havethe most signatures - maxkey, maxval = 0, 0 - for k in prevgt.unconfirmed[j]: - if len(prevgt.unconfirmed[j][k]) > maxval: - maxkey, maxval = k, len(prevgt.unconfirmed[j][k]) - self.confirmed.append(maxkey) - print j, {k: len(prevgt.unconfirmed[j][k]) for k in prevgt.unconfirmed[j]} - # Then add the new block that got 67% signatures - print i, sig.view[i] - self.confirmed.append(sig.view[i]) - - # Hash of the ghost table's contents (to make sure that it's not - # being modified when it's already supposed to be set in stone) - def hash(self): - print hashlib.sha256(repr(self.unconfirmed) + - repr(self.confirmed)).hexdigest()[:15] - - # Create a new ghost table that appends to an existing ghost table, adding - # some set of signatures - def append(self, sigs): - x = GhostTable() - x.confirmed = copy.deepcopy(self.confirmed) - x.unconfirmed = copy.deepcopy(self.unconfirmed) - for sig in sigs: - x.process_signature(sig) - return x - - -class Block(): - - def __init__(self, h, gt, maker): - self.gt = gt - self.height = h - self.maker = maker - self.hash = assign_hash() - - -class Validator(): - - def __init__(self): - self.gt = GhostTable() - self.view = [] - self.id = assign_id() - self.new_sigs = [] - self.clock_offset = clock_offset_distribution_sample() - self.last_block_produced = -99999 - self.last_unseen = 0 - - # Is this block compatible with our view? - def is_compatible_with_view(self, block): - return block.height >= len(self.view) or \ - self.view[block.height] is None - - # Add a block to this validator's view of probably valid - # blocks - def add_to_view(self, block): - while len(self.view) <= block.height: - self.view.append(None) - self.view[block.height] = block.hash - while self.last_unseen < len(self.view) and \ - self.view[self.last_unseen] is not None: - self.last_unseen += 1 - - # Make a block - def produce_block(self): - self.gt = self.gt.append(self.new_sigs) - newblk = Block(self.last_unseen, self.gt, self.id) - print 'newblk', newblk.height - self.add_to_view(newblk) - publish(newblk) - newsig = Signature(self.id, self.view[:self.last_unseen]) - self.new_sigs = [newsig] - publish(newsig) - - # Callback function upon receiving a block - def on_receive(self, obj): - if isinstance(obj, Block): - desired_maker = (self.time() // BLKTIME) % NUM_VALIDATORS - if 0 <= (desired_maker - obj.maker) % 100 <= 0: - if self.is_compatible_with_view(obj): - self.add_to_view(obj) - publish(Signature(self.id, self.view[:self.last_unseen])) - if isinstance(obj, Signature): - self.new_sigs.append(obj) - - # Do everything that you need to do in this particular round - def tick(self): - if (self.time() // BLKTIME) % NUM_VALIDATORS == self.id: - if self.time() - self.last_block_produced > \ - BLKTIME * NUM_VALIDATORS: - self.produce_block() - self.last_block_produced = self.time() - - # Calculate the validator's own clock based on the actual time - # plus a time offset that this validator happens to be wrong by - # (eg. +1 second) - def time(self): - return real_time[0] + self.clock_offset - - -block_map = {} -listening_queue = {} -real_time = [0] -validators = {} - - -# Publish a block or a signature -def publish(obj): - if isinstance(obj, Block): - block_map[obj.hash] = obj - # For every validator, add it to the validator's listening queue - # at a time randomly sampled from the latency distribution - for v in validators: - arrival_time = real_time[0] + latency_distribution_sample() - if arrival_time not in listening_queue: - listening_queue[arrival_time] = [] - listening_queue[arrival_time].append((v, obj)) - - -# One round of the clock ticking -def tick(): - for _, v in validators.items(): - v.tick() - if real_time[0] in listening_queue: - for validator_id, obj in listening_queue[real_time[0]]: - validators[validator_id].on_receive(obj) - real_time[0] += 1 - print real_time[0] - - -# Main function: run(7000) = simulate casper for 7000 ticks -def run(steps): - for k in block_map.keys(): - del block_map[k] - for k in listening_queue.keys(): - del listening_queue[k] - for k in validators.keys(): - del validators[k] - real_time[0] = 0 - ids[0] = 0 - for i in range(NUM_VALIDATORS): - v = Validator() - validators[v.id] = v - for i in range(steps): - tick() - c = [] - for _, v in validators.items(): - for i, b in enumerate(v.gt.confirmed): - assert block_map[b].height == i - if v.gt.confirmed[:len(c)] != c[:len(v.gt.confirmed)]: - for i in range(min(len(c), len(v.gt.confirmed))): - if c[i] != v.gt.confirmed[i]: - print i, c[i], v.gt.confirmed[i] - raise Exception("Confirmed block list mismatch") - c.extend(v.gt.confirmed[len(c):]) - print c diff --git a/eip86/eip_86_basic_account.se b/eip86/eip_86_basic_account.se new file mode 100644 index 0000000..573e460 --- /dev/null +++ b/eip86/eip_86_basic_account.se @@ -0,0 +1,28 @@ +# Expects input 224+x bytes: v, r, s, nonce, gasprice, to, value, data +with zero = ~mload(0): + # Anti re-entrancy + ~jumpi(~pc(), msg.sender != ~sub(zero, 1)) + # Copy calldata + ~calldatacopy(32, zero, ~calldatasize()) + # Compute sighash + ~mstore(zero, ~sha3(32, 32 + ~calldatasize())) + # Do elliptic curve verification + ~call(3000, 1, zero, zero, 128, zero, 32) + # Memory: hash, v, r, s, nonce, gasprice, to, value, data + # Check sig is correct + ~jumpi(~pc(), ~mload(zero) != 0xfe2ec957647679d210034b65e9c7db2452910b0c) + with s = ~sload(zero): + # Check nonce is correct + ~jumpi(~pc(), s != ~mload(128)) + # Increment nonce + ~sstore(zero, s + 1) + with gasprice = ~mload(160): + # Check balance + ~jumpi(~pc(), self.balance < gasprice * msg.gas) + with g1 = msg.gas: + # Make the main call + ~call(msg.gas - 25000, ~mload(192), ~mload(224), 256, ~calldatasize() - 224, zero, 10000) + # Pay the miner + ~call(zero, block.coinbase, (g1 - msg.gas + 5000) * gasprice, zero, zero, zero, zero) + # Log to establish that the tx passed through successfully + ~log0(zero, zero) diff --git a/forwarder.py b/forwarder.py deleted file mode 100644 index 786e311..0000000 --- a/forwarder.py +++ /dev/null @@ -1,39 +0,0 @@ -from ethereum import utils - -def mk_forwarder(address): - code = b'\x36\x60\x00\x60\x00\x37' # CALLDATACOPY 0 0 (CALLDATASIZE) - code += b'\x61\x10\x00\x60\x00\x36\x60\x00' # 4096 0 CALLDATASIZE 0 - code += b'\x73' + utils.normalize_address(address) + b'\x5a' # address gas - code += b'\xf4' # delegatecall - code += b'\x61\x10\x00\x60\x00\xf3' # 4096 0 RETURN - return code - -def mk_wrapper(code): - lencodepush = b'\x60' + utils.encode_int(len(code)) # length of code - returner = lencodepush + b'\x60\x0c\x60\x00' # start from 12 in code, 0 in memory - returner += b'\x39' # CODECOPY - returner += lencodepush + b'\x60\x00' + b'\xf3' # return code - assert len(returner) == 12 - return returner + code - -kode = """ -moose: num -def increment_moose(i: num) -> num: - self.moose += i - return self.moose -""" - -def test(): - from ethereum.tools import tester2 - c = tester2.Chain() - x = c.contract(kode, language='viper', sender=tester2.k3) - fwdcode = mk_forwarder(x.address) - initcode = mk_wrapper(fwdcode) - y = c.contract(initcode, language='evm') - assert c.head_state.get_code(y) == fwdcode - z = tester2.ABIContract(c, x.translator, y) - assert z.increment_moose(3) == 3 - assert z.increment_moose(5) == 8 - -if __name__ == '__main__': - test() diff --git a/casper/README.md b/old_casper_poc1/README.md similarity index 100% rename from casper/README.md rename to old_casper_poc1/README.md diff --git a/casper/casper.py b/old_casper_poc1/casper.py similarity index 100% rename from casper/casper.py rename to old_casper_poc1/casper.py diff --git a/casper/distributions.py b/old_casper_poc1/distributions.py similarity index 100% rename from casper/distributions.py rename to old_casper_poc1/distributions.py diff --git a/casper/networksim.py b/old_casper_poc1/networksim.py similarity index 100% rename from casper/networksim.py rename to old_casper_poc1/networksim.py diff --git a/casper/run.py b/old_casper_poc1/run.py similarity index 100% rename from casper/run.py rename to old_casper_poc1/run.py diff --git a/casper/voting_strategy.py b/old_casper_poc1/voting_strategy.py similarity index 100% rename from casper/voting_strategy.py rename to old_casper_poc1/voting_strategy.py diff --git a/casper3/casper.py b/old_casper_poc3/casper.py similarity index 100% rename from casper3/casper.py rename to old_casper_poc3/casper.py diff --git a/casper3/distributions.py b/old_casper_poc3/distributions.py similarity index 100% rename from casper3/distributions.py rename to old_casper_poc3/distributions.py diff --git a/casper3/networksim.py b/old_casper_poc3/networksim.py similarity index 100% rename from casper3/networksim.py rename to old_casper_poc3/networksim.py diff --git a/casper_sm/test.py b/old_casper_poc3/selfish_mining_test.py similarity index 100% rename from casper_sm/test.py rename to old_casper_poc3/selfish_mining_test.py diff --git a/casper3/test.py b/old_casper_poc3/test.py similarity index 100% rename from casper3/test.py rename to old_casper_poc3/test.py diff --git a/ghost.py b/pow_research/ghost.py similarity index 100% rename from ghost.py rename to pow_research/ghost.py diff --git a/multi_uncle_ghost.py b/pow_research/multi_uncle_ghost.py similarity index 100% rename from multi_uncle_ghost.py rename to pow_research/multi_uncle_ghost.py diff --git a/random_circuit.py b/pow_research/random_circuit.py similarity index 100% rename from random_circuit.py rename to pow_research/random_circuit.py diff --git a/random_graphs.py b/pow_research/random_graphs.py similarity index 100% rename from random_graphs.py rename to pow_research/random_graphs.py diff --git a/selfish_mining_strats.py b/pow_research/selfish_mining_strats.py similarity index 100% rename from selfish_mining_strats.py rename to pow_research/selfish_mining_strats.py diff --git a/stability/csvgen.py b/price_analysis/csvgen.py similarity index 100% rename from stability/csvgen.py rename to price_analysis/csvgen.py diff --git a/stability/diff_and_price.csv b/price_analysis/diff_and_price.csv similarity index 100% rename from stability/diff_and_price.csv rename to price_analysis/diff_and_price.csv diff --git a/stability/fit.py b/price_analysis/fit.py similarity index 100% rename from stability/fit.py rename to price_analysis/fit.py diff --git a/price_analyzer.py b/price_analysis/price_analyzer.py similarity index 100% rename from price_analyzer.py rename to price_analysis/price_analyzer.py diff --git a/price_grabber.py b/price_analysis/price_grabber.py similarity index 100% rename from price_grabber.py rename to price_analysis/price_grabber.py diff --git a/stability/spread.py b/price_analysis/spread.py similarity index 100% rename from stability/spread.py rename to price_analysis/spread.py diff --git a/slasher_v2_sim.py b/slasher_v2_sim.py deleted file mode 100644 index 734b045..0000000 --- a/slasher_v2_sim.py +++ /dev/null @@ -1,23 +0,0 @@ -NUMSIGNERS = 15 -ATTACKER_SHARE = 0.495 -CHANCE_OF_SUCCESS = 0.049 -SCORE_DIFFERENTIAL = 10 -ATTACKER_VOTE = 0.95 -import random - -def sim(): - d = -SCORE_DIFFERENTIAL * 15 - while d < 0 and d > -(SCORE_DIFFERENTIAL * 15)-1000: - if random.random() < ATTACKER_SHARE: - for i in range(NUMSIGNERS): - if random.random() < ATTACKER_SHARE: - d += ATTACKER_VOTE - else: - d += min(CHANCE_OF_SUCCESS, 0.95) - else: - for i in range(NUMSIGNERS): - if random.random() < ATTACKER_SHARE: - pass - else: - d -= min(1 - CHANCE_OF_SUCCESS, 0.95) - return 1 if d >= 0 else 0 diff --git a/slasher_withholding_exploit.py b/slasher_withholding_exploit.py deleted file mode 100644 index 16e94f5..0000000 --- a/slasher_withholding_exploit.py +++ /dev/null @@ -1,43 +0,0 @@ -def fac(n): return 1 if n==0 else n * fac(n-1) -def choose(n,k): return fac(n) / fac(k) / fac(n-k) -def prob(n,k,p): return choose(n,k) * p ** k * (1-p) ** (n-k) - -def prob_lt(n,k,p): return sum([prob(n,i,p) for i in range(p)]) - -SIGS = 30 -ACTUALSIGS = 10 -POWRETURN = 0.03 -POSRETURN = 0.01 - -# Expected number of signatures on a block -def ev(pos): - return SIGS * pos - -# Chance you have at least k sigs -def at_least_k(pos, k): - return sum([prob(SIGS, i, pos) for i in range(k, SIGS + 1)]) - -# Expected number of signatures on a block filtering all