def test_request_invalid_once(self, context): node = self.nodes[0] privkey = ECKey() privkey.generate() # Build an invalid proof (no stake) no_stake_hex = node.buildavalancheproof( 42, 2000000000, bytes_to_wif(privkey.get_bytes()), []) no_stake = FromHex(LegacyAvalancheProof(), no_stake_hex) assert_raises_rpc_error(-8, "The proof is invalid: no-stake", node.verifyavalancheproof, no_stake_hex) # Send the proof msg = msg_avaproof() msg.proof = no_stake node.p2ps[0].send_message(msg) # Check we get banned node.p2ps[0].wait_for_disconnect() # Now that the node knows the proof is invalid, it should not be # requested anymore node.p2ps[1].send_message( msg_inv([CInv(t=context.inv_type, h=no_stake.proofid)])) # Give enough time for the node to eventually request the proof node.setmocktime( int(time.time()) + context.constants.getdata_interval + 1) node.p2ps[1].sync_with_ping() assert all(p.getdata_count == 0 for p in node.p2ps[1:])
def test_receive_proof(self): self.log.info("Test a peer is created on proof reception") node = self.nodes[0] _, proof = self.gen_proof(node) peer = node.add_p2p_connection(P2PInterface()) msg = msg_avaproof() msg.proof = proof peer.send_message(msg) wait_until(lambda: proof.proofid in get_proof_ids(node)) self.log.info("Test receiving a proof with missing utxo is orphaned") privkey = ECKey() privkey.generate() orphan_hex = node.buildavalancheproof( 42, 2000000000, privkey.get_pubkey().get_bytes().hex(), [{ 'txid': '0' * 64, 'vout': 0, 'amount': 10e6, 'height': 42, 'iscoinbase': False, 'privatekey': bytes_to_wif(privkey.get_bytes()), }]) orphan = FromHex(AvalancheProof(), orphan_hex) orphan_proofid = "{:064x}".format(orphan.proofid) msg = msg_avaproof() msg.proof = orphan peer.send_message(msg) wait_for_proof(node, orphan_proofid, expect_orphan=True)
def test_ban_invalid_proof(self): node = self.nodes[0] _, bad_proof = self.gen_proof(node) bad_proof.stakes = [] peer = node.add_p2p_connection(P2PInterface()) msg = msg_avaproof() msg.proof = bad_proof with node.assert_debug_log([ 'Misbehaving', 'invalid-avaproof', ]): peer.send_message(msg) peer.wait_for_disconnect()
def run_test(self): # peers that we expect to be protected from eviction protected_peers = set() current_peer = -1 node = self.nodes[0] blocks = node.generatetoaddress( 101, node.get_deterministic_priv_key().address) self.log.info( "Create 4 peers and protect them from eviction by sending us a block" ) for _ in range(4): block_peer = node.add_p2p_connection(SlowP2PDataStore()) current_peer += 1 block_peer.sync_with_ping() best_block = node.getbestblockhash() tip = int(best_block, 16) best_block_time = node.getblock(best_block)['time'] block = create_block(tip, create_coinbase(node.getblockcount() + 1), best_block_time + 1) block.solve() block_peer.send_blocks_and_test([block], node, success=True) protected_peers.add(current_peer) self.log.info( "Create 4 peers and protect them from eviction by sending us a proof" ) privkey = ECKey() privkey.generate() wif_privkey = bytes_to_wif(privkey.get_bytes()) pubkey = privkey.get_pubkey() stakes = create_coinbase_stakes(node, blocks, node.get_deterministic_priv_key().key) for i in range(4): proof_peer = node.add_p2p_connection(SlowP2PDataStore()) current_peer += 1 proof_peer.sync_with_ping() proof = node.buildavalancheproof(42, 2000000000, wif_privkey, [stakes[i]]) avaproof_msg = msg_avaproof() avaproof_msg.proof = FromHex(LegacyAvalancheProof(), proof) proof_peer.send_message(avaproof_msg) protected_peers.add(current_peer) self.log.info( "Create 5 slow-pinging peers, making them eviction candidates") for _ in range(5): node.add_p2p_connection(SlowP2PInterface()) current_peer += 1 self.log.info( "Create 4 peers and protect them from eviction by sending us a tx") for i in range(4): txpeer = node.add_p2p_connection(SlowP2PInterface()) current_peer += 1 txpeer.sync_with_ping() prevtx = node.getblock(node.getblockhash(i + 1), 2)['tx'][0] rawtx = node.createrawtransaction( inputs=[{ 'txid': prevtx['txid'], 'vout': 0 }], outputs=[{ node.get_deterministic_priv_key().address: 50000000 - 1250.00 }], ) sigtx = node.signrawtransactionwithkey( hexstring=rawtx, privkeys=[node.get_deterministic_priv_key().key], prevtxs=[{ 'txid': prevtx['txid'], 'vout': 0, 'amount': prevtx['vout'][0]['value'], 'scriptPubKey': prevtx['vout'][0]['scriptPubKey']['hex'], }], )['hex'] txpeer.send_message(msg_tx(FromHex(CTransaction(), sigtx))) protected_peers.add(current_peer) self.log.info( "Create 8 peers and protect them from eviction by having faster pings" ) for _ in range(8): fastpeer = node.add_p2p_connection(P2PInterface()) current_peer += 1 self.wait_until(lambda: "ping" in fastpeer.last_message, timeout=10) self.log.info( "Create 128 peers and protect them from eviction by sending an avahello message" ) proof = node.buildavalancheproof(42, 2000000000, wif_privkey, [stakes[0]]) proof_obj = FromHex(LegacyAvalancheProof(), proof) delegation = node.delegateavalancheproof( f"{proof_obj.limited_proofid:064x}", bytes_to_wif(privkey.get_bytes()), pubkey.get_bytes().hex(), ) for _ in range(128): avapeer = node.add_p2p_connection(SlowAvaP2PInterface()) current_peer += 1 avapeer.sync_with_ping() avapeer.send_avahello(delegation, privkey) # Make sure by asking the node what the actual min pings are peerinfo = node.getpeerinfo() pings = {} for i in range(len(peerinfo)): pings[i] = peerinfo[i]['minping'] if 'minping' in peerinfo[ i] else 1000000 sorted_pings = sorted(pings.items(), key=lambda x: x[1]) # Usually the 8 fast peers are protected. In rare case of unreliable pings, # one of the slower peers might have a faster min ping though. for i in range(8): protected_peers.add(sorted_pings[i][0]) self.log.info("Create peer that triggers the eviction mechanism") node.add_p2p_connection(SlowP2PInterface()) # One of the non-protected peers must be evicted. We can't be sure which one because # 4 peers are protected via netgroup, which is identical for all peers, # and the eviction mechanism doesn't preserve the order of identical # elements. evicted_peers = [] for i in range(len(node.p2ps)): if not node.p2ps[i].is_connected: evicted_peers.append(i) self.log.info("Test that one peer was evicted") self.log.debug("{} evicted peer: {}".format(len(evicted_peers), set(evicted_peers))) assert_equal(len(evicted_peers), 1) self.log.info("Test that no peer expected to be protected was evicted") self.log.debug("{} protected peers: {}".format(len(protected_peers), protected_peers)) assert evicted_peers[0] not in protected_peers