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