1import unittest
2
3from pycoin.ecdsa.secp256k1 import secp256k1_generator
4from pycoin.encoding.hexbytes import b2h, b2h_rev
5from pycoin.intbytes import int2byte
6from pycoin.networks.registry import network_for_netcode
7from pycoin.satoshi.der import sigdecode_der, sigencode_der
8
9
10PRIV_KEYS = (
11    2330949616242593315303241053456316633827293588958882755297900732239663851861,
12    4437411780076344925846479906614060621668407514498402815534040340772719979673,
13    14311886404724799688521454580288220586308410691395501373612453626821267193196,
14    16404731722033649474165521611800542240555275746052963990137782680023514762282,
15    92715304942310420502826004911529506622922082818576946681102234225452853924813,
16    103235678552410630318322729483874198805317322052500844759252733409163632402845,
17)
18
19
20def sigcheck(a_key, a_hash_for_sig, a_sig):
21    """
22    Returns True if a_key was used to generate a_sig from a_hash_for_sig;
23    False otherwise.
24    """
25    r, s = sigdecode_der(a_sig)
26
27    return secp256k1_generator.verify(a_key.public_pair(), a_hash_for_sig, (r, s))
28
29
30def sigmake(a_key, a_hash_for_sig, a_sig_type):
31    """
32    Signs a_hash_for_sig with a_key and returns a DER-encoded signature
33    with a_sig_type appended.
34    """
35    order = secp256k1_generator.order()
36    r, s = secp256k1_generator.sign(a_key.secret_exponent(), a_hash_for_sig)
37
38    if s + s > order:
39        s = order - s
40
41    return sigencode_der(r, s) + int2byte(a_sig_type)
42
43
44class SighashSingleTest(unittest.TestCase):
45
46    def test_sighash_single(self):
47        for netcode in ["BTC", "XTN"]:
48            self._test_sighash_single(network_for_netcode(netcode))
49
50    def _test_sighash_single(self, network):
51        flags = network.validator.flags
52
53        k0, k1, k2, k3, k4, k5 = [
54            network.keys.private(secret_exponent=se, is_compressed=True) for se in PRIV_KEYS]
55
56        # Fake a coinbase transaction
57        coinbase_tx = network.tx.coinbase_tx(k0.sec(), 500000000)
58        for k in [k1, k2]:
59            coinbase_tx.txs_out.append(network.tx.TxOut(
60                1000000000, network.script.compile('%s OP_CHECKSIG' % b2h(k.sec()))))
61
62        self.assertEqual('2acbe1006f7168bad538b477f7844e53de3a31ffddfcfc4c6625276dd714155a',
63                         b2h_rev(coinbase_tx.hash()))
64
65        # Make the test transaction
66        txs_in = [
67            network.tx.TxIn(coinbase_tx.hash(), 0),
68            network.tx.TxIn(coinbase_tx.hash(), 1),
69            network.tx.TxIn(coinbase_tx.hash(), 2),
70        ]
71        txs_out = [
72            network.tx.TxOut(900000000, network.contract.for_address(k3.address())),
73            network.tx.TxOut(800000000, network.contract.for_address(k4.address())),
74            network.tx.TxOut(800000000, network.contract.for_address(k5.address())),
75        ]
76        tx = network.tx(1, txs_in, txs_out)
77        tx.set_unspents(coinbase_tx.txs_out)
78
79        self.assertEqual('791b98ef0a3ac87584fe273bc65abd89821569fd7c83538ac0625a8ca85ba587', b2h_rev(tx.hash()))
80
81        sig_type = flags.SIGHASH_SINGLE
82
83        solution_checker = network.tx.SolutionChecker(tx)
84        sig_hash = solution_checker._signature_hash(coinbase_tx.txs_out[0].script, 0, sig_type)
85        self.assertEqual(0xcc52d785a3b4133504d1af9e60cd71ca422609cb41df3a08bbb466b2a98a885e, sig_hash)
86
87        sig = sigmake(k0, sig_hash, sig_type)
88        self.assertTrue(sigcheck(k0, sig_hash, sig[:-1]))
89
90        tx.txs_in[0].script = network.script.compile(b2h(sig))
91        self.assertTrue(tx.is_solution_ok(0))
92
93        sig_hash = solution_checker._signature_hash(coinbase_tx.txs_out[1].script, 1, sig_type)
94        self.assertEqual(0x93bb883d70fccfba9b8aa2028567aca8357937c65af7f6f5ccc6993fd7735fb7, sig_hash)
95
96        sig = sigmake(k1, sig_hash, sig_type)
97        self.assertTrue(sigcheck(k1, sig_hash, sig[:-1]))
98
99        tx.txs_in[1].script = network.script.compile(b2h(sig))
100        self.assertTrue(tx.is_solution_ok(1))
101
102        sig_hash = solution_checker._signature_hash(coinbase_tx.txs_out[2].script, 2, sig_type)
103        self.assertEqual(0x53ef7f67c3541bffcf4e0d06c003c6014e2aa1fb38ff33240b3e1c1f3f8e2a35, sig_hash)
104
105        sig = sigmake(k2, sig_hash, sig_type)
106        self.assertTrue(sigcheck(k2, sig_hash, sig[:-1]))
107
108        tx.txs_in[2].script = network.script.compile(b2h(sig))
109        self.assertTrue(tx.is_solution_ok(2))
110
111        sig_type = flags.SIGHASH_SINGLE | flags.SIGHASH_ANYONECANPAY
112
113        sig_hash = solution_checker._signature_hash(coinbase_tx.txs_out[0].script, 0, sig_type)
114        self.assertEqual(0x2003393d246a7f136692ce7ab819c6eadc54ffea38eb4377ac75d7d461144e75, sig_hash)
115
116        sig = sigmake(k0, sig_hash, sig_type)
117        self.assertTrue(sigcheck(k0, sig_hash, sig[:-1]))
118
119        tx.txs_in[0].script = network.script.compile(b2h(sig))
120        self.assertTrue(tx.is_solution_ok(0))
121
122        sig_hash = solution_checker._signature_hash(coinbase_tx.txs_out[1].script, 1, sig_type)
123        self.assertEqual(0xe3f469ac88e9f35e8eff0bd8ad4ad3bf899c80eb7645947d60860de4a08a35df, sig_hash)
124
125        sig = sigmake(k1, sig_hash, sig_type)
126        self.assertTrue(sigcheck(k1, sig_hash, sig[:-1]))
127
128        tx.txs_in[1].script = network.script.compile(b2h(sig))
129        self.assertTrue(tx.is_solution_ok(1))
130
131        sig_hash = solution_checker._signature_hash(coinbase_tx.txs_out[2].script, 2, sig_type)
132        self.assertEqual(0xbacd7c3ab79cad71807312677c1788ad9565bf3c00ab9a153d206494fb8b7e6a, sig_hash)
133
134        sig = sigmake(k2, sig_hash, sig_type)
135        self.assertTrue(sigcheck(k2, sig_hash, sig[:-1]))
136
137        tx.txs_in[2].script = network.script.compile(b2h(sig))
138        self.assertTrue(tx.is_solution_ok(2))
139
140
141if __name__ == "__main__":
142    unittest.main()
143