def handle_ADDR(self, addr): try: nodes = utils.read_message(addr)['nodes'] utils.logg(" [<] Recieved addr list from peer %s" % self.remote_nodeid) #for node in filter(lambda n: nodes[n][1] == "SEND", nodes): for node in nodes: utils.logg(" [*] %s %s" % (node[0], node[1])) if node[0] == self.nodeid: utils.logg("[!] Not connecting to %s thats me!" % node[0]) return if node[1] != "SPEAKER": utils.logg("[!] Not connecting to %s is %s" % (node[0], node[1])) return if node[0] in self.factory.peers: utils.logg("[!] Not connecting to %s already connected" % node[0]) return _print(" [ ] Trying to connect to peer " + node[0] + " " + node[1]) # TODO: Use [2] and a time limit to not connect to "old" peers host, port = node[0].split(":") point = TCP4ClientEndpoint(reactor, host, int(port)) d = connectProtocol( point, NCProtocol(ncfactory, "SENDHELLO", "SPEAKER")) d.addCallback(gotProtocol) except utils.InvalidSignatureError: _print(" [!] ERROR: Invalid addr sign ", self.remote_ip) self.transport.loseConnection()
def dataReceived(self, data): for line in data.splitlines(): line = line.strip() envelope = utils.read_envelope(line) if self.state in ["GETHELLO", "SENTHELLO"]: # Force first message to be HELLO or crash if envelope['msgtype'] == 'hello': self.handle_HELLO(line) else: utils.logg( f" [!] Ignoring {envelope['msgtype']} in {self.state}" ) else: if envelope['msgtype'] == 'ping': self.handle_PING(line) elif envelope['msgtype'] == 'pong': self.handle_PONG(line) elif envelope['msgtype'] == 'addr': pass elif envelope['msgtype'] == 'sync': self.handle_SYNC(line) elif envelope['msgtype'] == 'givemeblocks': self.handle_SENDBLOCKS(line) elif envelope['msgtype'] == 'getblock': self.handleRECEIVEDBLOCK(line)
def handle_HELLO(self, hello): try: hello = utils.read_message(hello) self.remote_nodeid = hello['nodeid'] self.remote_node_protocol_version = hello["protocol"] if self.remote_nodeid == self.nodeid: utils.logg("[!] Found myself at %s" % self.host_ip) self.transport.loseConnection() else: if self.state == "GETHELLO": my_hello = utils.create_hello(self.nodeid, self.VERSION, self.ProtocolVersion) self.transport.write(my_hello + "\n") self.add_peer() self.state = "READY" self.print_peers() #self.write(utils.create_ping(self.nodeid)) if self.kind == "LISTENER": # The listener pings it's audience utils.logg("[ ] Starting pinger to %s" % self.remote_ip) self.lc_ping.start(PING_INTERVAL, now=False) # Tell new audience about my peers self.send_ADDR() self.lc_sync.start(SYNC_INTERVAL, now=True) except utils.InvalidSignatureError: _print(" [!] ERROR: Invalid hello sign ", self.remote_ip) self.transport.loseConnection()
def verify(self, prev_txs): for vin in self.vin: if not prev_txs[vin.tx_id].ID: # log.error("Previous transaction is not correct") utils.logg("Previous transaction is not correct") tx_copy = self._trimmed_copy() for in_id, vin in enumerate(self.vin): prev_tx = prev_txs[vin.tx_id] tx_copy.vin[in_id].signature = None tx_copy.vin[in_id].public_key = prev_tx.vout[vin.vout].public_key_hash tx_copy.ID = tx_copy.hash() tx_copy.vin[in_id].public_key = None sig = self.vin[in_id].signature # vk = ecdsa.VerifyingKey.from_string( # vin.public_key[2:], curve=ecdsa.SECP256k1) vk = utils.pubkey_to_verifykey(vin.public_key) if not vk.verify(sig, utils.encode(tx_copy.ID)): return False return True
def send_ADDR(self): utils.logg(" [>] Telling to %s about my peers" % self.remote_nodeid) # Shouldn't this be a list and not a dict? peers = self.factory.peers listeners = [(n, peers[n][0], peers[n][1], peers[n][2]) for n in peers] addr = utils.create_addr(self.nodeid, listeners) self.write(addr)
def handle_SENDBLOCKS(self, line): # A peer ask us top send him blocks utils.logg("[>] Got sendblocks message from %s" % self.remote_nodeid) # read peer message data = utils.read_message(line) # extract remote peer besthash from message peer_best = data["besthash"] # be sure that we have peer besthash block if peer_best in ctx.mapBlockIndex: # find the next block hash next_hash = next_block(peer_best) # access the block object of the next hash block = ctx.mapBlockIndex[next_hash] # serialize the block object ret = block.serialize() # build a sent block message containing serialized block message = utils.create_send_block(self.nodeid, ret) # send the block to peer self.write(message) # logg :P utils.logg("block %s send to %s" % (thisHeight + 1, self.remote_nodeid)) else: pass
def send_SYNC(self): # Send a sync message to remote peer utils.logg("[>] Asking %s if we need sync" % self.remote_nodeid) # Build a sync message, contains our best height and our besthash sync = utils.create_sync(self.nodeid, ctx.BestHeight, ctx.bestBlockHash) # send the sync message self.write(sync)
def send_PING(self): # Send a pig message to remote peer utils.logg(" [>] PING to %s %s" % (self.remote_nodeid, self.remote_ip)) # Build a ping message ping = utils.create_ping(self.nodeid) # send the ping message self.write(ping)
def add_peer(self): entry = (self.remote_ip, self.kind, self.remote_node_protocol_version, time()) self.factory.peers[self.remote_nodeid] = entry utils.logg( "[] peer %s at %s with protocol version %d added to peers list" % (self.remote_nodeid, self.remote_ip, self.remote_node_protocol_version))
def handle_PONG(self, pong): # Receive a pong message pong = utils.read_message(pong) # loggig utils.logg("[<] PONG from %s at %s" % (self.remote_nodeid, self.remote_ip)) # hacky addr, kind = self.factory.peers[self.remote_nodeid][:2] self.factory.peers[self.remote_nodeid] = (addr, kind, time())
def GetNextWorkRequired(pindexLast): if pindexLast == None: return 10 # Difficulty will change every 600 seconds or 10 minuntes nTargetTimespan = 600 # We need a new block every 100 seconds nTargetSpacing = 50 # That give us a interval 12 blocks nInterval = nTargetTimespan / nTargetSpacing # Only change once per interval if ((ctx.mapBlockHeight[pindexLast] + 1) % nInterval != 0): return ctx.mapBlockIndex[pindexLast].bits # A dictionary that allow to access a block hash by height heights = dict((v, k) for k, v in ctx.mapBlockHeight.items()) nActualTimespan = int( ctx.mapBlockIndex[pindexLast].timestamp - ctx.mapBlockIndex[heights[ctx.mapBlockHeight[pindexLast] - nInterval + 2]].timestamp) if nActualTimespan < nTargetTimespan / 4: nActualTimespan = nTargetTimespan / 4 if nActualTimespan > nTargetTimespan * 4: nActualTimespan = nTargetTimespan * 4 bnNew = utils.bits2target(ctx.mapBlockIndex[pindexLast].bits) bnNew *= nActualTimespan bnNew /= nTargetTimespan if bnNew > bnProofOfWorkLimit: bnNew = bnProofOfWorkLimit utils.logg("\n\n\nGetNextWorkRequired RETARGET *****\n") utils.logg("nTargetTimespan = %d nActualTimespan = %d\n" % ( nTargetTimespan, nActualTimespan, )) utils.logg("Last %d blocks time average was %d\n" % ( nInterval, nActualTimespan, )) utils.logg("Before: %08x %s\n" % ( ctx.mapBlockIndex[pindexLast].bits, nActualTimespan, )) utils.logg("After: %08x %s\n" % ( utils.GetCompact(int(bnNew)), nActualTimespan, )) return utils.target2bits(bnNew)
def sign(self, priv_key, prev_txs): for vin in self.vin: if prev_txs[vin.tx_id].isCoinBase(): # log.error("Previous transaction is not correct") utils.logg("Previous transaction is not correct") tx_copy = self._trimmed_copy() for in_id, vin in enumerate(tx_copy.vin): prev_tx = prev_txs[vin.tx_id] tx_copy.vin[in_id].signature = None tx_copy.vin[in_id].public_key = prev_tx.vout[vin.vout].public_key_hash tx_copy.ID = tx_copy.hash() tx_copy.vin[in_id].public_key = None sk = ecdsa.SigningKey.from_string( priv_key, curve=ecdsa.SECP256k1) sig = sk.sign(utils.encode(tx_copy.ID)) self.vin[in_id].signature = sig
def __init__(self, from_addr, to_addr, amount, utxo_set): inputs = [] outputs = [] # log('UTXOTx') wallets = ws.Wallets() wallet = wallets.get_wallet(from_addr) pubkey_hash = utils.hash_public_key(wallet.public_key) acc, valid_outputs = utxo_set.find_spendable_outputs( pubkey_hash, amount) if acc < amount: # log.error('Not enough funds') utils.logg('Not enough funds') sys.exit() # Build a list of inputs for tx_id, outs in valid_outputs.items(): for out in outs: ctxin = TXInput() ctxin._tx_id = tx_id ctxin._vout = out ctxin._signature = None ctxin._public_key = wallet.public_key inputs.append(ctxin) # Build a list of outputs outputs.append(TXOutput(amount, to_addr)) if acc > amount: # A change outputs.append(TXOutput(acc-amount, from_addr)) self._tx = Transaction() self._tx.vin = inputs self._tx.vout = outputs self._tx.set_id() self._utxo_set = utxo_set self._sign_utxo(wallet.private_key)
def handle_SYNC(self, line): # Got a reply about a sync message utils.logg("[>] Got reply about sync message from %s" % self.remote_nodeid) # read sync message data = utils.read_message(line) # peer height peerHeight = data["bestheight"] # we have missing blocks, we are behind if peerHeight > ctx.BestHeight: # calculate the diffrence diffrence = peerHeight - ctx.BestHeight # logging utils.logg("We need sync, we are behind %d blocks" % diffrence) # set dialog self.factory.dialog = "Need sync" # build a ask blocks message message = utils.create_ask_blocks(self.nodeid, ctx.bestBlockHash) # send ask block message to peer self.write(message) # peer heigfht == our heigh okkk elif peerHeight == ctx.BestHeight: self.factory.dialog = "Synced" utils.logg("we are synced")
def print_peers(self): if len(self.factory.peers) == 0: utils.logg(" [!] PEERS: No peers connected.") else: utils.logg(" [ ] PEERS:") for peer in self.factory.peers: addr, kind = self.factory.peers[peer][:2] utils.logg(" [*] %s at %s %s " % (peer, addr, kind))
def Miner(txs): address = GenerateNewAddress() # create a coinbase transaction txnew = Transaction() txnew.vin[0].tx_id = '' txnew.vin[ 0].public_key = 'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks' txnew.vout[0].value = 50 * COIN txnew.vout[0].address = address txnew.set_id() # create new block and Add our coinbase tx as first transaction block = Block([txnew]) # Collect the latest transactions into the block if len(txs) > 0: for tx in txs: block._tx_lst.append(tx) utils.logg('Running Miner with %d txs in block' % len(block._tx_lst)) block.timestamp = int(time.time()) block.prev_block_hash = ctx.bestBlockHash block.nonce = 0 block.bits = GetNextWorkRequired(ctx.bestBlockHash) # # target target = utils.bits2target(block.bits) i = 0 while 1: block.nonce = i hash_hex = block.getHash() hash_int = int(hash_hex, 16) if hash_int < target: # proccess this block utils.logg('Miner found a block %s' % hash_hex) if ProccessBlock(block, block.nonce): utils.logg('Miner block accepted:') return True else: utils.logg('Miner block rejected:') return False break i += 1
def AcceptBlock(block): bc = Blockchain() if bc.haveBlock(block.hash): utils.logg('ProccessBlock() Error - Block %s already exists' % block.hash) return False # Check prev block if bc._tip != block.prev_block_hash: utils.logg("Proccess().thisBlock : prev block not found") return False # Check timestamp against prev if bc.getBlock(bc._tip).timestamp >= block.timestamp: utils.logg("Proccess().thisBlock : block's timestamp is too early") return False # Check Proof Of Work if block.bits != GetNextWorkRequired(ctx.bestBlockHash): utils.logg("Proccess().thisBlock : incorrect proof of work") return False if not bc._block_put(block): utils.logg("AcceptBlock() : WriteToDisk failed") return False utxo_set = UTXOSet(bc) utxo_set.reindex() ctx.mapBlockIndex[block.hash] = block ctx.BestHeight = ctx.BestHeight + 1 ctx.mapBlockHeight[block.hash] = ctx.BestHeight ctx.bestBlockHash = block.hash return True
def CheckBlock(self): # Size limits if len(self._tx_lst) == 0 or len(self._tx_lst) > 1000000000: utils.logg("CheckBlock() : size limits failed") return False # First transaction must be coinbase, the rest must not be if len(self._tx_lst) == 0 or not self._tx_lst[0].isCoinBase(): utils.logg("CheckBlock() : first tx is not coinbase") return False for i in range(1, len(self._tx_lst)): if self._tx_lst[i].isCoinBase(): utils.logg("CheckBlock() : more than one coinbase") return False # Check transactions for tx in self._tx_lst: if not tx.CheckTransaction(): utils.logg("CheckBlock() : CheckTransaction failed") return False return True
def ProccessBlock(block, nonce): blockhash = block.getHash() # Check for duplicate bc = Blockchain() if bc.haveBlock(blockhash): utils.logg('ProccessBlock() Error - Block %s already exists' % blockhash) return False if not block.CheckBlock(): utils.logg("ProcessBlock() : CheckBlock FAILED") return True if not ctx.mapBlockIndex[block.prev_block_hash]: utils.logg("ProcessBlock: ORPHAN BLOCK, prev=%s\n" % block.prev_block_hash) if not AcceptBlock(block): utils.logg("ProcessBlock() : AcceptBlock FAILED") return False return True
def CheckTransaction(self): # Basic checks that don't depend on any context if len(self.vin) == 0 or len(self.vout) == 0: utils.logg("Transaction::CheckTransaction() : vin or vout empty") return False # Check for negative values for txout in self.vout: if txout.value < 0: utils.logg("CTransaction::CheckTransaction() : txout.nValue negative") return False if not self.isCoinBase(): for txin in self.vin: if txin.tx_id == 0: utils.logg("CTransaction::CheckTransaction() : prevout is null") return False return True
def handleRECEIVEDBLOCK(self, line): # We rceive a new block utils.logg("Proccesing block from %s" % (ctx.getBestHeight + 1, self.remote_nodeid)) # read block message data = utils.read_message(line) # extract block from message block = data["block"] # check if rceived block* is an instance of the Block object if isinstance(block, Block): # deserialize the blockk pblock = Block([]).deserialize(block) # procces this block if ProccessBlock(block, block.nonce): utils.logg('Miner block accepted:') else: utils.logg('Miner block rejected:') else: pass
def Start(factory): p2p_host = utils.p2p_host p2p_port = utils.p2p_port try: endpoint = TCP4ServerEndpoint(reactor, int(p2p_port), interface=p2p_host) utils.logg(" [ ] LISTEN: at %s:%d" % (p2p_host, (int(p2p_port)))) endpoint.listen(factory) except CannotListenError: utils.logg("[!] Address in use") raise SystemExit # connect to bootstrap addresses utils.logg(" [ ] Trying to connect to bootstrap hosts:") #point = TCP4ClientEndpoint(reactor, p2p_port, int(p2p_port)) #d = connectProtocol(point, NCProtocol(factory, "SENDHELLO", "LISTENER")) #d.addCallback(gotProtocol) reactor.run(installSignalHandlers=0)
if hash_int < target: # proccess this block utils.logg('Miner found a block %s' % hash_hex) if ProccessBlock(block, block.nonce): utils.logg('Miner block accepted:') return True else: utils.logg('Miner block rejected:') return False break i += 1 if loadBlockIndex(): utils.logg('BlockIndex loaded') def Mining(): while 1: f = Miner([]) ############################################################################################ class NCProtocol(Protocol): def __init__(self, factory, state="GETHELLO", kind="LISTENER"): self.factory = factory self.state = state self.VERSION = 0
def startFactory(self): utils.logg("Node started")
def loadBlockIndex(): blockchain = Blockchain() utils.logg('Loading BlockIndex') # # Load block index # for block in reversed(list(blockchain.blocks)): # add to index ctx.mapBlockIndex[block.hash] = block ctx.BestHeight = ctx.BestHeight + 1 ctx.bestBlockHash = block.hash ctx.mapBlockHeight[block.hash] = ctx.BestHeight # # Init with genesis block # if len(ctx.mapBlockIndex) == 0: txnew = Transaction() # Transaction # inputs txnew.vin[0].tx_id = '' txnew.vin[0].vout = -1 txnew.vin[ 0].public_key = 'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks' # output txnew.vout[0].value = 50 * COIN txnew.vout[0].address = '1F6kc25HfrXcn9b4brsNzCiTDWuqsDzAmx' txnew.set_id() # Transaction(id='12c86ae42baf87e9186cd2fabae575e5534ee0bb82a61a8d0d5616d3ec9bf029', # vin=[TXInput(tx_id=b'', vout=-1, signature=None, public_key='The Times 03/Jan/2009 Chancellor on brink of second bailout for banks')], # vout=[TXOutput(address='1F6kc25HfrXcn9b4brsNzCiTDWuqsDzAmx', value=5000000000, public_key_hash='9aa83d479c33c697cd727fc87d753d2d7f2daf19')]) # Block block = Block([txnew]) block.prev_block_hash = '' block.timestamp = 1607965519 block.bits = 0x1e0fffff block.nonce = 1484712 #Block(timestamp=b'1607965519', tx_lst=[Transaction(id='12c86ae42baf87e9186cd2fabae575e5534ee0bb82a61a8d0d5616d3ec9bf029', #vin=[TXInput(tx_id=b'', vout=-1, signature=None, public_key='The Times 03/Jan/2009 Chancellor on brink of second bailout for banks')], #vout=[TXOutput(address='1F6kc25HfrXcn9b4brsNzCiTDWuqsDzAmx', value=5000000000, public_key_hash='9aa83d479c33c697cd727fc87d753d2d7f2daf19')])], #prev_block_hash=b'', hash=None, nonce=1484712, bits=504365055) assert ( block.getHash() == '0000054bd29593ff231e77f7005a9e288e162bbda8cb8962077d57d9be8f87c0') blockchain._block_put(block) utxo_set = UTXOSet(blockchain) utxo_set.reindex() utils.logg('Genesis block added to database') # add to index ctx.mapBlockIndex[block.hash] = block ctx.mapBlockHeight[block.hash] = 1 ctx.BestHeight = ctx.BestHeight + 1 ctx.bestBlockHash = block.hash # Miner ''' target = utils.bits2target(block.bits) i = 0 while 1: block.nonce = i hash_hex = block.getHash() hash_int = int(hash_hex, 16) if hash_int < target: print (hash_hex, block.nonce, block.timestamp) i +=1 ''' return True