def test_risky_spend(self): self.chain_id = self.minter spend_value = Decimal(10.00, self.con) spend_dot = Dot((1, "11111")) self.state.apply_spend( self.chain_id, GENESIS_LINK, GENESIS_LINK, spend_dot, self.spender, self.receiver, spend_value, ) assert float(self.state.get_balance(self.spender)) == -10 # Next spend - tries to pretend as if hasn't happen self.new_spend_value = Decimal(6.00, self.con) self.new_spend_dot = Dot((2, "22222")) self.state.apply_spend( self.chain_id, Links((spend_dot,)), Links((spend_dot,)), self.new_spend_dot, self.spender, self.receiver, self.new_spend_value, ) # As the value is less -> the balance will not change until confirmed, or rejected assert float(self.state.get_balance(self.spender)) == -10
def add_block(self, block_blob: bytes, block: "PlexusBlock") -> None: block_hash = block.hash block_tx = block.transaction # 1. Add block blob and transaction blob to the block storage self.block_store.add_block(block_hash, block_blob) self.block_store.add_tx(block_hash, block_tx) self.block_store.add_extra(block_hash, encode_raw({b"type": block.type})) # 2. There are two chains: personal and community chain pers = block.public_key com = block.com_id if pers == com: pers = block.com_prefix + pers com = block.com_prefix + com else: com = block.com_prefix + com # 2.1: Process the block wrt personal chain if pers not in self.chains: self.chains[pers] = self.chain_factory.create_chain() pers_block_dot = Dot((block.sequence_number, block.short_hash)) pers_dots_list = self.chains[pers].add_block(block.previous, block.sequence_number, block_hash) full_dot_id = pers + encode_raw(pers_block_dot) self.block_store.add_dot(full_dot_id, block_hash) # TODO: add more chain topic # Notify subs of the personal chain self.notify(ChainTopic.ALL, chain_id=pers, dots=pers_dots_list) self.notify(ChainTopic.PERSONAL, chain_id=pers, dots=pers_dots_list) self.notify(pers, chain_id=pers, dots=pers_dots_list) # 2.2: add block to the community chain if com != EMPTY_PK: if com == pers: # Chain was processed already, notify rest self.notify(ChainTopic.GROUP, chain_id=com, dots=pers_dots_list) else: if com not in self.chains: self.chains[com] = self.chain_factory.create_chain() com_block_dot = Dot((block.com_seq_num, block.short_hash)) com_dots_list = self.chains[com].add_block( block.links, block.com_seq_num, block_hash) full_dot_id = com + encode_raw(com_block_dot) self.block_store.add_dot(full_dot_id, block_hash) self.notify(ChainTopic.ALL, chain_id=com, dots=com_dots_list) self.notify(ChainTopic.GROUP, chain_id=com, dots=com_dots_list) self.notify(com, chain_id=com, dots=com_dots_list)
def test_add_mint(self): chain_id = self.minter value = Decimal(12.00, self.con) dot = Dot((1, "123123")) self.state.apply_mint(chain_id, dot, GENESIS_LINK, self.minter, value) assert self.state.get_balance(self.minter) == value
def test_spend_fork(self): value = Decimal(1, self.con) dot = Dot((1, b"123123")) chain_id = self.spender self.state.apply_spend( chain_id, GENESIS_LINK, GENESIS_LINK, dot, self.spender, self.receiver, value, ) assert float(self.state.get_balance(self.spender)) == -1 new_dot = Dot((1, b"5464646")) self.state.apply_spend( chain_id, GENESIS_LINK, GENESIS_LINK, new_dot, self.spender, self.receiver, value, ) assert float(self.state.get_balance(self.spender)) == -2 assert self.state.is_chain_forked(chain_id, self.spender) assert self.state.was_chain_forked(chain_id, self.spender) # Fix the fork: f_dot = Dot((2, b"000000")) value = Decimal(3, self.con) self.state.apply_spend( chain_id, Links((dot, new_dot)), Links((dot, new_dot)), f_dot, self.spender, self.receiver, value, ) assert float(self.state.get_balance(self.spender)) == -3 assert not self.state.is_chain_forked(chain_id, self.spender) assert self.state.was_chain_forked(chain_id, self.spender)
def _find_first_conflicting_point(self, conf_dict: Dict, chain: BaseChain) -> Set[Dot]: to_request = set() for sn, hash_vals in conf_dict.items(): local_val = chain.get_all_short_hash_by_seq_num(sn) if not local_val: # Don't know this value => request from peer to_request.update(Dot((sn, k) for k in hash_vals)) continue diff_val = local_val - set(hash_vals) sim_diff = set(hash_vals) - local_val if sim_diff: to_request.update(Dot((sn, k)) for k in sim_diff) # If there is a hash that is known if diff_val: # First inconsistency point met return {Dot((sn, k)) for k in diff_val}, to_request return set(), to_request
def test_mint_and_spend_fork(self): chain_id = self.minter value = Decimal(12.00, self.con) dot = Dot((1, "123123")) self.state.apply_mint(chain_id, dot, GENESIS_LINK, self.minter, value) assert self.state.get_balance(self.minter) == value spend_value = Decimal(12.00, self.con) spend_dot = Dot((1, "323123")) self.state.apply_spend( chain_id, GENESIS_LINK, GENESIS_LINK, spend_dot, self.spender, self.receiver, spend_value, ) assert float(self.state.get_balance(self.spender)) == 0 assert self.state.is_chain_forked(chain_id, self.spender)
def std_vals(self): self.chain_id = b"chain_id" self.block_dot = Dot((3, ShortKey("808080"))) self.block_dot_encoded = encode_raw(self.block_dot) self.dot_id = self.chain_id + self.block_dot_encoded self.test_hash = b"test_hash" self.tx_blob = b"tx_blob" self.block_blob = b"block_blob" self.test_block = FakeBlock() self.pers = self.test_block.public_key self.com_id = self.test_block.com_id
def test_add_claim(self): value = Decimal(12.11, self.con) dot = Dot((1, "123123")) chain_id = self.spender self.state.apply_spend( chain_id, GENESIS_LINK, GENESIS_LINK, dot, self.spender, self.receiver, value, ) assert self.state.last_spend_values[self.spender][self.receiver][dot] == value assert float(self.state.get_balance(self.spender)) == -12.11 claim_dot = Dot((1, "2323")) self.state.apply_confirm( chain_id, self.receiver, Links((dot,)), claim_dot, self.spender, dot, value ) assert self.state.last_spend_values[self.spender][self.receiver][dot] == value assert float(self.state.get_balance(self.receiver)) == 12.11
def test_mint_and_spend(self): chain_id = self.minter value = Decimal(15.00, self.con) dot = Dot((1, "123123")) self.state.apply_mint(chain_id, dot, GENESIS_LINK, self.minter, value) assert self.state.get_balance(self.minter) == value assert not self.state.is_chain_forked(chain_id, self.minter) spend_value = Decimal(12.00, self.con) spend_dot = Dot((2, "23123")) self.state.apply_spend( chain_id, GENESIS_LINK, Links((dot,)), spend_dot, self.spender, self.receiver, spend_value, ) assert float(self.state.get_balance(self.spender)) == 3 assert not self.state.is_chain_forked(chain_id, self.spender) assert not self.state.was_chain_forked(chain_id, self.spender) return chain_id, spend_value, spend_dot
def test_mint_value_unbound_value(self, set_vals): mint_tx = {b"value": 10**7 - 1} chain_id = set_vals.community_id minter = set_vals.nodes[0].overlay.my_pub_key_bin set_vals.nodes[0].overlay.state_db.apply_mint( chain_id, Dot((1, "123123")), GENESIS_LINK, minter, Decimal(mint_tx.get(b"value"), set_vals.context), True, ) next_mint = {b"value": 1} with pytest.raises(UnboundedMintException): set_vals.nodes[0].overlay.verify_mint(chain_id, minter, next_mint)
def test_valid_spend_with_reject(self): chain_id, value, spend_dot = self.test_mint_and_spend() reject_dot = Dot((3, "33333")) # Add confirmation from the counter-party self.state.apply_reject( chain_id, self.receiver, Links((spend_dot,)), reject_dot, self.spender, spend_dot, ) # As the transaction is confirmed, inconsistency is resolved assert float(self.state.get_balance(self.spender)) == 3 + value assert float(self.state.get_balance(self.receiver)) == 0
def test_risky_spend_with_reject(self): self.test_risky_spend() reject_dot = Dot((3, "33333")) # Add confirmation from the counter-party self.state.apply_reject( self.chain_id, self.receiver, Links((self.new_spend_dot,)), reject_dot, self.spender, self.new_spend_dot, ) # As the transaction is rejected - the effect of it is reverted to the previous stable state - zero. assert float(self.state.get_balance(self.spender)) == 0 assert float(self.state.get_balance(self.receiver)) == 0 assert self.state.was_balance_negative(self.spender)
def test_risky_spend_with_confirm(self): self.test_risky_spend() confirm_dot = Dot((3, "33333")) # Add confirmation from the counter-party self.state.apply_confirm( self.chain_id, self.receiver, Links((self.new_spend_dot,)), confirm_dot, self.spender, self.new_spend_dot, self.new_spend_value, ) # As the transaction is confirmed, inconsistency is resolved assert float(self.state.get_balance(self.spender)) == -6 assert float(self.state.get_balance(self.receiver)) == 6 assert self.state.was_balance_negative(self.spender)
def test_state_updates(self): chain_id = self.minter value = Decimal(12.00, self.con) dot = Dot((1, b"123123")) self.state.apply_mint( chain_id, dot, GENESIS_LINK, self.minter, value, store_update=True ) assert self.state.get_balance(self.minter) == value assert not self.state.is_chain_forked(chain_id, self.minter) spend_value = Decimal(12.00, self.con) spend_dot = Dot((2, b"23123")) self.state.apply_spend( chain_id, GENESIS_LINK, Links((dot,)), spend_dot, self.spender, self.receiver, spend_value, store_status_update=True, ) assert float(self.state.get_balance(self.spender)) == 0 assert not self.state.is_chain_forked(chain_id, self.spender) claim_dot = Dot((3, b"23323")) self.state.apply_confirm( chain_id, self.receiver, Links((spend_dot,)), claim_dot, self.spender, spend_dot, spend_value, store_update=True, ) v = self.state.get_closest_peers_status(chain_id, 1) assert v is not None assert v[0] == 1 assert len(v[1]) == 1 assert v[1].get(shorten(self.spender)) == (True, True) v = self.state.get_closest_peers_status(chain_id, 2) assert v is not None assert ( (v[0] == 2) and (len(v[1]) == 1) and (v[1].get(shorten(self.spender)) == (True, True)) ) v = self.state.get_closest_peers_status(chain_id, 3) assert v is not None assert ( v[0] == 3 and len(v[1]) == 2 and (v[1].get(shorten(self.spender)) == (True, True)) and (v[1].get(shorten(self.receiver)) == (True, True)) ) assert v[1] == self.state.get_last_peer_status(chain_id)
def com_dot(self) -> Dot: return Dot((self.com_seq_num, self.short_hash))
def pers_dot(self) -> Dot: return Dot((self.sequence_number, self.short_hash))
def test_add_invalid_claim(self): value = Decimal(1, self.con) dot = Dot((1, "123123")) chain_id = self.spender self.state.apply_spend( chain_id, GENESIS_LINK, GENESIS_LINK, dot, self.spender, self.receiver, value, ) assert self.state.last_spend_values[self.spender][self.receiver][dot] == value assert float(self.state.get_balance(self.spender)) == -1 new_dot = Dot((1, "223123")) self.state.apply_spend( chain_id, GENESIS_LINK, GENESIS_LINK, new_dot, self.spender, self.receiver, value, ) assert self.state.last_spend_values[self.spender][self.receiver][dot] == value assert float(self.state.get_balance(self.spender)) == -2 assert self.state.was_chain_forked(chain_id, self.spender) claim_dot = Dot((2, "33323")) with pytest.raises(InvalidClaimException): # Should raise exception as the claim links are not correct self.state.apply_confirm( chain_id, self.receiver, GENESIS_LINK, claim_dot, self.spender, dot, value, ) self.state.apply_confirm( chain_id, self.receiver, Links((dot,)), claim_dot, self.spender, dot, value ) assert self.state.last_spend_values[self.spender][self.receiver][dot] == value assert float(self.state.get_balance(self.receiver)) == 1 with pytest.raises(InvalidClaimException): # Double claim - should raise exception self.state.apply_confirm( chain_id, self.receiver, Links((claim_dot,)), claim_dot, self.spender, dot, value, ) assert float(self.state.get_balance(self.receiver)) == 1 assert not self.state.was_chain_forked(chain_id, self.receiver) # Add inconsistent claim inconsistent_value = Decimal(100, self.con) with pytest.raises(InconsistentClaimException): self.state.apply_confirm( chain_id, self.receiver, Links((claim_dot,)), claim_dot, self.spender, new_dot, inconsistent_value, ) assert float(self.state.get_balance(self.receiver)) == 1 assert not self.state.was_chain_forked(chain_id, self.receiver)