def test_invalid_proofs_all_from_same_wallet():

    testdb = create_reward_test_blockchain_database()

    chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])

    required_number_of_proofs_for_reward_type_2_proof = chain.get_consensus_db(timestamp=Timestamp(int(time.time()))).required_number_of_proofs_for_reward_type_2_proof

    node_staking_scores = []

    score = 1000000
    for i in range(1, 10):
        # Second score/proof is from instance 1
        current_private_key = private_keys[1]
        node_staking_score = NodeStakingScore(
            recipient_node_wallet_address=private_keys[0].public_key.to_canonical_address(),
            score=int(score-i),
            since_block_number=0,
            timestamp=int(time.time()),
            head_hash_of_sender_chain=chain.chaindb.get_canonical_head_hash(
                current_private_key.public_key.to_canonical_address()),
            v=0,
            r=0,
            s=0,
            )
        signed_node_staking_score = node_staking_score.get_signed(current_private_key, MAINNET_NETWORK_ID)
        node_staking_scores.append(signed_node_staking_score)

    # Now we try to import the reward block with instance 0
    reward_chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])

    with pytest.raises(ValidationError):
        reward_chain.import_current_queue_block_with_reward(node_staking_scores)
def test_invalid_proofs_timestamp_in_past():

    testdb = create_reward_test_blockchain_database()

    chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])
    min_time_between_blocks = chain.get_vm(timestamp=Timestamp(int(time.time()))).min_time_between_blocks
    tx_list = [[private_keys[1], private_keys[0], to_wei(1, 'ether'), int(int(time.time())-min_time_between_blocks*10)]]

    add_transactions_to_blockchain_db(testdb, tx_list)

    chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])

    required_number_of_proofs_for_reward_type_2_proof = chain.get_consensus_db(timestamp=Timestamp(int(time.time()))).required_number_of_proofs_for_reward_type_2_proof

    node_staking_scores = []

    # First score/proof with timestamp far in future
    current_private_key = private_keys[1]
    node_staking_score = NodeStakingScore(
        recipient_node_wallet_address=private_keys[0].public_key.to_canonical_address(),
        score=int(1000000),
        since_block_number=0,
        timestamp=int(time.time())-60*10,
        head_hash_of_sender_chain=chain.chaindb.get_canonical_head_hash(
            current_private_key.public_key.to_canonical_address()),
        v=0,
        r=0,
        s=0,
    )
    signed_node_staking_score = node_staking_score.get_signed(current_private_key, MAINNET_NETWORK_ID)
    node_staking_scores.append(signed_node_staking_score)

    score = 100000
    for i in range(2, 10):
        # Second score/proof is from instance 1
        current_private_key = private_keys[i]
        node_staking_score = NodeStakingScore(
            recipient_node_wallet_address=private_keys[0].public_key.to_canonical_address(),
            score=int(score-i),
            since_block_number=1,
            timestamp=int(time.time()),
            head_hash_of_sender_chain=chain.chaindb.get_canonical_head_hash(
                current_private_key.public_key.to_canonical_address()),
            v=0,
            r=0,
            s=0,
            )
        signed_node_staking_score = node_staking_score.get_signed(current_private_key, MAINNET_NETWORK_ID)
        node_staking_scores.append(signed_node_staking_score)

    # Now we try to import the reward block with instance 0
    reward_chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])

    with pytest.raises(ValidationError):
        reward_chain.import_current_queue_block_with_reward(node_staking_scores)
def test_invalid_proofs_no_proofs():

    testdb = create_reward_test_blockchain_database()

    chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])
    min_time_between_blocks = chain.get_vm(timestamp=Timestamp(int(time.time()))).min_time_between_blocks
    tx_list = [[private_keys[1], private_keys[0], to_wei(1, 'ether'), int(int(time.time())-min_time_between_blocks*10)]]

    add_transactions_to_blockchain_db(testdb, tx_list)

    chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])

    required_number_of_proofs_for_reward_type_2_proof = chain.get_consensus_db(timestamp=Timestamp(int(time.time()))).required_number_of_proofs_for_reward_type_2_proof


    # Now we try to import the reward block with instance 0
    reward_chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])

    with pytest.raises(RewardAmountRoundsToZero):
        imported_block = reward_chain.import_current_queue_block_with_reward([])
def test_break_chronological_consistency_1():
    testdb = create_reward_test_blockchain_database()

    chain = MainnetChain(testdb, private_keys[1].public_key.to_canonical_address(), private_keys[1])

    tx_nonce = chain.get_current_queue_block_nonce()
    tx = chain.create_and_sign_transaction(nonce = tx_nonce,
        gas_price=to_wei(1, 'gwei'),
        gas=GAS_TX,
        to=private_keys[0].public_key.to_canonical_address(),
        value=1,
        data=b"",
        v=0,
        r=0,
        s=0
    )
    new_block_to_import = create_valid_block_at_timestamp(testdb,
                                    private_keys[1],
                                    transactions=[tx],
                                    receive_transactions=None,
                                    reward_bundle=None,
                                    timestamp=int(time.time()-1))

    chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])
    node_staking_scores = []

    score = 100000
    for i in range(1, 10):
        # Second score/proof is from instance 1
        current_private_key = private_keys[i]
        node_staking_score = NodeStakingScore(
            recipient_node_wallet_address=private_keys[0].public_key.to_canonical_address(),
            score=int(score-i),
            since_block_number=0,
            timestamp=int(time.time()),
            head_hash_of_sender_chain=chain.chaindb.get_canonical_head_hash(
                current_private_key.public_key.to_canonical_address()),
            v=0,
            r=0,
            s=0,
            )
        signed_node_staking_score = node_staking_score.get_signed(current_private_key, MAINNET_NETWORK_ID)
        node_staking_scores.append(signed_node_staking_score)

    # Now we try to import the reward block with instance 0
    reward_chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])

    reward_block = reward_chain.import_current_queue_block_with_reward(node_staking_scores)

    print("reward block timestamp {}".format(reward_block.header.timestamp))
    for proof in reward_block.reward_bundle.reward_type_2.proof:
        print(encode_hex(proof.sender))
        for i in range(1, 10):
            if proof.sender == private_keys[i].public_key.to_canonical_address():
                print("proof from {}".format(i))

    print("new_block_to_import timestamp {}".format(new_block_to_import.header.timestamp))
    print("new_block_to_import chain address {}".format(encode_hex(new_block_to_import.header.chain_address)))
    # Now we import a block on private key 1 with a timestamp set to 10 seconds ago
    chain = MainnetChain(testdb, private_keys[1].public_key.to_canonical_address(), private_keys[1])
    with pytest.raises(ReplacingBlocksNotAllowed):
        chain.import_block(new_block_to_import, allow_replacement=False)

# test_break_chronological_consistency_1()
# exit()
def test_invalid_proofs_not_enough_stake():
    testdb = MemoryDB()

    chain = MainnetChain(testdb, GENESIS_PRIVATE_KEY.public_key.to_canonical_address(), GENESIS_PRIVATE_KEY)
    coin_mature_time = chain.get_vm(timestamp=Timestamp(int(time.time()))).consensus_db.coin_mature_time_for_staking
    min_time_between_blocks = chain.get_vm(timestamp=Timestamp(int(time.time()))).min_time_between_blocks
    now = int(time.time())
    start = now - max((coin_mature_time * 2), (min_time_between_blocks * 20))
    key_balance_dict = {
        private_keys[0]: (to_wei(1, 'ether'), start),
        private_keys[1]: (to_wei(1, 'ether'), start + min_time_between_blocks * 1),
        private_keys[2]: (to_wei(1, 'ether'), start + min_time_between_blocks * 2),
        private_keys[3]: (to_wei(1, 'ether'), start + min_time_between_blocks * 3),
        private_keys[4]: (to_wei(1, 'ether'), start + min_time_between_blocks * 4),
        private_keys[5]: (to_wei(1, 'ether'), start + min_time_between_blocks * 5),
        private_keys[6]: (to_wei(1, 'ether'), start + min_time_between_blocks * 6),
        private_keys[7]: (to_wei(1, 'ether'), start + min_time_between_blocks * 7),
        private_keys[8]: (to_wei(1, 'ether'), start + min_time_between_blocks * 8),
        private_keys[9]: (to_wei(1, 'ether'), now - coin_mature_time + 1),  # immature
    }
    create_dev_fixed_blockchain_database(testdb, key_balance_dict)

    chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])

    node_staking_scores = []

    # First score/proof with timestamp far in future
    current_private_key = private_keys[1]
    node_staking_score = NodeStakingScore(
        recipient_node_wallet_address=private_keys[0].public_key.to_canonical_address(),
        score=int(1000000),
        since_block_number=0,
        timestamp=int(time.time())-60*10,
        head_hash_of_sender_chain=chain.chaindb.get_canonical_head_hash(
            current_private_key.public_key.to_canonical_address()),
        v=0,
        r=0,
        s=0,
    )
    signed_node_staking_score = node_staking_score.get_signed(current_private_key, MAINNET_NETWORK_ID)
    node_staking_scores.append(signed_node_staking_score)

    score = 100000
    for i in range(2, 10):
        # Second score/proof is from instance 1
        current_private_key = private_keys[i]
        node_staking_score = NodeStakingScore(
            recipient_node_wallet_address=private_keys[0].public_key.to_canonical_address(),
            score=int(score-i),
            since_block_number=1,
            timestamp=int(time.time()),
            head_hash_of_sender_chain=chain.chaindb.get_canonical_head_hash(
                current_private_key.public_key.to_canonical_address()),
            v=0,
            r=0,
            s=0,
            )
        signed_node_staking_score = node_staking_score.get_signed(current_private_key, MAINNET_NETWORK_ID)
        node_staking_scores.append(signed_node_staking_score)

    # Now we try to import the reward block with instance 0
    reward_chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])

    with pytest.raises(NotEnoughProofsOrStakeForRewardType2Proof):
        reward_chain.import_current_queue_block_with_reward(node_staking_scores)
def _test_block_rewards_system():
    #The genesis chain will be adding a reward block. We need to generate fake NodeStakingScores from a bunch of other
    #nodes

    # testdb = LevelDB('/home/tommy/.local/share/helios/instance_test/mainnet/chain/full/')
    # testdb = JournalDB(testdb)
    testdb = create_reward_test_blockchain_database()

    chain = MainnetChain(testdb, GENESIS_PRIVATE_KEY.public_key.to_canonical_address(), GENESIS_PRIVATE_KEY)
    coin_mature_time = chain.get_vm(timestamp=Timestamp(int(time.time()))).consensus_db.coin_mature_time_for_staking
    min_time_between_blocks = chain.get_vm(timestamp=Timestamp(int(time.time()))).min_time_between_blocks
    # now = int(time.time())
    # start = now - max((coin_mature_time * 2), (min_time_between_blocks*20))
    # key_balance_dict = {
    #     private_keys[0]: (to_wei(10, 'ether'), start),
    #     private_keys[1]: (to_wei(200, 'ether'), start + min_time_between_blocks * 1),
    #     private_keys[2]: (to_wei(340, 'ether'), start + min_time_between_blocks * 2),
    #     private_keys[3]: (to_wei(1000, 'ether'), start + min_time_between_blocks * 3),
    #     private_keys[4]: (to_wei(1400, 'ether'), start + min_time_between_blocks * 4),
    #     private_keys[5]: (to_wei(2400, 'ether'), start + min_time_between_blocks * 5),
    #     private_keys[6]: (to_wei(3000, 'ether'), start + min_time_between_blocks * 6),
    #     private_keys[7]: (to_wei(4000, 'ether'), start + min_time_between_blocks * 7),
    #     private_keys[8]: (to_wei(1000, 'ether'), start + min_time_between_blocks * 8),
    #     private_keys[9]: (to_wei(10000, 'ether'), now-coin_mature_time+1),# immature
    # }
    # create_dev_fixed_blockchain_database(testdb, key_balance_dict)

    chain = MainnetChain(testdb, GENESIS_PRIVATE_KEY.public_key.to_canonical_address(), GENESIS_PRIVATE_KEY)

    # class NodeStakingScore(rlp.Serializable, metaclass=ABCMeta):
    #     fields = [
    #         ('recipient_node_wallet_address', address),
    #         ('score', f_big_endian_int),
    #         ('since_block_number', f_big_endian_int),
    #         ('timestamp', f_big_endian_int),
    #         ('v', big_endian_int),
    #         ('r', big_endian_int),
    #         ('s', big_endian_int),
    #     ]

    node_staking_scores = []

    score = 1000000
    for private_key in private_keys:
        node_staking_score = NodeStakingScore(recipient_node_wallet_address = GENESIS_PRIVATE_KEY.public_key.to_canonical_address(),
                                              score = int(score),
                                              since_block_number = 0,
                                              timestamp = int(time.time()),
                                              head_hash_of_sender_chain = chain.chaindb.get_canonical_head_hash(private_key.public_key.to_canonical_address()),
                                              v = 0,
                                              r = 0,
                                              s = 0,
                                              )
        signed_node_staking_score = node_staking_score.get_signed(private_key,MAINNET_NETWORK_ID)
        node_staking_scores.append(signed_node_staking_score)
        score = score/5

    chain = MainnetChain(testdb, GENESIS_PRIVATE_KEY.public_key.to_canonical_address(), GENESIS_PRIVATE_KEY)

    node_staking_scores.sort(key=lambda x: -1* x.score)
    for node_staking_score in node_staking_scores:
        node_staking_score.validate()
        print(node_staking_score.is_signature_valid)
        print(node_staking_score.sender)
        print(node_staking_score.score, chain.get_mature_stake(node_staking_score.sender, node_staking_score.timestamp))


    reward_bundle = chain.get_consensus_db().create_reward_bundle_for_block(GENESIS_PRIVATE_KEY.public_key.to_canonical_address(), node_staking_scores, at_timestamp = int(time.time()))


    chain.get_consensus_db().validate_reward_bundle(reward_bundle, GENESIS_PRIVATE_KEY.public_key.to_canonical_address(), int(time.time()))

    print('AAAAAAAAAAA')
    print(reward_bundle.reward_type_1.amount)
    print(reward_bundle.reward_type_2.amount)
    print(reward_bundle.reward_type_2.proof[0].score)


    initial_balance = chain.get_vm().state.account_db.get_balance(GENESIS_PRIVATE_KEY.public_key.to_canonical_address())
    print("balance before reward = ", initial_balance)

    chain.import_current_queue_block_with_reward(reward_bundle.reward_type_2.proof)

    final_balance = chain.get_vm().state.account_db.get_balance(GENESIS_PRIVATE_KEY.public_key.to_canonical_address())
    print("balance after reward = ",final_balance)
    assert((reward_bundle.reward_type_1.amount + reward_bundle.reward_type_2.amount) == (final_balance- initial_balance))

    print("waiting {} seconds before importing the next block".format(min_time_between_blocks))
    time.sleep(min_time_between_blocks)

    proof_chain = MainnetChain(testdb, private_keys[1].public_key.to_canonical_address(), private_keys[1])
    mature_stake = proof_chain.get_mature_stake()
    print("proof chain mature stake")
    print(mature_stake)

    staking_score = proof_chain.get_signed_peer_score_string_private_key(private_keys[1].to_bytes(), private_keys[0].public_key.to_canonical_address())
    staking_score = staking_score.copy(score=1532)
    staking_score = staking_score.get_signed(private_keys[1], proof_chain.network_id)
    print('staking score')
    print(staking_score.score)

    # fields = [
    #     ('recipient_node_wallet_address', address),
    #     ('score', f_big_endian_int),  # a score out of 1,000,000
    #     ('since_block_number', f_big_endian_int),
    #     ('timestamp', f_big_endian_int),
    #     ('head_hash_of_sender_chain', hash32),
    #     ('v', big_endian_int),
    #     ('r', big_endian_int),
    #     ('s', big_endian_int),
    # ]
    #
    reward_chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])
    reward_bundle = reward_chain.get_consensus_db().create_reward_bundle_for_block(private_keys[0].public_key.to_canonical_address(), [staking_score],at_timestamp=Timestamp(int(time.time())))
    print("reward type 2 amount")
    print(reward_bundle.reward_type_2.amount)
    print("reward type 2 proof")
    print(reward_bundle.reward_type_2.proof)
    reward_chain.import_current_queue_block_with_reward([staking_score])

    # todo: this will fail if the reward block time is too long. Need to manually set it to a small number for the test... or manually make the blocks older?
    print("waiting {} seconds before importing the next block".format(min_time_between_blocks))
    time.sleep(min_time_between_blocks)

    proof_chain = MainnetChain(testdb, private_keys[1].public_key.to_canonical_address(), private_keys[1])
    mature_stake = proof_chain.get_mature_stake()
    print("proof chain mature stake")
    print(mature_stake)

    staking_score = proof_chain.get_signed_peer_score_string_private_key(private_keys[1].to_bytes(), private_keys[
        0].public_key.to_canonical_address())
    staking_score = staking_score.copy(score=1000000)
    staking_score = staking_score.get_signed(private_keys[1], proof_chain.network_id)
    print('staking score')
    print(staking_score.score)

    # fields = [
    #     ('recipient_node_wallet_address', address),
    #     ('score', f_big_endian_int),  # a score out of 1,000,000
    #     ('since_block_number', f_big_endian_int),
    #     ('timestamp', f_big_endian_int),
    #     ('head_hash_of_sender_chain', hash32),
    #     ('v', big_endian_int),
    #     ('r', big_endian_int),
    #     ('s', big_endian_int),
    # ]
    #
    reward_chain = MainnetChain(testdb, private_keys[0].public_key.to_canonical_address(), private_keys[0])
    reward_bundle = reward_chain.get_consensus_db().create_reward_bundle_for_block(
        private_keys[0].public_key.to_canonical_address(), [staking_score], at_timestamp=Timestamp(int(time.time())))
    print("reward type 2 amount")
    print(reward_bundle.reward_type_2.amount)
    print("reward type 2 proof")
    print(reward_bundle.reward_type_2.proof)
    reward_chain.import_current_queue_block_with_reward([staking_score])