def process_block(node_from, block): """ :param node_from: :param block: :type block: Block.PyBlock :return: """ # Check for duplicate block_hash = block.get_hash() if block_hash in ctx.dictBlockIndex: print("ProcessBlock() : already have block %d %s" % (ctx.dictBlockIndex[block_hash].height, serialize.hexser_uint256(block_hash))) return False if block_hash in ctx.dictOrphanBlocks: print("ProcessBlock() : already have block (orphan) %s" % serialize.hexser_uint256(block_hash)) return False # Preliminary checks if not block.check_block(): del block print("ProcessBlock() : CheckBlock FAILED") return False # If don't already have its previous block, shunt it off to holding area until we get it if block.hash_prev_block not in ctx.dictBlockIndex: print("ProcessBlock: ORPHAN BLOCK, prev=%s" % serialize.hexser_uint256(block.hash_prev_block)[:14]) ctx.dictOrphanBlocks[block_hash] = block ctx.dictOrphanBlocksByPrev[block_hash] = block # Ask this guy to fill in what we're missing if node_from: node_from.push_message(netbase.COMMAND_GETBLOCKS, Block.PyBlockLocator(ctx.indexBest), (Block.get_orphan_root(block), "uint256")) return True # store to disk if not block.accept_block(): del block print("ProcessBlock() : AcceptBlock FAILED") return False del block # Recursively process any orphan blocks that depended on this one work_queue = list() work_queue.append(block_hash) for hash_prev in iter(work_queue): for block_orphan in ctx.dictOrphanBlocksByPrev[hash_prev]: if block_orphan.accept_block(): work_queue.append(block_orphan.get_hash()) ctx.dictOrphanBlocks.pop(block_orphan.get_hash(), None) del block_orphan ctx.dictOrphanBlocksByPrev.pop(hash_prev, None) print("ProcessBlock: ACCEPTED\n") return True
def genesis_block(): timestamp = "The Times 22/Mar/2017 Chancellor on brink of second bailout for banks" tx_new = Tx.PyTransaction() txin = Tx.PyTxIn() init_nbits = 0x1f00ffff sig = Script.PyScript().append(init_nbits).append_num(4).extend(timestamp) # script << nbits << extra_nonce txin.script_sig = sig txout = Tx.PyTxOut() txout.value = 50 * cfg.COIN # scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; Hash160(pubkey) # txNew.vout[0].scriptPubKey << key.GetPubKey() << OP_CHECKSIG; # txNew.vout[0].scriptPubKey = CScript() << CBigNum("0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704") << OP_CHECKSIG; txout.script_pubkey = Script.PyScript().extend(binascii.unhexlify(cfg.GENESIS_PUBKEY)) \ .append(Script.OpCodeType.OP_CHECKSIG) tx_new.l_in.append(txin) tx_new.l_out.append(txout) block = Block.PyBlock() block.l_tx.append(tx_new) block.hash_prev_block = 0 block.hash_merkle_root = block.build_merkle_tree() block.version = 1 block.time = 1490092537 # block.bits = 0x2000ffff # block.bits = init_nbits # nonce:45164 hash:2e72d19b7558bd6ec869e5601c3613cc7a6b3f24118773d7aaaa3c285493fdfc # block.bits = 0x1e00ffff # nonce:xxx (cast almost 5 min) block.nonce = 0 hash_target = BN.PyBigNum.set_compact(block.bits).get_uint256() target_hex = serialize.hexser_uint256(hash_target) # target_hex = long(hash_target) time1 = timeutil.get_time() print 'start time:', time1 while True: block_hash = cryptoutil.Hash(block.serialize()) print 'nonce:', block.nonce print 'now hash:' print serialize.hexser_uint256(block_hash) # print long(block_hash) print 'target' print target_hex if block_hash <= hash_target: print 'current nonce:' print block.nonce print binascii.hexlify(serialize.ser_uint256(block.get_hash())) print block.get_hash('hex') print long(hash_target) break block.nonce += 1 time2 = timeutil.get_time() print 'end time:', time2 diff = time2 - time1 print("%d:%d" % (diff / 60, diff % 60))
def InsertLine(self, f_new, index, tx_hash, str_sort, *strs): data = hash(tx_hash) hexformat = serialize.hexser_uint256(tx_hash) if f_new: # index = self.m_listCtrl_txs.InsertItem(0, str_sort) index = self.m_listCtrl_txs.InsertStringItem(0, str_sort) else: if index == -1: # find item index = self.m_listCtrl_txs.FindItemData(index, data) while index != -1: if get_item_text(self.m_listCtrl_txs, index, 1) == hexformat: break index = self.m_listCtrl_txs.FindItemData(index, data) if index == -1: print( "MainFrame::InsertLine : Couldn't find item to be updated" ) return # If sort key changed, must delete and reinsert to make it relocate if get_item_text(self.m_listCtrl_txs, index, 0) != str_sort: self.m_listCtrl_txs.DeleteItem(index) # index = self.m_listCtrl_txs.InsertItem(0, str_sort) index = self.m_listCtrl_txs.InsertStringItem(0, str_sort) # self.m_listCtrl_txs.SetItem(index, 1, hexformat) self.m_listCtrl_txs.SetStringItem(index, 1, hexformat) for i in range(2, min(7, len(strs))): # self.m_listCtrl_txs.SetItem(index, i, strs[i - 2]) self.m_listCtrl_txs.SetStringItem(index, i, strs[i - 2]) self.m_listCtrl_txs.SetItemData(index, data)
def test_nbits(): n = PyBigNum.set_compact(0x1f00ffff) print serialize.hexser_uint256(n) n = PyBigNum.set_compact(0x1d00ffff) print serialize.hexser_uint256(n) n = PyBigNum(ctx.proofOfWorkLimit) print serialize.hexser_uint256(n) print serialize.hexser_uint256(PyBigNum.set_compact(n.get_compact())) pass
def send_money(script_pubkey, value, wtx_new, ignoreui=True): """ 发送 value 的coin :param script_pubkey: :param value: :param wtx_new: :type wtx_new: Block.PyWalletTx :return: :rtype bool """ with ctx.mainLock: fee_required = list() fee = create_transaction(script_pubkey, value, wtx_new, fee_required) if fee is None: if value + fee_required[0] > get_balance(): str_error = "Error: This is an oversized transaction that requires a transaction fee of %s " \ % util.format_money(fee) else: str_error = "Error: Transaction creation failed " if not ignoreui: wx.MessageBox(str_error, "Sending...") print ("SendMoney() : %s" % str_error) return False if not commit_transaction_spend(wtx_new): if not ignoreui: wx.MessageBox("Error finalizing transaction", "Sending...") print ("SendMoney() : Error finalizing transaction") return False print ("SendMoney: %s\n" % serialize.hexser_uint256(wtx_new.get_hash())[:6]) # Broadcast if not wtx_new.accept_transaction(): # TODO reject at this point # This must not fail. The transaction has already been signed and recorded. if not ignoreui: wx.MessageBox("Error: Transaction not valid", "Sending...") print ("SendMoney() : Error: Transaction not valid") return False wtx_new.relay_wallet_transaction() if not ignoreui: ui.mainframe_repaint() return True
def get_uint256(self, out_type=None): b = bn2mpi(self) size = len(b) if size < 4: return 0 else: b[4] &= 0x7F # modify num negative n = bytearray(32) # uint256 i = 0 # 0 -> 28 for n, empty for last 4 B for j in range(size - 1, 3, -1): # 32 -> 4 for b if i >= 32: break n[i] = b[j] i += 1 if out_type == "str": return bytes(n) if out_type == "hex": return serialize.hexser_uint256(bytes(n), in_type="str") return serialize.deser_uint256(bytes(n))
def OnPaintListCtrl(self, event): # Update listctrl contents if ctx.listWalletUpdated: if ctx.dictWalletLock._RLock__count == 0: with ctx.dictWalletLock: for tx_hash, f_new in ctx.listWalletUpdated: wtx = ctx.dictWallet.get(tx_hash, None) if wtx is not None: print("listWalletUpdated: %s %s" % (serialize.hexser_uint256(tx_hash)[:6], "new" if f_new else "")) self.InsertTransaction(wtx, f_new) self.m_listCtrl_txs.ScrollList(0, 0xFFFF) del ctx.listWalletUpdated[:] pass # end block pass # end try block # Update status column of visible items only self.RefreshStatus() # Update status bar str_gen = '' if ctx.fGenerateCoins: str_gen = " Generating" if ctx.fGenerateCoins and len(ctx.listNodes) == 0: str_gen = "(not connected)" self.m_statusBar.SetStatusText(unicode(str_gen), 1) str_status = " %d connections %d blocks %d transactions" % \ (len(ctx.listNodes), ctx.bestHeight + 1, self.m_listCtrl_txs.GetItemCount()) self.m_statusBar.SetStatusText(unicode(str_status), 2) # Balance total if ctx.dictWalletLock._RLock__count == 0: with ctx.dictWalletLock: self.m_statictext_balance.SetLabel( util.format_money(txaction.get_balance()) + ' ') # print 'set label' self.m_listCtrl_txs.OnPaint(event) pass # end func
def process_message(node_from, command, recv): """ :param node_from: :type node_from: net.PyNode :param command: :param recv: :type recv: serialize.PyDataStream :return: :rtype bool """ global dictReuseKey print("received: %-12s (%d bytes) " % (command, len(recv))) buf = list() for i in range(min(len(recv), 25)): buf.append("%02x " % (bytearray(recv[i])[0] & 0xff)) print "".join(buf) print("") if ctx.dropMessagesTest > 0 and baseutil.get_rand( ctx.dropMessagesTest) == 0: # 这难吧·· print("dropmessages DROPPING RECV MESSAGE\n") # 应该是模拟随机丢弃消息 return True if command == base.COMMAND_VERSION: # Can only do this once if node_from.nVersion != 0: return False node_from.nVersion = recv.stream_out(out_type="int") # int node_from.services = recv.stream_out(out_type="uint64") # uint64 time = recv.stream_out(out_type="int64") # int64 addr_me = recv.stream_out(cls=net.PyAddress) if node_from.nVersion == 0: return False node_from.send.nVersion = min(node_from.nVersion, cfg.VERSION) node_from.recv.nVersion = min(node_from.nVersion, cfg.VERSION) node_from.f_client = not (node_from.services & cfg.NODE_NETWORK) if node_from.f_client: node_from.send.nType |= serialize.SerType.SER_BLOCKHEADERONLY node_from.recv.nType |= serialize.SerType.SER_BLOCKHEADERONLY timeutil.add_time_data(node_from.addr.ip, time) # Ask the first connected node for block updates global fAskedForBlocks if not fAskedForBlocks and not node_from.f_client: fAskedForBlocks = True node_from.push_message(base.COMMAND_GETBLOCKS, Block.PyBlockLocator(ctx.indexBest), (0, "uint256")) print("version addrMe = %s\n" % str(addr_me)) elif node_from.nVersion == 0: # Must have a version message before anything else return False elif command == base.COMMAND_ADDR: list_addr = recv.stream_out(out_type="list", cls=net.PyAddress) # list<PyAddress> # Store the new addresses addrdb = net.PyAddrDB() for addr in list_addr: if ctx.fShutdown: return True if net.add_address(addrdb, addr): # Put on lists to send to other nodes node_from.set_addr_known.add(addr) with ctx.listNodesLock: if addr not in node_from.set_addr_known: node_from.list_addr_to_send.append(addr) addrdb.close() elif command == base.COMMAND_INV: list_inv = recv.stream_out("list", net.PyInv) txdb = Tx.PyTxDB("r") for inv in list_inv: if ctx.fShutdown: return True node_from.add_inventory_known(inv) f_already_have = already_have(txdb, inv) print(" got inventory: %s %s\n" % (str(inv), "have" if f_already_have else "new")) if not f_already_have: node_from.ask_for(inv) elif inv.msg_type == net.MsgType.MSG_BLOCK and inv.msg_hash in ctx.dictOrphanBlocks: node_from.push_message( base.COMMAND_GETBLOCKS, Block.PyBlockLocator(ctx.indexBest), (Block.get_orphan_root( ctx.dictOrphanBlocks[inv.msg_hash]), "uint256")) txdb.close() elif command == base.COMMAND_GETDATA: list_inv = recv.stream_out("list", net.PyInv) for inv in list_inv: if ctx.fShutdown: return True print("received getdata for: %s\n" % str(inv)) if inv.msg_type == net.MsgType.MSG_BLOCK: # Send block from disk block_index = ctx.dictBlockIndex.get(inv.msg_hash, None) if block_index is not None: ## could optimize this to send header straight from blockindex for client block = Block.PyBlock() block.read_from_disk( block_index, read_txs=( not node_from.f_client)) # index, block_pos=0 node_from.push_message(base.COMMAND_BLOCK, block) elif inv.is_known_type(): # Send stream from relay memory with ctx.dictRelayLock: data = ctx.dictRelay.get(inv, None) # data:PyDataStream if data is not None: node_from.push_message(inv.get_command(), data) elif command == base.COMMAND_GETBLOCKS: locator = recv.stream_out(cls=Block.PyBlockLocator) hash_stop = recv.stream_out(out_type="uint256") # Find the first block the caller has in the main chain index = locator.get_block_index() # Send the rest of the chain if index: index = index.next_index print("getblocks %d to %s\n" % (index.height if index else -1, serialize.hexser_uint256(hash_stop)[:14])) while index: if index.get_block_hash() == hash_stop: print(" getblocks stopping at %d %s" % (index.height, index.get_block_hash(output_type='hex')[:14])) break # Bypass setInventoryKnown in case an inventory message got lost with node_from.inventory_lock: inv = net.PyInv(net.MsgType.MSG_BLOCK, index.get_block_hash()) if inv not in node_from.set_inventory_know2: node_from.set_inventory_know2.add(inv) node_from.set_inventory_know.discard( inv) # not throw exception node_from.list_inventory_to_send.append(inv) index = index.next_index pass # end loop elif command == base.COMMAND_TX: list_work_queue = list() # hash256 list msg = recv.copy() tx = recv.stream_out(cls=Tx.PyTransaction) inv = net.PyInv(net.MsgType.MSG_TX, tx.get_hash()) node_from.add_inventory_known(inv) f_missing_inputs = [False] if tx.accept_transaction(check_inputs=True, missing_inputs=f_missing_inputs): Block.add_to_wallet_if_mine(tx, None) netaction.relay_message(inv, msg) ctx.dictAlreadyAskedFor.pop(inv, None) # delete from dict list_work_queue.append(inv.msg_hash) # Recursively process any orphan transactions that depended on this one for hash_prev in iter(list_work_queue): for msg in ctx.dictOrphanBlocksByPrev[ hash_prev]: # list<PyDataStrem> tx = msg.stream_out(cls=Tx.PyTransaction) inv = net.PyInv(net.MsgType.MSG_TX, tx.get_hash()) if tx.accept_transaction(check_inputs=True): print(" accepted orphan tx %s" % serialize.hexser_uint256(inv.msg_hash)) Block.add_to_wallet_if_mine(tx, None) netaction.relay_message(inv, msg) ctx.dictAlreadyAskedFor.pop(inv, None) # delete from dict list_work_queue.append(inv.msg_hash) pass # end for list_work_queue loop for tx_hash in list_work_queue: txaction.erase_orphan_tx(tx_hash) elif f_missing_inputs[ 0]: # f_missing_inputs will chang in accept_transaction() print("storing orphan tx %s" % serialize.hexser_uint256(inv.msg_hash)[:6]) txaction.add_orphan_tx(msg) elif command == base.COMMAND_REVIEW: msg = recv.copy() review = recv.stream_out(cls=market.PyReview) inv = net.PyInv(net.MsgType.MSG_REVIEW, review.get_hash()) node_from.add_inventory_known(inv) if review.accept_review(): # Relay the original message as-is in case it's a higher version than we know how to parse netaction.relay_message(inv, msg) ctx.dictAlreadyAskedFor.pop(inv, None) # del from dict elif command == base.COMMAND_BLOCK: block = recv.stream_out(cls=Block.PyBlock) ## debug print print("received block: \n%s" % str(block)) inv = net.PyInv(net.MsgType.MSG_BLOCK, block.get_hash()) node_from.add_inventory_known(inv) if blockaction.process_block(node_from, block): ctx.dictAlreadyAskedFor.pop(inv, None) elif command == base.COMMAND_GETADDR: node_from.list_addr_to_send = list() ## need to expand the time range if not enough found since = timeutil.get_adjusted_time( ) - cfg.COMMAND_GETADDR_SPAN_TIME # in the last hour with ctx.dictAddressesLock: for addr in iter(ctx.dictAddresses.itervalues()): if ctx.fShutdown: return if addr.time > since: node_from.list_addr_to_send.append(addr) elif command == base.COMMAND_CHECKORDER: hash_reply = recv.stream_out(out_type="uint256") order = recv.stream_out(cls=Block.PyWalletTx) # todo: we have a chance to check the order here # # Keep giving the same key to the same ip until they use it if node_from.addr.ip is not dictReuseKey: dictReuseKey[node_from.addr.ip] = keyaction.generate_new_key( ) # generate pubkey : str # Send back approval of order and pubkey to use script_pubkey = script.PyScript() script_pubkey.extend(dictReuseKey[node_from.addr.ip]).append( script.OpCodeType.OP_CHECKSIG) node_from.push_message(base.COMMAND_REPLY, (hash_reply, "uint256"), (0, "int"), (script_pubkey.get_str(), "str")) elif command == base.COMMAND_SUBMITORDER: hash_reply = recv.stream_out(out_type="uint256") wtx_new = recv.stream_out(cls=Block.PyWalletTx) # Broadcast if not wtx_new.accept_wallet_transaction(): node_from.push_message(base.COMMAND_REPLY, (hash_reply, "uint256"), (1, "int")) print( "submitorder AcceptWalletTransaction() failed, returning error 1" ) return False wtx_new.time_received_is_tx_time = 1 Block.add_to_wallet(wtx_new) wtx_new.relay_wallet_transaction() dictReuseKey.pop(node_from.addr.ip, None) # Send back confirmation node_from.push_message(base.COMMAND_REPLY, (hash_reply, "uint256"), (0, "int")) elif command == base.COMMAND_REPLY: hash_reply = recv.stream_out(out_type="uint256") # code_reply = recv.stream_out(out_type="int") with node_from.dict_requests_lock: tracker = node_from.dict_requests.get(hash_reply, None) if tracker is not None: tracker.func(tracker.param, recv) # recv contain code_reply(0 or 1) else: # Ignore unknown commands for extensibility print("ProcessMessage(%s) : Ignored unknown message" % command) if len(recv) != 0: print("ProcessMessage(%s) : %d extra bytes" % (command, len(recv))) return True
def bitcoin_miner(t): """ :param t: :type t: ExitedThread :return: """ print "Miner started" # TODO add thread priority THREAD_PRIORITY_LOWEST key = Key.PyKey() key.make_new_key() extra_nonce = 0 while ctx.fGenerateCoins: timeutil.sleep_msec(50) # 50 ms t.check_self_shutdown() if t.exit: break # while len(ctx.listNodes) == 0: # timeutil.sleep_msec(1000) # t.check_self_shutdown() # if t.exit: # return True transactions_updated_last = ctx.transactionsUpdated index_prev = ctx.indexBest bits = Block.get_next_work_required(index_prev) # create coinbase tx tx_new = Tx.PyTransaction() tx_in = Tx.PyTxIn() tx_in.prev_out.set_null() extra_nonce += 1 tx_in.script_sig.append(bits).append(extra_nonce) tx_new.l_in.append(tx_in) tx_out = Tx.PyTxOut() tx_out.script_pubkey.extend(key.get_pubkey()).append( Script.OpCodeType.OP_CHECKSIG) tx_new.l_out.append(tx_out) # create new block block = Block.PyBlock() # Add our coinbase tx as first transaction block.l_tx.append(tx_new) # Collect the latest transactions into the block fees = 0 with ctx.mainLock: with ctx.dictTransactionsLock: txdb = Tx.PyTxDB("r") test_pool = dict() # map<uint256, PyTxIndex> flag_already_added = [False] * len(ctx.dictTransactions) found_something = True block_size = 0 while found_something and block_size < cfg.MAX_SIZE / 2: # block_size < 2MB/2 = 1MB found_something = False n = 0 for tx_hash, tx in ctx.dictTransactions.iteritems(): if flag_already_added[n]: continue if tx.is_coinbase() or not tx.is_final(): continue # Transaction fee requirements, mainly only needed for flood control # Under 10K (about 80 inputs) is free for first 100 transactions # Base rate is 0.01 per KB min_fee = tx.get_min_fee( len(block.l_tx) < 100) # 100 个交易内的打折,之后的不打折 tmp_test_pool = copy.deepcopy( test_pool) # 防止下面执行出错对test_pool产生干扰 ret = tx.connect_inputs(txdb, tmp_test_pool, Tx.PyDiskTxPos(1, 1, 1), 0, False, True, min_fee) if ret is None: continue fees += ret # 累积交易费 test_pool = tmp_test_pool block.l_tx.append(tx) block_size += tx.serialize_size( serialize.SerType.SER_NETWORK) flag_already_added[n] = True found_something = True # 这是为了跳出外层 while n += 1 # end for pass pass # end while txdb.close() pass # end critical_block pass block.bits = bits block.l_tx[0].l_out[0].value = block.get_block_value(fees) print("\n\nRunning Miner with %d transactions in block" % len(block.l_tx)) # Prebuild hash buffer class Unnamed1(serialize.Serializable): class Unnamed2(serialize.Serializable): def __init__(self): self.version = 0 self.hash_prev_block = 0 self.hash_merkle_root = 0 self.time = 0 self.bits = 0 self.nonce = 0 def serialize(self, nType=0, nVersion=cfg.VERSION): s = b'' s += serialize.ser_int(self.version) s += serialize.ser_uint256(self.hash_prev_block) s += serialize.ser_uint256(self.hash_merkle_root) s += serialize.ser_uint(self.time) s += serialize.ser_uint(self.bits) s += serialize.ser_uint(self.nonce) return s def __init__(self): self.block = Unnamed1.Unnamed2() self.padding0 = ['\x00'] * 64 self.hash1 = 0 self.padding1 = ['\x00'] * 64 tmp = Unnamed1() tmp.block.version = block.version tmp.block.hash_prev_block = block.hash_prev_block = ( index_prev.get_block_hash() if index_prev else 0) tmp.block.hash_merkle_root = block.hash_merkle_root = block.build_merkle_tree( ) tmp.block.time = block.time = max( (index_prev.get_median_time_past() + 1 if index_prev else 0), timeutil.get_adjusted_time()) tmp.block.bits = block.bits = bits # difficulty tmp.block.nonce = block.nonce = 1 # 从1 开始计算 # blocks0 = format_hash_blocks(tmp.block.serialize(), tmp.padding0) # blocks1 = format_hash_blocks(serialize.ser_uint256(tmp.hash1), tmp.padding1) # search start = timeutil.get_time() block_hash = 0 hash_target = BN.PyBigNum.set_compact(block.bits).get_uint256() while True: if t.exit: break # use local sha256 block_hash = cryptoutil.Hash(tmp.block.serialize()) if block_hash <= hash_target: block.nonce = tmp.block.nonce assert block_hash == block.get_hash() # debug print print("Miner:") print("proof-of-work found \n hash: %s \ntarget: %s\n" % (serialize.hexser_uint256(block_hash), serialize.hexser_uint256(hash_target))) print str(block) # TODO setthreadpriority THREAD_PRIORITY_NORMAL with ctx.mainLock: # save key kaction.add_key(key) key.make_new_key() # Process this block the same as if we had received it from another node if not blkaction.process_block(None, block): print( "ERROR in CoinMiner, ProcessBlock, block not accepted" ) else: print("miner a block, waiting for relay") # TODO ADD THREAD_PRIORITY_LOWEST timeutil.sleep_msec(500) break # Update nTime every few seconds tmp.block.nonce += 1 if tmp.block.nonce & 0x3FFFF == 0: # checkforshutdown if tmp.block.nonce == 0: break if id(index_prev) != id(ctx.indexBest): break if ctx.transactionsUpdated != transactions_updated_last and ( timeutil.get_time() - start > 60): break if not ctx.fGenerateCoins: break tmp.block.time = block.time = max( (index_prev.get_median_time_past() + 1), timeutil.get_adjusted_time()) pass # end nonce loop pass # end whole loop return True