Beispiel #1
0
class TestCoin(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        super(TestCoin, self).__init__(*args, **kwargs)
        self.network = testNetwork()
        self.ec_keys = [random_sk() for _ in range(10)]
        self.public_keys = [make_fake_public_key(secret_key=sk) for sk in self.ec_keys]
        self.addresses = [address_from_public_key(public_key) for public_key in self.public_keys]
        for address in self.addresses:
            for value in [10000, 1000, 500, 100]:
                self.network.add_coin(address, value, tx_hash = fake_hash(address, value))
        self.coin = Coin(self.network)

    def test_001_sufficient_funds(self):
        pubkey_1, pubkey_2, pubkey_3 = self.public_keys[0:3]
        address_1, address_2, address_3 = self.addresses[0:3]
        address_x = "111111111111111111111111111"
        inputs = {
            pubkey_1:[
                fake_hash(address_1, 1000) + ":0",
                fake_hash(address_1, 100)  + ":0"
                ],
            pubkey_2:[
                fake_hash(address_2, 1000) + ":0"]
            }
        self.assertTrue(self.coin.check_inputs_for_sufficient_funds(inputs, 2022))
        self.assertFalse(self.coin.check_inputs_for_sufficient_funds(inputs, 20022))
        bad_hash_input = {Hash("pubkey_1").hex():["a1h4"]}
        self.assertIsNone(self.coin.check_inputs_for_sufficient_funds(bad_hash_input, 2222))
        bad_address_input = {Hash("pubkey_111").hex():["a111h1"]}
        self.assertIsNone(self.coin.check_inputs_for_sufficient_funds(bad_address_input, 2222))


    def test_002_get_coins(self):
        pubkey_1, pubkey_2, pubkey_3 = self.public_keys[0:3]
        address_1, address_2, address_3 = self.addresses[0:3]
        inputs = {
            pubkey_1:[
                fake_hash(address_1, 1000)+":0",
                fake_hash(address_1, 100)+":0"
                ],
            pubkey_2:[fake_hash(address_2, 1000)+":0"]
            }
        coins = self.coin.get_coins(inputs)
        self.assertEquals(coins[pubkey_1][0]["value"], 1000)
        self.assertEquals(coins[pubkey_1][0]["tx_hash"], fake_hash(address_1, 1000))
        self.assertEquals(coins[pubkey_1][1]["value"], 100)
        self.assertEquals(coins[pubkey_1][1]["tx_hash"], fake_hash(address_1, 100))
        self.assertEquals(coins[pubkey_2][0]["value"], 1000)
        self.assertEquals(coins[pubkey_2][0]["tx_hash"], fake_hash(address_2, 1000))
        bad_input = {
            pubkey_1:[fake_hash(address_2, 2222)]
        }
        coins = self.coin.get_coins(bad_input)
        self.assertIsNone(coins)

    def test_003_make_unsigned_transaction(self):
        fee = 50
        amount = 1000
        pubkey_1, pubkey_2, pubkey_3, pubkey_4 = self.public_keys[0:4]
        address_1, address_2, address_3, address_4 = self.addresses[0:4]
        inputs = {
            "player_1_vk":
            {
                pubkey_1:[
                    fake_hash(address_1, 500) + ":0",
                    fake_hash(address_1, 100) + ":0",
                ],
                pubkey_2:[
                    fake_hash(address_2, 500) + ":0",
                ]
            },
            "player_2_vk":
            {
                pubkey_3:[
                    fake_hash(address_3, 1000) + ":0" ,
                    fake_hash(address_3, 100) + ":0",
                ]
            },
            "player_3_vk":
            {
                pubkey_4:[
                    fake_hash(address_4, 10000)+ ":0",
                ]
            }
        }
        outputs = [make_fake_address() for _ in range(3)]
        changes = { player:make_fake_address() for player in ["player_1_vk", "player_2_vk", "player_3_vk"]}
        transaction = self.coin.make_unsigned_transaction(amount, fee, inputs, outputs, changes)

        flat_inputs = [
            {"public_key": pubkey_1, "address": address_1, "tx_hash": inputs["player_1_vk"][pubkey_1][0].split(":")[0], "value": 500},
            {"public_key": pubkey_1, "address": address_1, "tx_hash": inputs["player_1_vk"][pubkey_1][1].split(":")[0], "value": 100},
            {"public_key": pubkey_2, "address": address_2, "tx_hash": inputs["player_1_vk"][pubkey_2][0].split(":")[0], "value": 500},
            {"public_key": pubkey_3, "address": address_3, "tx_hash": inputs["player_2_vk"][pubkey_3][0].split(":")[0], "value": 1000},
            {"public_key": pubkey_3, "address": address_3, "tx_hash": inputs["player_2_vk"][pubkey_3][1].split(":")[0], "value": 100},
            {"public_key": pubkey_4, "address": address_4, "tx_hash": inputs["player_3_vk"][pubkey_4][0].split(":")[0], "value": 10000},
        ]
        flat_inputs.sort(key=lambda x:x["tx_hash"])
        for i, input in enumerate(transaction.inputs()):
            self.assertEquals(input['value'], flat_inputs[i]['value'])
            self.assertEquals(input['tx_hash'], flat_inputs[i]['tx_hash'])
            self.assertEquals(input['pubkeys'][0], flat_inputs[i]['public_key'])
            self.assertEquals(input['address'].to_string(Address.FMT_LEGACY), flat_inputs[i]['address'])
        amounts = {"player_1_vk":1100, "player_2_vk":1100, "player_3_vk":10000 }
        flat_changes = [(changes[player], amounts[player]) for player in sorted(changes)]
        flat_outputs = [
            (0, Address.from_string(outputs[0]), amount),
            (0, Address.from_string(outputs[1]), amount),
            (0, Address.from_string(outputs[2]), amount),
            (0, Address.from_string(flat_changes[0][0]), flat_changes[0][1] - amount -fee),
            (0, Address.from_string(flat_changes[1][0]), flat_changes[1][1] - amount -fee),
            (0, Address.from_string(flat_changes[2][0]), flat_changes[2][1] - amount - fee)
        ]
        self.assertEquals(transaction.outputs(), flat_outputs)


    def test_004_all_about_signatures(self):
        fee = 50
        amount = 1000
        sk_1, sk_2, sk_3, sk_4 = self.ec_keys[0:4]
        pubkey_1, pubkey_2, pubkey_3, pubkey_4 = self.public_keys[0:4]
        address_1, address_2, address_3, address_4 = self.addresses[0:4]
        inputs_vk_1 = {
            pubkey_1:[
                fake_hash(address_1, 500)+":0",
                fake_hash(address_1, 100)+":0",
            ],
            pubkey_2:[
                fake_hash(address_2, 500)+":0",
            ]
        }
        inputs_vk_2 = {
            pubkey_3:[
                fake_hash(address_3, 1000)+":0",
                fake_hash(address_3, 100)+":0",
            ]
        }
        inputs_vk_3 = {
            pubkey_4:[
                fake_hash(address_4, 10000)+":0",
            ]
        }
        inputs = {
            "player_1_vk": inputs_vk_1,
            "player_2_vk": inputs_vk_2,
            "player_3_vk": inputs_vk_3
        }
        outputs = [make_fake_address() for _ in range(3)]
        changes = { player:make_fake_address() for player in ["player_1_vk", "player_2_vk", "player_3_vk"]}
        transaction = self.coin.make_unsigned_transaction(amount, fee, inputs, outputs, changes)
        secret_keys_vk_1 = {pubkey_1:sk_1, pubkey_2: sk_2}
        secret_keys_vk_2 = {pubkey_3:sk_3}
        secret_keys_vk_3 = {pubkey_4:sk_4}
        signatures = {}
        signatures.update(self.coin.get_transaction_signature(transaction, inputs_vk_1, secret_keys_vk_1))
        signatures.update(self.coin.get_transaction_signature(transaction, inputs_vk_2, secret_keys_vk_2))
        signatures.update(self.coin.get_transaction_signature(transaction, inputs_vk_3, secret_keys_vk_3))
        for player in inputs:
            for pubkey in inputs[player]:
                for tx_hash in inputs[player][pubkey]:
                    signature = signatures[tx_hash]
                    self.assertTrue(self.coin.verify_tx_signature(signature , transaction, pubkey, tx_hash))
        self.coin.add_transaction_signatures(transaction, signatures)


    def test_005_verigy_signatures(self):
        message = b"some_message_for_test"
        sk_1 = random_sk()
        pubkey_1 = sk_1.get_public_key(True)
        signature_1 = sk_1.sign_message(message, True)
        self.assertTrue(self.coin.verify_signature(signature_1, message, pubkey_1))

        sk_2 = random_sk()
        pubkey_2 = sk_2.get_public_key(False)
        signature_2 = sk_2.sign_message(message, False)
        self.assertTrue(self.coin.verify_signature(signature_2, message, pubkey_2))
        self.assertFalse(self.coin.verify_signature(signature_2, message, pubkey_1))
class TestRound(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super(TestRound, self).__init__(*args, **kwargs)
        self.network = testNetwork()
        self.ec_keys = [random_sk() for _ in range(10)]
        self.public_keys = [
            make_fake_public_key(secret_key=sk) for sk in self.ec_keys
        ]
        self.addresses = [
            address_from_public_key(public_key)
            for public_key in self.public_keys
        ]
        for address in self.addresses:
            for value in [10000, 1000, 500, 100]:
                self.network.add_coin(address,
                                      value,
                                      tx_hash=fake_hash(address, value))
        self.coin = Coin(self.network)

    def setUp(self):
        self.number_of_players = 5
        self.crypto = Crypto()
        self.messages = Messages()
        self.outchan = Channel()
        self.inchan = Channel()
        self.logchan = Channel()
        self.session = b"session"
        self.phase = Phase('Announcement')
        self.amount = 1000
        self.fee = 50
        self.secret_keys = [random_sk() for _ in range(self.number_of_players)]
        self.verification_keys = [
            make_fake_public_key(secret_key=sk) for sk in self.secret_keys
        ]
        self.players = {
            index + 1: vk
            for index, vk in enumerate(self.verification_keys)
        }
        self.inputs = {
            self.verification_keys[0]: {
                self.public_keys[0]: [
                    fake_hash(self.addresses[0], 500) + ":0",
                    fake_hash(self.addresses[0], 100) + ":0"
                ],
                self.public_keys[1]:
                [fake_hash(self.addresses[1], 500) + ":0"]
            },
            self.verification_keys[1]: {
                self.public_keys[2]: [
                    fake_hash(self.addresses[2], 1000) + ":0",
                    fake_hash(self.addresses[2], 100) + ":0"
                ],
            },
            self.verification_keys[2]: {
                self.public_keys[3]:
                [fake_hash(self.addresses[3], 1000) + ":0"],
                self.public_keys[4]:
                [fake_hash(self.addresses[4], 100) + ":0"],
            },
            self.verification_keys[3]: {
                self.public_keys[5]:
                [fake_hash(self.addresses[5], 10000) + ":0"],
            },
            self.verification_keys[4]: {
                self.public_keys[6]:
                [fake_hash(self.addresses[6], 1000) + ":0"],
                self.public_keys[7]:
                [fake_hash(self.addresses[7], 500) + ":0"]
            }
        }
        self.sks = {
            self.public_keys[0]: self.ec_keys[0],
            self.public_keys[1]: self.ec_keys[1],
        }
        self.all_sks = {
            self.verification_keys[0]: {
                self.public_keys[0]: self.ec_keys[0],
                self.public_keys[1]: self.ec_keys[1]
            },
            self.verification_keys[1]: {
                self.public_keys[2]: self.ec_keys[2],
            },
            self.verification_keys[2]: {
                self.public_keys[3]: self.ec_keys[3],
                self.public_keys[4]: self.ec_keys[4],
            },
            self.verification_keys[3]: {
                self.public_keys[5]: self.ec_keys[5],
            },
            self.verification_keys[4]: {
                self.public_keys[6]: self.ec_keys[6],
                self.public_keys[7]: self.ec_keys[7]
            }
        }
        self.sk = self.secret_keys[0]
        self.verification_key = self.verification_keys[0]
        self.new_addresses = [
            make_fake_address() for _ in range(self.number_of_players)
        ]
        self.changes = {
            vk: make_fake_address()
            for vk in self.verification_keys
        }
        self.addr_new = self.new_addresses[0]
        self.change = make_fake_address()
        self.round = Round(self.coin, self.crypto, self.messages, self.inchan,
                           self.outchan, self.logchan, self.session,
                           self.phase, self.amount, self.fee, self.sk,
                           self.sks, self.inputs, self.verification_key,
                           self.players, self.addr_new, self.change)

    def test_001_test_init(self):
        self.assertEquals(self.round.number_of_players, self.number_of_players)
        self.assertEquals(self.round.me, 1)

    def test_002_blame_insufficient_funds(self):
        self.assertTrue(self.round.blame_insufficient_funds())
        self.assertEquals(self.logchan.get(),
                          "Player 1 finds sufficient funds")
        self.round.inputs[self.round.players[1]][
            self.public_keys[1]][0] = fake_hash(self.addresses[1], 100) + ":0"
        self.assertFalse(self.round.blame_insufficient_funds())
        self.assertEquals(self.logchan.get(),
                          "Blame: insufficient funds of player 1")
        self.assertIsNotNone(self.outchan.get(timeout=0))

    def test_0021_blame_insufficient_funds_network_fail(self):
        self.round.coin.network = None
        self.assertIsNone(self.round.blame_insufficient_funds())
        self.assertEquals(self.logchan.get(),
                          "Error: blockchain network fault!")

    def test_003_process_equivocation_check(self):
        # SetUp
        self.round.new_addresses = self.new_addresses
        self.round.changes = self.changes
        self.round.encryption_keys = {
            player: "encryption_key"
            for player in self.round.players.values()
        }
        computed_hash = self.crypto.hash(
            str(self.round.new_addresses) + str([
                self.round.encryption_keys[self.round.players[i]]
                for i in sorted(self.round.players)
            ]))
        # Wrong phase ignored
        self.round.phase = 'Announcement'
        self.round.process_equivocation_check()
        self.assertTrue(self.outchan.empty())
        self.assertTrue(self.logchan.empty())
        # Set Proper phase
        # Check if incomplete inbox ignored
        self.round.phase = 'EquivocationCheck'
        self.round.inbox[self.messages.phases[self.round.phase]] = {
            self.round.players[1]: "something"
        }
        self.round.process_equivocation_check()
        self.assertTrue(self.outchan.empty())
        self.assertTrue(self.logchan.empty())
        # check processing with inbox completed
        # making the fake messages with equal hashes and fulfill inbox with it
        self.messages.clear_packets()
        self.messages.add_hash(computed_hash)
        message = self.messages.packets.SerializeToString()
        self.round.inbox[self.messages.phases[self.round.phase]] = {
            player: message
            for player in self.round.players.values()
        }
        self.round.process_equivocation_check()
        self.assertEquals(self.round.phase, "VerificationAndSubmission")
        self.assertTrue(self.logchan.get(timeout=0),
                        "Player 1 reaches phase 5")
        self.assertTrue(self.logchan.get(timeout=0),
                        "Player 1 send transction signatures")
        self.assertTrue(self.logchan.empty())
        self.assertIsNotNone(self.round.transaction)
        self.messages.packets.ParseFromString(self.outchan.get(timeout=0))
        self.assertTrue(self.outchan.empty())
        signatures = self.messages.get_signatures()
        for pubkey in self.round.inputs[self.round.vk]:
            for hash in self.round.inputs[self.round.vk][pubkey]:
                self.assertIn(hash, signatures)
        # Checking the blame behaviour
        self.round.phase = 'EquivocationCheck'
        self.messages.clear_packets()
        self.messages.add_hash(computed_hash + b"fail")
        message = self.messages.packets.SerializeToString()
        self.round.inbox[self.messages.phases[self.round.phase]][
            self.round.vk] = message
        self.round.process_equivocation_check()
        self.assertEquals(self.round.phase, "Blame")
        self.assertEquals(self.logchan.get(timeout=0),
                          "Player 1 found bad hash from 1")
        self.assertEquals(self.logchan.get(timeout=0),
                          "Blame: wrong hash computed by player 1")
        self.assertTrue(self.logchan.empty())
        self.messages.packets.ParseFromString(self.outchan.get(timeout=0))
        self.assertEquals(
            self.messages.get_blame_reason(),
            self.messages.blame_reason("EQUIVOCATIONFAILURE")
        )  # This should be replaced with allias for the reason instread of 2.

    def test_004_process_verification_and_submission(self):
        self.round.phase = 'VerificationAndSubmission'
        # Check if nothing is happend with incomplete inbox
        self.round.inbox[self.messages.phases[self.round.phase]] = {
            self.round.players[1]: "something"
        }
        self.round.process_verification_and_submission()
        self.assertTrue(self.outchan.empty())
        self.assertTrue(self.logchan.empty())
        # Check normal case
        transaction = self.coin.make_unsigned_transaction(
            self.amount, self.fee, self.inputs, self.new_addresses,
            self.changes)
        self.round.transaction = transaction
        signatures = {
            player:
            self.coin.get_transaction_signature(transaction,
                                                self.inputs[player],
                                                self.all_sks[player])
            for player in self.round.players.values()
        }
        transaction = self.coin.make_unsigned_transaction(
            self.amount, self.fee, self.inputs, self.new_addresses,
            self.changes)
        for player in signatures:
            self.messages.clear_packets()
            self.messages.add_signatures(signatures[player])
            self.round.inbox[self.messages.phases[self.round.phase]][
                player] = self.messages.packets.SerializeToString()
        self.round.process_verification_and_submission()
        self.assertIsNotNone(self.round.tx)
        self.assertTrue(self.round.done)
        self.assertEquals(self.logchan.get(timeout=0),
                          "Player 1 got transction signatures")
        self.assertEquals(self.logchan.get(timeout=0), "Player 1 done")
        self.assertEquals(self.logchan.get(timeout=0),
                          "Player 1 complete protocol")
        self.assertTrue(self.logchan.empty())
        self.assertTrue(self.outchan.empty())
        # Abnormal case
        self.round.done = False
        self.round.tx = None
        transaction = self.coin.make_unsigned_transaction(
            self.amount, self.fee, self.inputs, self.new_addresses,
            self.changes)
        signatures = {
            player:
            self.coin.get_transaction_signature(transaction,
                                                self.inputs[player],
                                                self.all_sks[player])
            for player in self.round.players.values()
        }
        signatures[self.round.vk][self.inputs[self.round.vk][
            self.public_keys[0]][0]] = signatures[self.round.vk][self.inputs[
                self.round.vk][self.public_keys[0]][1]]
        for player in signatures:
            self.messages.clear_packets()
            self.messages.add_signatures(signatures[player])
            self.round.inbox[self.messages.phases[self.round.phase]][
                player] = self.messages.packets.SerializeToString()
        self.round.transaction = self.coin.make_unsigned_transaction(
            self.amount, self.fee, self.inputs, self.new_addresses,
            self.changes)
        self.round.process_verification_and_submission()
        self.assertEquals(self.logchan.get(timeout=0),
                          "Player 1 got transction signatures")
        self.assertEquals(self.logchan.get(timeout=0),
                          "Blame: wrong transaction signature from player 1")
        self.assertTrue(self.logchan.empty())
        self.messages.packets.ParseFromString(self.outchan.get(timeout=0))
        self.assertEquals(self.messages.get_blame_reason(),
                          self.messages.blame_reason("Invalid Signature"))