예제 #1
0
def test_genesis_chain(db):
    k, v, k2, v2 = accounts()
    blk = mkquickgenesis({v: {"balance": utils.denoms.ether * 1}}, db=db)
    chain = Chain(env=env(blk.db), genesis=blk)

    assert chain.has_block(blk.hash)
    assert blk.hash in chain
    assert chain.get(blk.hash) == blk
    assert chain.head == blk
    assert chain.get_children(blk) == []
    assert chain.get_uncles(blk) == []
    assert chain.get_chain() == [blk]
    assert chain.get_chain(blk.hash) == [blk]
    assert chain.get_descendants(blk, count=10) == []
    assert chain.index.has_block_by_number(0)
    assert not chain.index.has_block_by_number(1)
    assert chain.index.get_block_by_number(0) == blk.hash
    with pytest.raises(KeyError):
        chain.index.get_block_by_number(1)
예제 #2
0
def test_genesis_chain(db):
    k, v, k2, v2 = accounts()
    blk = mkquickgenesis({v: {"balance": utils.denoms.ether * 1}}, db=db)
    chain = Chain(env=env(blk.db), genesis=blk)

    assert chain.has_block(blk.hash)
    assert blk.hash in chain
    assert chain.get(blk.hash) == blk
    assert chain.head == blk
    assert chain.get_children(blk) == []
    assert chain.get_uncles(blk) == []
    assert chain.get_chain() == [blk]
    assert chain.get_chain(blk.hash) == [blk]
    assert chain.get_descendants(blk, count=10) == []
    assert chain.index.has_block_by_number(0)
    assert not chain.index.has_block_by_number(1)
    assert chain.index.get_block_by_number(0) == blk.hash
    with pytest.raises(KeyError):
        chain.index.get_block_by_number(1)
예제 #3
0
def test_simple_chain(db):
    k, v, k2, v2 = accounts()
    blk = mkquickgenesis({v: {"balance": utils.denoms.ether * 1}}, db=db)
    store_block(blk)
    chain = Chain(env=env(blk.db), genesis=blk)
    tx = get_transaction()
    blk2 = mine_next_block(blk, transactions=[tx])
    store_block(blk2)
    chain.add_block(blk2)

    assert blk.hash in chain
    assert blk2.hash in chain
    assert chain.has_block(blk2.hash)
    assert chain.get(blk2.hash) == blk2
    assert chain.head == blk2
    assert chain.get_children(blk) == [blk2]
    assert chain.get_uncles(blk2) == []

    assert chain.get_chain() == [blk2, blk]
    assert chain.get_chain(count=0) == []
    assert chain.get_chain(count=1) == [blk2]
    assert chain.get_chain(count=2) == [blk2, blk]
    assert chain.get_chain(count=100) == [blk2, blk]
    assert chain.get_chain(blk.hash) == [blk]
    assert chain.get_chain(blk.hash, 0) == []
    assert chain.get_chain(blk2.hash) == [blk2, blk]
    assert chain.get_chain(blk2.hash, 1) == [blk2]
    assert chain.get_descendants(blk, count=10) == [blk2]
    assert chain.get_descendants(blk, count=1) == [blk2]
    assert chain.get_descendants(blk, count=0) == []

    assert chain.index.has_block_by_number(1)
    assert not chain.index.has_block_by_number(2)
    assert chain.index.get_block_by_number(1) == blk2.hash
    with pytest.raises(KeyError):
        chain.index.get_block_by_number(2)
    assert chain.index.get_transaction(tx.hash) == (tx, blk2, 0)
예제 #4
0
def test_simple_chain(db):
    k, v, k2, v2 = accounts()
    blk = mkquickgenesis({v: {"balance": utils.denoms.ether * 1}}, db=db)
    store_block(blk)
    chain = Chain(env=env(blk.db), genesis=blk)
    tx = get_transaction()
    blk2 = mine_next_block(blk, transactions=[tx])
    store_block(blk2)
    chain.add_block(blk2)

    assert blk.hash in chain
    assert blk2.hash in chain
    assert chain.has_block(blk2.hash)
    assert chain.get(blk2.hash) == blk2
    assert chain.head == blk2
    assert chain.get_children(blk) == [blk2]
    assert chain.get_uncles(blk2) == []

    assert chain.get_chain() == [blk2, blk]
    assert chain.get_chain(count=0) == []
    assert chain.get_chain(count=1) == [blk2]
    assert chain.get_chain(count=2) == [blk2, blk]
    assert chain.get_chain(count=100) == [blk2, blk]
    assert chain.get_chain(blk.hash) == [blk]
    assert chain.get_chain(blk.hash, 0) == []
    assert chain.get_chain(blk2.hash) == [blk2, blk]
    assert chain.get_chain(blk2.hash, 1) == [blk2]
    assert chain.get_descendants(blk, count=10) == [blk2]
    assert chain.get_descendants(blk, count=1) == [blk2]
    assert chain.get_descendants(blk, count=0) == []

    assert chain.index.has_block_by_number(1)
    assert not chain.index.has_block_by_number(2)
    assert chain.index.get_block_by_number(1) == blk2.hash
    with pytest.raises(KeyError):
        chain.index.get_block_by_number(2)
    assert chain.index.get_transaction(tx.hash) == (tx, blk2, 0)
예제 #5
0
class Validator():
    def __init__(self, genesis, key, network, env, time_offset=5):
        # Create a chain object
        self.chain = Chain(genesis, env=env)
        # Use the validator's time as the chain's time
        self.chain.time = lambda: self.get_timestamp()
        # My private key
        self.key = key
        # My address
        self.address = privtoaddr(key)
        # My randao
        self.randao = RandaoManager(sha3(self.key))
        # Pointer to the test p2p network
        self.network = network
        # Record of objects already received and processed
        self.received_objects = {}
        # The minimum eligible timestamp given a particular number of skips
        self.next_skip_count = 0
        self.next_skip_timestamp = 0
        # This validator's indices in the state
        self.indices = None
        # Is this validator active?
        self.active = False
        # Code that verifies signatures from this validator
        self.validation_code = generate_validation_code(privtoaddr(key))
        # Parents that this validator has already built a block on
        self.used_parents = {}
        # This validator's clock offset (for testing purposes)
        self.time_offset = random.randrange(time_offset) - (time_offset // 2)
        # Determine the epoch length
        self.epoch_length = self.call_casper('getEpochLength')
        # Give this validator a unique ID
        self.id = len(ids)
        ids.append(self.id)
        self.find_my_indices()
        self.cached_head = self.chain.head_hash

    def call_casper(self, fun, args=[]):
        return call_casper(self.chain.state, fun, args)

    def find_my_indices(self):
        epoch = self.chain.state.block_number // self.epoch_length
        print 'Finding indices for epoch %d' % epoch, self.call_casper(
            'getEpoch')
        for i in range(len(validator_sizes)):
            valcount = self.call_casper('getHistoricalValidatorCount',
                                        [epoch, i])
            print i, valcount, self.call_casper('getHistoricalValidatorCount',
                                                [0, i])
            for j in range(valcount):
                valcode = self.call_casper('getValidationCode', [i, j])
                print(valcode, self.validation_code)
                if valcode == self.validation_code:
                    self.indices = i, j
                    start = self.call_casper('getStartEpoch', [i, j])
                    end = self.call_casper('getEndEpoch', [i, j])
                    if start <= epoch < end:
                        self.active = True
                        self.next_skip_count = 0
                        self.next_skip_timestamp = get_timestamp(
                            self.chain, self.next_skip_count)
                        print 'In current validator set at (%d, %d)' % (i, j)
                        return
                    else:
                        self.indices = None
                        self.active = False
                        self.next_skip_count, self.next_skip_timestamp = 0, 0
                        print 'Registered at (%d, %d) but not in current set' % (
                            i, j)
                        return
        self.indices = None
        self.active = False
        self.next_skip_count, self.next_skip_timestamp = 0, 0
        print 'Not in current validator set'

    def get_uncles(self):
        anc = self.chain.get_block(
            self.chain.get_blockhash_by_number(self.chain.state.block_number -
                                               CHECK_FOR_UNCLES_BACK))
        if anc:
            descendants = self.chain.get_descendants(anc)
        else:
            descendants = self.chain.get_descendants(
                self.chain.db.get('GENESIS_HASH'))
        potential_uncles = [
            x for x in descendants
            if x not in self.chain and isinstance(x, Block)
        ]
        uncles = [
            x.header for x in potential_uncles if not call_casper(
                self.chain.state, 'isDunkleIncluded', [x.header.hash])
        ]
        return uncles

    def get_timestamp(self):
        return int(self.network.time * 0.01) + self.time_offset

    def on_receive(self, obj):
        if isinstance(obj, list):
            for _obj in obj:
                self.on_receive(_obj)
            return
        if obj.hash in self.received_objects:
            return
        if isinstance(obj, Block):
            print 'Receiving block', obj
            assert obj.hash not in self.chain
            block_success = self.chain.add_block(obj)
            self.network.broadcast(self, obj)
            self.network.broadcast(self, ChildRequest(obj.header.hash))
            self.update_head()
        elif isinstance(obj, Transaction):
            if self.chain.add_transaction(obj):
                self.network.broadcast(self, obj)
        self.received_objects[obj.hash] = True
        for x in self.chain.get_chain():
            assert x.hash in self.received_objects

    def tick(self):
        # Try to create a block
        # Conditions:
        # (i) you are an active validator,
        # (ii) you have not yet made a block with this parent
        if self.indices and self.chain.head_hash not in self.used_parents:
            t = self.get_timestamp()
            # Is it early enough to create the block?
            if t >= self.next_skip_timestamp and (
                    not self.chain.head
                    or t > self.chain.head.header.timestamp):
                # Wrong validator; in this case, just wait for the next skip count
                if not check_skips(self.chain, self.indices,
                                   self.next_skip_count):
                    self.next_skip_count += 1
                    self.next_skip_timestamp = get_timestamp(
                        self.chain, self.next_skip_count)
                    # print 'Incrementing proposed timestamp for block %d to %d' % \
                    #     (self.chain.head.header.number + 1 if self.chain.head else 0, self.next_skip_timestamp)
                    return
                self.used_parents[self.chain.head_hash] = True
                # Simulated 15% chance of validator failure to make a block
                if random.random() > 0.999:
                    print 'Simulating validator failure, block %d not created' % (
                        self.chain.head.header.number +
                        1 if self.chain.head else 0)
                    return
                # Make the block, make sure it's valid
                pre_dunkle_count = call_casper(self.chain.state,
                                               'getTotalDunklesIncluded', [])
                dunkle_txs = []
                for i, u in enumerate(self.get_uncles()[:4]):
                    start_nonce = self.chain.state.get_nonce(self.address)
                    txdata = casper_ct.encode('includeDunkle', [rlp.encode(u)])
                    dunkle_txs.append(
                        Transaction(start_nonce + i, 0, 650000,
                                    self.chain.config['CASPER_ADDR'], 0,
                                    txdata).sign(self.key))
                for dtx in dunkle_txs[::-1]:
                    self.chain.add_transaction(dtx, force=True)
                blk = make_block(self.chain, self.key, self.randao,
                                 self.indices, self.next_skip_count)
                global global_block_counter
                global_block_counter += 1
                for dtx in dunkle_txs:
                    assert dtx in blk.transactions, (dtx, blk.transactions)
                print 'made block with timestamp %d and %d dunkles' % (
                    blk.timestamp, len(dunkle_txs))
                assert blk.timestamp >= self.next_skip_timestamp
                assert self.chain.add_block(blk)
                self.update_head()
                post_dunkle_count = call_casper(self.chain.state,
                                                'getTotalDunklesIncluded', [])
                assert post_dunkle_count - pre_dunkle_count == len(dunkle_txs)
                self.received_objects[blk.hash] = True
                print 'Validator %d making block %d (%s)' % (
                    self.id, blk.header.number,
                    blk.header.hash[:8].encode('hex'))
                self.network.broadcast(self, blk)
        # Sometimes we received blocks too early or out of order;
        # run an occasional loop that processes these
        if random.random() < 0.02:
            self.chain.process_time_queue()
            self.chain.process_parent_queue()
            self.update_head()

    def update_head(self):
        if self.cached_head == self.chain.head_hash:
            return
        self.cached_head = self.chain.head_hash
        if self.chain.state.block_number % self.epoch_length == 0:
            self.find_my_indices()
        if self.indices:
            self.next_skip_count = 0
            self.next_skip_timestamp = get_timestamp(self.chain,
                                                     self.next_skip_count)
        print 'Head changed: %s, will attempt creating a block at %d' % (
            self.chain.head_hash.encode('hex'), self.next_skip_timestamp)

    def withdraw(self, gasprice=20 * 10**9):
        h = sha3(b'withdrawwithdrawwithdrawwithdraw')
        v, r, s = ecsign(h, self.key)
        sigdata = encode_int32(v) + encode_int32(r) + encode_int32(s)
        txdata = casper_ct.encode('startWithdrawal',
                                  [self.indices[0], self.indices[1], sigdata])
        tx = Transaction(self.chain.state.get_nonce(self.address), gasprice,
                         650000, self.chain.config['CASPER_ADDR'], 0,
                         txdata).sign(self.key)
        self.chain.add_transaction(tx)
        self.network.broadcast(self, tx)
        print 'Withdrawing!'
예제 #6
0
파일: casper.py 프로젝트: yep/research
class Validator():
    def __init__(self, genesis, key, network, env, time_offset=5):
        # Create a chain object
        self.chain = Chain(genesis, env=env)
        # Create a transaction queue
        self.txqueue = TransactionQueue()
        # Use the validator's time as the chain's time
        self.chain.time = lambda: self.get_timestamp()
        # My private key
        self.key = key
        # My address
        self.address = privtoaddr(key)
        # My randao
        self.randao = RandaoManager(sha3(self.key))
        # Pointer to the test p2p network
        self.network = network
        # Record of objects already received and processed
        self.received_objects = {}
        # The minimum eligible timestamp given a particular number of skips
        self.next_skip_count = 0
        self.next_skip_timestamp = 0
        # Is this validator active?
        self.active = False
        # Code that verifies signatures from this validator
        self.validation_code = generate_validation_code(privtoaddr(key))
        # Validation code hash
        self.vchash = sha3(self.validation_code)
        # Parents that this validator has already built a block on
        self.used_parents = {}
        # This validator's clock offset (for testing purposes)
        self.time_offset = random.randrange(time_offset) - (time_offset // 2)
        # Determine the epoch length
        self.epoch_length = self.call_casper('getEpochLength')
        # My minimum gas price
        self.mingasprice = 20 * 10**9
        # Give this validator a unique ID
        self.id = len(ids)
        ids.append(self.id)
        self.update_activity_status()
        self.cached_head = self.chain.head_hash

    def call_casper(self, fun, args=[]):
        return call_casper(self.chain.state, fun, args)

    def update_activity_status(self):
        start_epoch = self.call_casper('getStartEpoch', [self.vchash])
        now_epoch = self.call_casper('getEpoch')
        end_epoch = self.call_casper('getEndEpoch', [self.vchash])
        if start_epoch <= now_epoch < end_epoch:
            self.active = True
            self.next_skip_count = 0
            self.next_skip_timestamp = get_timestamp(self.chain,
                                                     self.next_skip_count)
            print 'In current validator set'
        else:
            self.active = False

    def get_timestamp(self):
        return int(self.network.time * 0.01) + self.time_offset

    def on_receive(self, obj):
        if isinstance(obj, list):
            for _obj in obj:
                self.on_receive(_obj)
            return
        if obj.hash in self.received_objects:
            return
        if isinstance(obj, Block):
            print 'Receiving block', obj
            assert obj.hash not in self.chain
            block_success = self.chain.add_block(obj)
            self.network.broadcast(self, obj)
            self.network.broadcast(self, ChildRequest(obj.header.hash))
            self.update_head()
        elif isinstance(obj, Transaction):
            print 'Receiving transaction', obj
            if obj.gasprice >= self.mingasprice:
                self.txqueue.add_transaction(obj)
                print 'Added transaction, txqueue size %d' % len(
                    self.txqueue.txs)
                self.network.broadcast(self, obj)
            else:
                print 'Gasprice too low'
        self.received_objects[obj.hash] = True
        for x in self.chain.get_chain():
            assert x.hash in self.received_objects

    def tick(self):
        # Try to create a block
        # Conditions:
        # (i) you are an active validator,
        # (ii) you have not yet made a block with this parent
        if self.active and self.chain.head_hash not in self.used_parents:
            t = self.get_timestamp()
            # Is it early enough to create the block?
            if t >= self.next_skip_timestamp and (
                    not self.chain.head
                    or t > self.chain.head.header.timestamp):
                # Wrong validator; in this case, just wait for the next skip count
                if not check_skips(self.chain, self.vchash,
                                   self.next_skip_count):
                    self.next_skip_count += 1
                    self.next_skip_timestamp = get_timestamp(
                        self.chain, self.next_skip_count)
                    # print 'Incrementing proposed timestamp for block %d to %d' % \
                    #     (self.chain.head.header.number + 1 if self.chain.head else 0, self.next_skip_timestamp)
                    return
                self.used_parents[self.chain.head_hash] = True
                # Simulated 15% chance of validator failure to make a block
                if random.random() > 0.999:
                    print 'Simulating validator failure, block %d not created' % (
                        self.chain.head.header.number +
                        1 if self.chain.head else 0)
                    return
                # Make the block
                s1 = self.chain.state.trie.root_hash
                pre_dunkle_count = self.call_casper('getTotalDunklesIncluded')
                dunkle_txs = get_dunkle_candidates(self.chain,
                                                   self.chain.state)
                blk = make_head_candidate(self.chain, self.txqueue)
                randao = self.randao.get_parent(
                    self.call_casper('getRandao', [self.vchash]))
                blk = sign_block(blk, self.key, randao, self.vchash,
                                 self.next_skip_count)
                # Make sure it's valid
                global global_block_counter
                global_block_counter += 1
                for dtx in dunkle_txs:
                    assert dtx in blk.transactions, (dtx, blk.transactions)
                print 'made block with timestamp %d and %d dunkles' % (
                    blk.timestamp, len(dunkle_txs))
                s2 = self.chain.state.trie.root_hash
                assert s1 == s2
                assert blk.timestamp >= self.next_skip_timestamp
                assert self.chain.add_block(blk)
                self.update_head()
                post_dunkle_count = self.call_casper('getTotalDunklesIncluded')
                assert post_dunkle_count - pre_dunkle_count == len(dunkle_txs)
                self.received_objects[blk.hash] = True
                print 'Validator %d making block %d (%s)' % (
                    self.id, blk.header.number,
                    blk.header.hash[:8].encode('hex'))
                self.network.broadcast(self, blk)
        # Sometimes we received blocks too early or out of order;
        # run an occasional loop that processes these
        if random.random() < 0.02:
            self.chain.process_time_queue()
            self.chain.process_parent_queue()
            self.update_head()

    def update_head(self):
        if self.cached_head == self.chain.head_hash:
            return
        self.cached_head = self.chain.head_hash
        if self.chain.state.block_number % self.epoch_length == 0:
            self.update_activity_status()
        if self.active:
            self.next_skip_count = 0
            self.next_skip_timestamp = get_timestamp(self.chain,
                                                     self.next_skip_count)
        print 'Head changed: %s, will attempt creating a block at %d' % (
            self.chain.head_hash.encode('hex'), self.next_skip_timestamp)

    def withdraw(self, gasprice=20 * 10**9):
        sigdata = make_withdrawal_signature(self.key)
        txdata = casper_ct.encode('startWithdrawal', [self.vchash, sigdata])
        tx = Transaction(self.chain.state.get_nonce(self.address), gasprice,
                         650000, self.chain.config['CASPER_ADDR'], 0,
                         txdata).sign(self.key)
        self.txqueue.add_transaction(tx, force=True)
        self.network.broadcast(self, tx)
        print 'Withdrawing!'

    def deposit(self, gasprice=20 * 10**9):
        assert value * 10**18 >= self.chain.state.get_balance(
            self.address) + gasprice * 1000000
        tx = Transaction(
            self.chain.state.get_nonce(self.address) * 10**18, gasprice,
            1000000, casper_config['CASPER_ADDR'], value * 10**18,
            ct.encode('deposit', [self.validation_code,
                                  self.randao.get(9999)]))
예제 #7
0
class Validator():
    def __init__(self, genesis, key, network, env, time_offset=5):
        # Create a chain object
        self.chain = Chain(genesis, env=env)
        # Use the validator's time as the chain's time
        self.chain.time = lambda: self.get_timestamp()
        # My private key
        self.key = key
        # My address
        self.address = privtoaddr(key)
        # My randao
        self.randao = RandaoManager(sha3(self.key))
        # Pointer to the test p2p network
        self.network = network
        # Record of objects already received and processed
        self.received_objects = {}
        # The minimum eligible timestamp given a particular number of skips
        self.next_skip_count = 0
        self.next_skip_timestamp = 0
        # This validator's indices in the state
        self.indices = None
        # Code that verifies signatures from this validator
        self.validation_code = generate_validation_code(privtoaddr(key))
        # Parents that this validator has already built a block on
        self.used_parents = {}
        # This validator's clock offset (for testing purposes)
        self.time_offset = random.randrange(time_offset) - (time_offset // 2)
        # Give this validator a unique ID
        self.id = len(ids)
        ids.append(self.id)
        self.find_my_indices()
        self.cached_head = self.chain.head_hash

    def find_my_indices(self):
        for i in range(len(validatorSizes)):
            epoch = self.chain.state.block_number // EPOCH_LENGTH
            valcount = call_casper(self.chain.state, 'getHistoricalValidatorCount', [epoch, i])
            for j in range(valcount):
                valcode = call_casper(self.chain.state, 'getValidationCode', [i, j])
                if valcode == self.validation_code:
                    self.indices = i, j
                    self.next_skip_count = 0
                    self.next_skip_timestamp = get_timestamp(self.chain, self.next_skip_count)
                    print 'In current validator set at (%d, %d)' % (i, j)
                    return
        self.indices = None
        self.next_skip_count, self.next_skip_timestamp = 0, 0
        print 'Not in current validator set'

    def get_uncles(self):
        anc = self.chain.get_block(self.chain.get_blockhash_by_number(self.chain.state.block_number - CHECK_FOR_UNCLES_BACK))
        if anc:
            descendants = self.chain.get_descendants(anc)
        else:
            descendants = self.chain.get_descendants(self.chain.db.get('GENESIS_HASH'))
        potential_uncles = [x for x in descendants if x not in self.chain and isinstance(x, Block)]
        uncles = [x.header for x in potential_uncles if not call_casper(self.chain.state, 'isDunkleIncluded', [x.header.hash])]
        return uncles

    def get_timestamp(self):
        return int(self.network.time * 0.01) + self.time_offset

    def on_receive(self, obj):
        if isinstance(obj, list):
            for _obj in obj:
                self.on_receive(_obj)
            return
        if obj.hash in self.received_objects:
            return
        if isinstance(obj, Block):
            print 'Receiving block', obj
            assert obj.hash not in self.chain, (self.received_objects, obj.hash, [x.hash for x in self.chain.get_chain()])
            block_success = self.chain.add_block(obj)
            self.network.broadcast(self, obj)
            self.network.broadcast(self, ChildRequest(obj.header.hash))
            self.update_head()
        elif isinstance(obj, Transaction):
            self.chain.add_transaction(obj)
        self.received_objects[obj.hash] = True
        for x in self.chain.get_chain():
            assert x.hash in self.received_objects

    def tick(self):
        # Try to create a block
        # Conditions:
        # (i) you are an active validator,
        # (ii) you have not yet made a block with this parent
        if self.indices and self.chain.head_hash not in self.used_parents:
            t = self.get_timestamp()
            # Is it early enough to create the block?
            if t >= self.next_skip_timestamp and (not self.chain.head or t > self.chain.head.header.timestamp):
                print 'creating', t, self.next_skip_timestamp
                # Wrong validator; in this case, just wait for the next skip count
                if not check_skips(self.chain, self.indices, self.next_skip_count):
                    self.next_skip_count += 1
                    self.next_skip_timestamp = get_timestamp(self.chain, self.next_skip_count)
                    print 'Incrementing proposed timestamp for block %d to %d' % \
                        (self.chain.head.header.number + 1 if self.chain.head else 0, self.next_skip_timestamp)
                    return
                self.used_parents[self.chain.head_hash] = True
                # Simulated 15% chance of validator failure to make a block
                if random.random() > 0.999:
                    print 'Simulating validator failure, block %d not created' % (self.chain.head.header.number + 1 if self.chain.head else 0)
                    return
                # Make the block, make sure it's valid
                pre_dunkle_count = call_casper(self.chain.state, 'getTotalDunklesIncluded', [])
                dunkle_txs = []
                for i, u in enumerate(self.get_uncles()[:4]):
                    start_nonce = self.chain.state.get_nonce(self.address)
                    print 'start_nonce', start_nonce
                    txdata = casper_ct.encode('includeDunkle', [rlp.encode(u)])
                    dunkle_txs.append(Transaction(start_nonce + i, 0, 650000, self.chain.config['CASPER_ADDR'], 0, txdata).sign(self.key))
                for dtx in dunkle_txs[::-1]:
                    self.chain.add_transaction(dtx, force=True)
                blk = make_block(self.chain, self.key, self.randao, self.indices, self.next_skip_count)
                global global_block_counter
                global_block_counter += 1
                for dtx in dunkle_txs:
                    assert dtx in blk.transactions, (dtx, blk.transactions)
                print 'made block with timestamp %d and %d dunkles' % (blk.timestamp, len(dunkle_txs))
                assert blk.timestamp >= self.next_skip_timestamp
                assert self.chain.add_block(blk)
                self.update_head()
                post_dunkle_count = call_casper(self.chain.state, 'getTotalDunklesIncluded', [])
                assert post_dunkle_count - pre_dunkle_count == len(dunkle_txs)
                self.received_objects[blk.hash] = True
                print 'Validator %d making block %d (%s)' % (self.id, blk.header.number, blk.header.hash[:8].encode('hex'))
                self.network.broadcast(self, blk)
        # Sometimes we received blocks too early or out of order;
        # run an occasional loop that processes these
        if random.random() < 0.02:
            self.chain.process_time_queue()
            self.chain.process_parent_queue()
            self.update_head()

    def update_head(self):
        if self.cached_head == self.chain.head_hash:
            return
        self.cached_head = self.chain.head_hash
        if self.chain.state.block_number % EPOCH_LENGTH == 0:
            self.find_my_indices()
        if self.indices:
            self.next_skip_count = 0
            self.next_skip_timestamp = get_timestamp(self.chain, self.next_skip_count)
        print 'Head changed: %s, will attempt creating a block at %d' % (self.chain.head_hash.encode('hex'), self.next_skip_timestamp)
예제 #8
0
파일: casper.py 프로젝트: ethereum/research
class Validator():
    def __init__(self, genesis, key, network, env, time_offset=5):
        # Create a chain object
        self.chain = Chain(genesis, env=env)
        # Create a transaction queue
        self.txqueue = TransactionQueue()
        # Use the validator's time as the chain's time
        self.chain.time = lambda: self.get_timestamp()
        # My private key
        self.key = key
        # My address
        self.address = privtoaddr(key)
        # My randao
        self.randao = RandaoManager(sha3(self.key))
        # Pointer to the test p2p network
        self.network = network
        # Record of objects already received and processed
        self.received_objects = {}
        # The minimum eligible timestamp given a particular number of skips
        self.next_skip_count = 0
        self.next_skip_timestamp = 0
        # Is this validator active?
        self.active = False
        # Code that verifies signatures from this validator
        self.validation_code = generate_validation_code(privtoaddr(key))
        # Validation code hash
        self.vchash = sha3(self.validation_code)
        # Parents that this validator has already built a block on
        self.used_parents = {}
        # This validator's clock offset (for testing purposes)
        self.time_offset = random.randrange(time_offset) - (time_offset // 2)
        # Determine the epoch length
        self.epoch_length = self.call_casper('getEpochLength')
        # My minimum gas price
        self.mingasprice = 20 * 10**9
        # Give this validator a unique ID
        self.id = len(ids)
        ids.append(self.id)
        self.update_activity_status()
        self.cached_head = self.chain.head_hash

    def call_casper(self, fun, args=[]):
        return call_casper(self.chain.state, fun, args)

    def update_activity_status(self):
        start_epoch = self.call_casper('getStartEpoch', [self.vchash])
        now_epoch = self.call_casper('getEpoch')
        end_epoch = self.call_casper('getEndEpoch', [self.vchash])
        if start_epoch <= now_epoch < end_epoch:
            self.active = True
            self.next_skip_count = 0
            self.next_skip_timestamp = get_timestamp(self.chain, self.next_skip_count)
            print 'In current validator set'
        else:
            self.active = False

    def get_timestamp(self):
        return int(self.network.time * 0.01) + self.time_offset

    def on_receive(self, obj):
        if isinstance(obj, list):
            for _obj in obj:
                self.on_receive(_obj)
            return
        if obj.hash in self.received_objects:
            return
        if isinstance(obj, Block):
            print 'Receiving block', obj
            assert obj.hash not in self.chain
            block_success = self.chain.add_block(obj)
            self.network.broadcast(self, obj)
            self.network.broadcast(self, ChildRequest(obj.header.hash))
            self.update_head()
        elif isinstance(obj, Transaction):
            print 'Receiving transaction', obj
            if obj.gasprice >= self.mingasprice:
                self.txqueue.add_transaction(obj)
                print 'Added transaction, txqueue size %d' % len(self.txqueue.txs)
                self.network.broadcast(self, obj)
            else:
                print 'Gasprice too low'
        self.received_objects[obj.hash] = True
        for x in self.chain.get_chain():
            assert x.hash in self.received_objects

    def tick(self):
        # Try to create a block
        # Conditions:
        # (i) you are an active validator,
        # (ii) you have not yet made a block with this parent
        if self.active and self.chain.head_hash not in self.used_parents:
            t = self.get_timestamp()
            # Is it early enough to create the block?
            if t >= self.next_skip_timestamp and (not self.chain.head or t > self.chain.head.header.timestamp):
                # Wrong validator; in this case, just wait for the next skip count
                if not check_skips(self.chain, self.vchash, self.next_skip_count):
                    self.next_skip_count += 1
                    self.next_skip_timestamp = get_timestamp(self.chain, self.next_skip_count)
                    # print 'Incrementing proposed timestamp for block %d to %d' % \
                    #     (self.chain.head.header.number + 1 if self.chain.head else 0, self.next_skip_timestamp)
                    return
                self.used_parents[self.chain.head_hash] = True
                # Simulated 15% chance of validator failure to make a block
                if random.random() > 0.999:
                    print 'Simulating validator failure, block %d not created' % (self.chain.head.header.number + 1 if self.chain.head else 0)
                    return
                # Make the block
                s1 = self.chain.state.trie.root_hash
                pre_dunkle_count = self.call_casper('getTotalDunklesIncluded')
                dunkle_txs = get_dunkle_candidates(self.chain, self.chain.state)
                blk = make_head_candidate(self.chain, self.txqueue)
                randao = self.randao.get_parent(self.call_casper('getRandao', [self.vchash]))
                blk = sign_block(blk, self.key, randao, self.vchash, self.next_skip_count)
                # Make sure it's valid
                global global_block_counter
                global_block_counter += 1
                for dtx in dunkle_txs:
                    assert dtx in blk.transactions, (dtx, blk.transactions)
                print 'made block with timestamp %d and %d dunkles' % (blk.timestamp, len(dunkle_txs))
                s2 = self.chain.state.trie.root_hash
                assert s1 == s2
                assert blk.timestamp >= self.next_skip_timestamp
                assert self.chain.add_block(blk)
                self.update_head()
                post_dunkle_count = self.call_casper('getTotalDunklesIncluded')
                assert post_dunkle_count - pre_dunkle_count == len(dunkle_txs)
                self.received_objects[blk.hash] = True
                print 'Validator %d making block %d (%s)' % (self.id, blk.header.number, blk.header.hash[:8].encode('hex'))
                self.network.broadcast(self, blk)
        # Sometimes we received blocks too early or out of order;
        # run an occasional loop that processes these
        if random.random() < 0.02:
            self.chain.process_time_queue()
            self.chain.process_parent_queue()
            self.update_head()

    def update_head(self):
        if self.cached_head == self.chain.head_hash:
            return
        self.cached_head = self.chain.head_hash
        if self.chain.state.block_number % self.epoch_length == 0:
            self.update_activity_status()
        if self.active:
            self.next_skip_count = 0
            self.next_skip_timestamp = get_timestamp(self.chain, self.next_skip_count)
        print 'Head changed: %s, will attempt creating a block at %d' % (self.chain.head_hash.encode('hex'), self.next_skip_timestamp)

    def withdraw(self, gasprice=20 * 10**9):
        sigdata = make_withdrawal_signature(self.key)
        txdata = casper_ct.encode('startWithdrawal', [self.vchash, sigdata])
        tx = Transaction(self.chain.state.get_nonce(self.address), gasprice, 650000, self.chain.config['CASPER_ADDR'], 0, txdata).sign(self.key)
        self.txqueue.add_transaction(tx, force=True)
        self.network.broadcast(self, tx)
        print 'Withdrawing!'

    def deposit(self, gasprice=20 * 10**9):
        assert value * 10**18 >= self.chain.state.get_balance(self.address) + gasprice * 1000000
        tx = Transaction(self.chain.state.get_nonce(self.address) * 10**18, gasprice, 1000000,
                         casper_config['CASPER_ADDR'], value * 10**18,
                         ct.encode('deposit', [self.validation_code, self.randao.get(9999)]))