1#!/usr/bin/env python3
2# Copyright (c) 2014-2019 Daniel Kraft
3# Distributed under the MIT software license, see the accompanying
4# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6# Test the merge-mining RPC interface:
7# getauxblock, createauxblock, submitauxblock
8
9from test_framework.test_framework import BitcoinTestFramework
10from test_framework.util import (
11  assert_equal,
12  assert_greater_than_or_equal,
13  assert_raises_rpc_error,
14)
15
16from test_framework.auxpow import reverseHex
17from test_framework.auxpow_testing import (
18  computeAuxpow,
19  getCoinbaseAddr,
20  mineAuxpowBlockWithMethods,
21)
22
23from decimal import Decimal
24
25class AuxpowMiningTest (BitcoinTestFramework):
26
27  def set_test_params (self):
28    self.num_nodes = 2
29
30  def add_options (self, parser):
31    parser.add_argument ("--segwit", dest="segwit", default=False,
32                         action="store_true",
33                         help="Test behaviour with SegWit active")
34
35  def run_test (self):
36    # Activate segwit if requested.
37    if self.options.segwit:
38      self.nodes[0].generate (500)
39      self.sync_all ()
40
41    # Test with getauxblock and createauxblock/submitauxblock.
42    self.test_getauxblock ()
43    self.test_create_submit_auxblock ()
44
45  def test_common (self, create, submit):
46    """
47    Common test code that is shared between the tests for getauxblock and the
48    createauxblock / submitauxblock method pair.
49    """
50
51    # Verify data that can be found in another way.
52    auxblock = create ()
53    assert_equal (auxblock['chainid'], 1)
54    assert_equal (auxblock['height'], self.nodes[0].getblockcount () + 1)
55    assert_equal (auxblock['previousblockhash'],
56                  self.nodes[0].getblockhash (auxblock['height'] - 1))
57
58    # Calling again should give the same block.
59    auxblock2 = create ()
60    assert_equal (auxblock2, auxblock)
61
62    # If we receive a new block, the old hash will be replaced.
63    self.sync_all ()
64    self.nodes[1].generate (1)
65    self.sync_all ()
66    auxblock2 = create ()
67    assert auxblock['hash'] != auxblock2['hash']
68    assert_raises_rpc_error (-8, 'block hash unknown', submit,
69                             auxblock['hash'], "x")
70
71    # Invalid format for auxpow.
72    assert_raises_rpc_error (-1, None, submit,
73                             auxblock2['hash'], "x")
74
75    # Invalidate the block again, send a transaction and query for the
76    # auxblock to solve that contains the transaction.
77    self.nodes[0].generate (1)
78    addr = self.nodes[1].getnewaddress ()
79    txid = self.nodes[0].sendtoaddress (addr, 1)
80    self.sync_all ()
81    assert_equal (self.nodes[1].getrawmempool (), [txid])
82    auxblock = create ()
83    target = reverseHex (auxblock['_target'])
84
85    # Cross-check target value with GBT to make explicitly sure that it is
86    # correct (not just implicitly by successfully mining blocks for it
87    # later on).
88    gbt = self.nodes[0].getblocktemplate ({"rules": ["segwit"]})
89    assert_equal (target, gbt['target'].encode ("ascii"))
90
91    # Compute invalid auxpow.
92    apow = computeAuxpow (auxblock['hash'], target, False)
93    res = submit (auxblock['hash'], apow)
94    assert not res
95
96    # Compute and submit valid auxpow.
97    apow = computeAuxpow (auxblock['hash'], target, True)
98    res = submit (auxblock['hash'], apow)
99    assert res
100
101    # Make sure that the block is indeed accepted.
102    self.sync_all ()
103    assert_equal (self.nodes[1].getrawmempool (), [])
104    height = self.nodes[1].getblockcount ()
105    assert_equal (height, auxblock['height'])
106    assert_equal (self.nodes[1].getblockhash (height), auxblock['hash'])
107
108    # Call getblock and verify the auxpow field.
109    data = self.nodes[1].getblock (auxblock['hash'])
110    assert 'auxpow' in data
111    auxJson = data['auxpow']
112    assert_equal (auxJson['chainindex'], 0)
113    assert_equal (auxJson['merklebranch'], [])
114    assert_equal (auxJson['chainmerklebranch'], [])
115    assert_equal (auxJson['parentblock'], apow[-160:])
116
117    # Also previous blocks should have 'auxpow', since all blocks (also
118    # those generated by "generate") are merge-mined.
119    oldHash = self.nodes[1].getblockhash (100)
120    data = self.nodes[1].getblock (oldHash)
121    assert 'auxpow' in data
122
123    # Check that it paid correctly to the first node.
124    t = self.nodes[0].listtransactions ("*", 1)
125    assert_equal (len (t), 1)
126    t = t[0]
127    assert_equal (t['category'], "immature")
128    assert_equal (t['blockhash'], auxblock['hash'])
129    assert t['generated']
130    assert_greater_than_or_equal (t['amount'], Decimal ("1"))
131    assert_equal (t['confirmations'], 1)
132
133    # Verify the coinbase script.  Ensure that it includes the block height
134    # to make the coinbase tx unique.  The expected block height is around
135    # 200, so that the serialisation of the CScriptNum ends in an extra 00.
136    # The vector has length 2, which makes up for 02XX00 as the serialised
137    # height.  Check this.  (With segwit, the height is different, so we skip
138    # this for simplicity.)
139    if not self.options.segwit:
140      blk = self.nodes[1].getblock (auxblock['hash'])
141      tx = self.nodes[1].getrawtransaction (blk['tx'][0], True, blk['hash'])
142      coinbase = tx['vin'][0]['coinbase']
143      assert_equal ("02%02x00" % auxblock['height'], coinbase[0 : 6])
144
145  def test_getauxblock (self):
146    """
147    Test the getauxblock method.
148    """
149
150    create = self.nodes[0].getauxblock
151    submit = self.nodes[0].getauxblock
152    self.test_common (create, submit)
153
154    # Ensure that the payout address is changed from one block to the next.
155    hash1 = mineAuxpowBlockWithMethods (create, submit)
156    hash2 = mineAuxpowBlockWithMethods (create, submit)
157    self.sync_all ()
158    addr1 = getCoinbaseAddr (self.nodes[1], hash1)
159    addr2 = getCoinbaseAddr (self.nodes[1], hash2)
160    assert addr1 != addr2
161    info = self.nodes[0].getaddressinfo (addr1)
162    assert info['ismine']
163    info = self.nodes[0].getaddressinfo (addr2)
164    assert info['ismine']
165
166  def test_create_submit_auxblock (self):
167    """
168    Test the createauxblock / submitauxblock method pair.
169    """
170
171    # Check for errors with wrong parameters.
172    assert_raises_rpc_error (-1, None, self.nodes[0].createauxblock)
173    assert_raises_rpc_error (-5, "Invalid coinbase payout address",
174                             self.nodes[0].createauxblock,
175                             "this_an_invalid_address")
176
177    # Fix a coinbase address and construct methods for it.
178    coinbaseAddr = self.nodes[0].getnewaddress ()
179    def create ():
180      return self.nodes[0].createauxblock (coinbaseAddr)
181    submit = self.nodes[0].submitauxblock
182
183    # Run common tests.
184    self.test_common (create, submit)
185
186    # Ensure that the payout address is the one which we specify
187    hash1 = mineAuxpowBlockWithMethods (create, submit)
188    hash2 = mineAuxpowBlockWithMethods (create, submit)
189    self.sync_all ()
190    addr1 = getCoinbaseAddr (self.nodes[1], hash1)
191    addr2 = getCoinbaseAddr (self.nodes[1], hash2)
192    assert_equal (addr1, coinbaseAddr)
193    assert_equal (addr2, coinbaseAddr)
194
195    # Ensure that different payout addresses will generate different auxblocks
196    auxblock1 = self.nodes[0].createauxblock(self.nodes[0].getnewaddress ())
197    auxblock2 = self.nodes[0].createauxblock(self.nodes[0].getnewaddress ())
198    assert auxblock1['hash'] != auxblock2['hash']
199
200if __name__ == '__main__':
201  AuxpowMiningTest ().main ()
202