Exemplo n.º 1
0
 def test_chain(self):
     G = Block.genesis()
     C = Chain()
     C.append(G)
     b = C.mine()
     self.assertEqual(len(C), 2)
     self.assertEqual(C[0], G)
     self.assertEqual(C[-1], b)
     self.assertEqual(C[0:2], C)
     self.assertEqual(len(C.slice(G, b)), 1)
     self.assertEqual(C[0].height, 0)
     self.assertEqual(C[1].height, 1)
     self.assertTrue(C.is_chained())
Exemplo n.º 2
0
class Adversary(Party):
    def __init__(self, genesis, k, m, mu_B):
        self.attack_phase = AttackPhase.DOUBLE_SPEND
        self.m = m
        self.k = k
        self.mu_B = mu_B
        self.bypass_level = mu_B
        self.bypass_level_blocks_seen = 0
        self.min_bypass_level = 2
        self.honest_chain = Chain(genesis)
        self.last_good_honest_block = None
        self.double_spend_block = None
        self.stitch_block = None
        self.blocks_mined = []
        self.need_bypass = False
        self.adversarial_nipopow = NIPoPoW(k, m)
        self.adversarial_nipopow.chain.append(genesis)

        super().__init__(genesis)

    def play(self):
        if self.attack_phase == AttackPhase.DOUBLE_SPEND:
            self.double_spend_block = self.chain.mine(True)
            self.blocks_mined.append(self.double_spend_block)
            self.adversarial_nipopow.chain.append(self.double_spend_block)
            self.attack_phase = AttackPhase.STITCH

            return None  # withhold double spending block

        if self.attack_phase == AttackPhase.STITCH:
            self.stitch_block = self.honest_chain.mine(True)
            self.stitch_block.set_thorny_interlink([self.double_spend_block])
            self.last_good_honest_block = self.stitch_block
            self.blocks_mined.append(self.stitch_block)
            self.adversarial_nipopow.chain.append(self.stitch_block)
            self.attack_phase = AttackPhase.GROW

            return self.honest_chain

        if self.attack_phase == AttackPhase.GROW:
            # discard = True
            if self.need_bypass:
                b = Block.mine(self.honest_chain[-1])
                b.adversarial = True
                b.set_thorny_interlink([self.last_good_honest_block])
                self.blocks_mined.append(b)
                if b.level < self.bypass_level:
                    # Our block is of low enough level to stay under the radar
                    # so we can use it for bypassing
                    # Bypass any blocks between last_good_honest_block and b,
                    # as they are blocks of a higher level than what we want.
                    self.last_good_honest_block = b
                    self.honest_chain.append(b)
                    self.adversarial_nipopow.chain.append(b)
                    # Bypass was successful.
                    self.need_bypass = False
                    # discard = False
                    return self.honest_chain
                else:
                    # Unfortunately, we mined a block of high level and this
                    # cannot be used for bypassing (as b would then be included
                    # by the honest prover into their proof), so we have to discard
                    # this block and try again
                    # discard = True
                    pass

            # if not discard:
            #     return b

        if self.attack_phase == AttackPhase.SUFFIX:
            b = self.chain.mine(True)
            self.blocks_mined.append(b)
            self.adversarial_nipopow.chain.append(b)
            self.suffix_size += 1
            if self.suffix_size >= k and self.growth_completed():
                self.attack_phase = AttackPhase.DONE

            return None  # withhold suffix

    def block_arrival(self, chain):
        block = chain[-1]

        if self.attack_phase > AttackPhase.STITCH and self.attack_phase < AttackPhase.SUFFIX:
            if block.level >= self.bypass_level:
                # A superblock has appeared which is of higher level than we want.
                # We need to bypass it.
                # (There could be multiple of these in a row)
                self.need_bypass = True
                self.bypass_level_blocks_seen += 1
                if self.bypass_level_blocks_seen >= m:
                    self.bypass_level -= 1
                    self.bypass_level_blocks_seen = 0
                    if self.bypass_level < self.min_bypass_level:
                        self.attack_phase = AttackPhase.SUFFIX
                        self.chain = Chain(self.last_good_honest_block)
                        self.suffix_size = 0
            else:
                if not self.need_bypass:
                    self.last_good_honest_block = block
                    self.adversarial_nipopow.chain.append(block)

        self.honest_chain = chain

    def growth_completed(self):
        m = self.m
        mu_B = self.mu_B
        C = self.honest_chain.slice(self.stitch_block)
        if C.count_upchain(mu_B) < 2 * m:
            return False
        b = C[0]
        for mu in range(mu_B - 1, 0, -1):
            b = C.upchain(mu + 1)[m]
            C = C.slice(b)
            if C.count_upchain(mu) < 2 * m:
                return False
        return True

    def ready(self):
        return self.attack_phase == AttackPhase.DONE