예제 #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())
예제 #2
0
    def test_verify_inconsistency(self):
        k = 1
        m = 3

        C = self.make_expected_distribution_superchain(2**7, True)

        C1 = Chain(C[-1])
        for i in range(10):
            C1.mine()
            C1[-1].level = 30
        C2 = self.make_expected_distribution_superchain(2**7, True, C[-1])

        self.assertTrue(C1.is_chained())
        self.assertTrue(C2.is_chained())

        proof1 = NIPoPoW.prove(k, m, C1)
        proof2 = NIPoPoW.prove(k, m, C2)

        self.assertTrue(proof1.chain.is_chained())
        self.assertTrue(proof2.chain.is_chained())

        # a shorter chain with more superblocks wins
        # (this will never happen in practice for large values of m)
        self.assertWins(NIPoPoW.prove(k, m, C1), NIPoPoW.prove(k, m, C2))
예제 #3
0
class NIPoPoW:
    @classmethod
    def prove(cls, k, m, C):
        proof = NIPoPoW(k, m)
        if len(C) <= k:
            proof.chain = C
            return proof

        chi = C[-k:]
        stable_C = C[:-k]
        pi = Chain()
        b = C[0]
        for mu in range(len(stable_C[-1].real_interlink) + 1, -1, -1):
            stable_C = stable_C.slice(b)
            alpha = stable_C.upchain(mu)
            pi |= alpha
            if len(alpha) > m:
                b = alpha[-m]

        proof.chain = pi | chi
        return proof

    @classmethod
    def score(cls, proof1, proof2):
        assert proof1.k == proof2.k and proof1.m == proof2.m, 'Proofs are incomparable'
        assert proof1.chain[0] == proof2.chain[0], 'Proofs must share a genesis block'

        k = proof1.k

        if len(proof1.chain) < k or len(proof2.chain) < k:
            # Comparison at the 0 level
            return proof1.chain[0], (len(proof1.chain), 0), (len(proof2.chain), 0)

        pi_1, chi_1 = proof1.chain[:-k], proof1.chain[-k:]
        pi_2, chi_2 = proof2.chain[:-k], proof2.chain[-k:]

        b = (pi_1 & pi_2)[-1]

        return b, proof1.best_arg(b), proof2.best_arg(b)

    def __init__(self, k, m):
        self.k = k
        self.m = m
        self.chain = Chain()

    def best_arg(self, b):
        k, m = self.k, self.m

        pi = self.chain[:-k]
        fork = pi.slice(b)[1:]
        best_score = 0
        best_level = -1
        for mu in range(len(pi[-1].real_interlink) + 1):
            argument = fork.count_upchain(mu)
            if argument >= m or mu == 0:
                mu_score = 2**mu * argument
                if mu_score > best_score:
                    best_score = mu_score
                    best_level = mu
        return best_score, best_level

    def __ge__(self, other):
        if not self.chain.is_chained():
            return False
        if not other.chain.is_chained():
            return True

        b, (score1, mu1), (score2, mu2) = NIPoPoW.score(self, other)

        return score1 >= score2