class IrcHandler(asyncore.dispatcher_with_send, Observable): EVT_CONNECT = Observable.createevent() EVT_DISCONNECT = Observable.createevent() EVT_RECV_LINE = Observable.createevent() def __init__(self, log, sockaddr): Observable.__init__(self) asyncore.dispatcher_with_send.__init__(self) self.sockaddr = sockaddr self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect(sockaddr) self.incommingbuff = "" self.log = log def handle_connect(self): self.fire(self.EVT_CONNECT) def readable(self): return (True) def handle_read(self): self.incommingbuff += self.recv(RECV_SIZE) lines = self.incommingbuff.split("\r\n") completedlines, leftover = lines[:-1], lines[-1] for l in completedlines: self.fire(self.EVT_RECV_LINE, line=l) self.incommingbuff = leftover def handle_error(self): self.log.error(traceback.format_exc()) def handle_close(self): self.close() self.fire(self.EVT_DISCONNECT)
def __init__(self, parent, size=(300, 200)): wx.Dialog.__init__(self, parent, size=size, title="Receive", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) Observable.__init__(self) # Create Controls self.address_label = wx.StaticText(self, -1, "Address:") self.address_textctrl = wx.TextCtrl(self, -1, "", size=(250,-1), style=wx.TE_READONLY|wx.BORDER_SIMPLE) self.address_textctrl.SetBackgroundColour((196,196,196)) self.label_label = wx.StaticText(self, -1, "Label:") self.label_textctrl = wx.TextCtrl(self, -1, "", size=(80,-1)) # Setup Sizers sizer = wx.BoxSizer(wx.VERTICAL) formsizer = wx.FlexGridSizer(2, 2) formsizer.Add(self.address_label, 0, wx.LEFT|wx.ALL, 5) formsizer.Add(self.address_textctrl, 1, wx.LEFT|wx.ALL|wx.EXPAND, 5) formsizer.Add(self.label_label, 0, wx.LEFT|wx.ALL, 5) formsizer.Add(self.label_textctrl, 1, wx.LEFT|wx.ALL|wx.EXPAND, 5) formsizer.AddGrowableCol(1) sizer.Add(formsizer, 1, wx.EXPAND|wx.TOP|wx.BOTTOM, 20) btnsizer = wx.StdDialogButtonSizer() ok_button = wx.Button(self, wx.ID_OK) ok_button.SetDefault() btnsizer.AddButton(ok_button) cancel_button = wx.Button(self, wx.ID_CANCEL) btnsizer.AddButton(cancel_button) btnsizer.Realize() sizer.Add(btnsizer, 0, wx.ALIGN_RIGHT|wx.ALL, 5) self.SetSizer(sizer) # Bind Events ok_button.Bind(wx.EVT_BUTTON, self.on_ok)
def __init__(self, log, sockaddr): Observable.__init__(self) asyncore.dispatcher_with_send.__init__(self) self.sockaddr = sockaddr self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect(sockaddr) self.incommingbuff = "" self.log = log
def __init__(self, parent): Observable.__init__(self) wx.aui.AuiNotebook.__init__(self, parent, -1, wx.DefaultPosition, wx.Size(400, 300)) #page = WalletNoteBookPage(self) #self.AddPage(page, "wallet.dat 1") self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_wallet_close) self.pages = {} # id => WalletNoteBookPage
def __init__(self, sockaddr, sock=None): Observable.__init__(self) asyncore.dispatcher.__init__(self, sock=sock) self.sockaddr = sockaddr self.isoutbound = (sock == None) self.out_buffer = "" if not sock: self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect((sockaddr.ip, sockaddr.port))
def __init__(self, parent): wx.Panel.__init__(self, parent) #, style=wx.SIMPLE_BORDER Observable.__init__(self) # Controls self.balance = BalancePanel(self) self.send_button = wx.Button(self, label="Send") self.receive_button = wx.Button(self, label="Receive") self.keylist = wx.ListCtrl(self, style=wx.LC_REPORT, size=(400,100)) self.keylist.InsertColumn(0, "Public Key") self.keylist.InsertColumn(1, "Private Key") self.keylist.InsertColumn(2, "Address") self.keylist.InsertColumn(3, "Description") self.keylist.SetColumnWidth(2, 250) self.keylist.SetColumnWidth(3, 250) self.show_hide_private_keys_button = wx.Button(self, label="Show Hide Private Keys") self.txhistory_list = wx.ListCtrl(self,style=wx.LC_REPORT, size=(400,100)) self.txhistory_list.InsertColumn(0, "Date") self.txhistory_list.InsertColumn(1, "Address") self.txhistory_list.InsertColumn(2, "Label") self.txhistory_list.InsertColumn(3, "Amount") self.txhistory_list.InsertColumn(4, "Confirmed") self.txhistory_list.SetColumnWidth(0, 120) self.txhistory_list.SetColumnWidth(1, 250) # Sizers self.sizer = wx.BoxSizer(orient=wx.VERTICAL) self.sizer.Add(self.balance, 0, wx.EXPAND) send_receive_sizer = wx.BoxSizer(orient=wx.HORIZONTAL) send_receive_sizer.Add(self.send_button, 0, wx.LEFT) send_receive_sizer.Add(self.receive_button, 0, wx.LEFT) self.sizer.Add(send_receive_sizer, 0, wx.EXPAND) self.sizer.Add(wx.StaticText(self, -1, "Keys: "), 0) self.sizer.Add(self.keylist, 0, wx.EXPAND) self.sizer.Add(self.show_hide_private_keys_button, 0) #self.sizer.Add(self.address_book, 0, wx.EXPAND) self.sizer.Add(wx.StaticText(self, -1, "Transactions: "), 0) self.sizer.Add(self.txhistory_list, 1, wx.EXPAND) self.SetSizer(self.sizer) # Events self.show_hide_private_keys_button.Bind(wx.EVT_BUTTON, self.on_show_hide_private_keys) self.send_button.Bind(wx.EVT_BUTTON, self.on_send) self.receive_button.Bind(wx.EVT_BUTTON, self.on_receive) # ChildViews (could be moved into some View directory service) self.send_view = SendView(self) self.receive_view = ReceiveView(self) self.enter_passphrase_view = EnterPassphraseView(self) # Initialize private data self.keylist_idpool = IdPool() self.keys = {} self.key_itemids = {} self.itemdata_ids = IdPool() self.tx_history_items = {} # id => itemdata_ids
class OrphanBlockPool(Observable): EVT_ADDED_ORPHAN_BLOCK = Observable.createevent() EVT_REMOVED_ORPHAN_BLOCK = Observable.createevent() def __init__(self, log): Observable.__init__(self) self.log = log self.blocks = {} self.blocks_by_prev = { } # { prevhash => [blkhash1, blkhash2, ...], ...} def __contains__(self, hash): return hash in self.blocks def contains_block(self, blkhash): return hash in self.blocks def add_block(self, sender, hash, block): self.blocks[hash] = (sender, hash, block) if block.blockheader.hash_prev not in self.blocks_by_prev: self.blocks_by_prev[block.blockheader.hash_prev] = [] self.blocks_by_prev[block.blockheader.hash_prev].append(hash) self.fire(self.EVT_ADDED_ORPHAN_BLOCK, hash=hash) def get_block(self, blkhash): return self.blocks[blkhash] def contains(self, blkhash): return blkhash in self.blocks def get_orphan_root(self, blkhash): while blkhash in self.blocks: sender, hash, block = self.blocks[blkhash] blkhash = block.blockheader.hash_prev return (sender, hash) def pop_descendant_blocks(self, blkhash): result = [] workqueue = [blkhash] while workqueue: blkhash = workqueue.pop() if blkhash in self.blocks_by_prev: result += [ self.blocks[h] for h in self.blocks_by_prev[blkhash] ] workqueue += self.blocks_by_prev[blkhash] del self.blocks_by_prev[blkhash] for sender, hash, block in result: del self.blocks[hash] self.fire(self.EVT_REMOVED_ORPHAN_BLOCK, hash=hash) return result
def __init__(self, params, log): Observable.__init__(self) asyncore.dispatcher.__init__(self) self.params = params self.log = log self.message_encoder = MessageSerializer(self.params.runmode) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind(('localhost', self.params.port)) self.listen(5) self.log.info("Listening on port :%d " % (self.params.port)) self.peers = {} # addr => handler self.peer_states = {} # handler => state
def __init__(self, node, blockchain, txpool, process_pool, log): Observable.__init__(self) self.node = node self.blockchain = blockchain self.txpool = txpool self.log = log self.items_to_download = deque() self.requested_tx = {} self.orphan_tx = {} self.process_pool = process_pool self.txverifier = TxVerifier(self.blockchain.database.runmode) node.subscribe((VersionExchangeService.EVT_MESSAGE, MSG_INV), self.on_inv) node.subscribe((VersionExchangeService.EVT_MESSAGE, MSG_TX), self.on_tx) self.node.subscribe (Node.EVT_DISCONNECTED, self.on_peer_disconnected)
class WalletNotebook(wx.aui.AuiNotebook, Observable): EVT_CLOSE_WALLET = Observable.createevent() EVT_OPEN_WALLET = Observable.createevent() def __init__(self, parent): Observable.__init__(self) wx.aui.AuiNotebook.__init__(self, parent, -1, wx.DefaultPosition, wx.Size(400, 300)) #page = WalletNoteBookPage(self) #self.AddPage(page, "wallet.dat 1") self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_wallet_close) self.pages = {} # id => WalletNoteBookPage @guithread def add_wallet_view(self, id, label): page = WalletNoteBookPage(self) self.pages[id] = page self.AddPage(page, label) index = self.GetPageIndex(page) self.SetSelection(index) #self.SetPageText(index, label + "%d" % index) #return page.wallet_panel self.fire(self.EVT_OPEN_WALLET, id=id, view=page.wallet_panel) def find_page_index(self, page_id): for id, page in self.pages.iteritems(): if id == page_id: return self.GetPageIndex(page) return None def find_page_id(self, index): for id, page in self.pages.iteritems(): if self.GetPageIndex(page)== index: return id return None @guithread def remove_wallet_view(self, id): index = self.find_page_index(id) self.pages[id].Destroy() del self.pages[id] self.RemovePage(index) def on_wallet_close(self, event): event.Veto() self.fire(self.EVT_CLOSE_WALLET, id=self.find_page_id(event.Int))
def __init__(self, parent, size=(300, 200)): wx.Dialog.__init__(self, parent, size=size, title="Receive", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) Observable.__init__(self) # Create Controls self.address_label = wx.StaticText(self, -1, "Address:") self.address_textctrl = wx.TextCtrl(self, -1, "", size=(250, -1), style=wx.TE_READONLY | wx.BORDER_SIMPLE) self.address_textctrl.SetBackgroundColour((196, 196, 196)) self.label_label = wx.StaticText(self, -1, "Label:") self.label_textctrl = wx.TextCtrl(self, -1, "", size=(80, -1)) # Setup Sizers sizer = wx.BoxSizer(wx.VERTICAL) formsizer = wx.FlexGridSizer(2, 2) formsizer.Add(self.address_label, 0, wx.LEFT | wx.ALL, 5) formsizer.Add(self.address_textctrl, 1, wx.LEFT | wx.ALL | wx.EXPAND, 5) formsizer.Add(self.label_label, 0, wx.LEFT | wx.ALL, 5) formsizer.Add(self.label_textctrl, 1, wx.LEFT | wx.ALL | wx.EXPAND, 5) formsizer.AddGrowableCol(1) sizer.Add(formsizer, 1, wx.EXPAND | wx.TOP | wx.BOTTOM, 20) btnsizer = wx.StdDialogButtonSizer() ok_button = wx.Button(self, wx.ID_OK) ok_button.SetDefault() btnsizer.AddButton(ok_button) cancel_button = wx.Button(self, wx.ID_CANCEL) btnsizer.AddButton(cancel_button) btnsizer.Realize() sizer.Add(btnsizer, 0, wx.ALIGN_RIGHT | wx.ALL, 5) self.SetSizer(sizer) # Bind Events ok_button.Bind(wx.EVT_BUTTON, self.on_ok)
class AccountSet(Observable): EVT_ADDED_ACCOUNT = Observable.createevent() EVT_REMOVED_ACCOUNT = Observable.createevent() def __init__(self): super(AccountSet, self).__init__() self.accounts = set() def add_account(self, account): self.accounts.add(account) self.fire(self.EVT_ADDED_ACCOUNT, account=account) def remove_account(self, account): self.accounts.remove(account) self.fire(self.EVT_REMOVED_ACCOUNT, account=account) def on_new_transaction(self, event): self.fire(self.EVT_NEW_TRANSACTION)
class IrcBootstrapper(Observable): EVT_FOUND_PEER = Observable.createevent() def __init__(self, runmode, log, ircserver=SockAddr("92.243.23.21", 6667)):#irc.lfnet.org super(IrcBootstrapper, self).__init__() self.log = log self.runmode = runmode self.ircserver = ircserver self.running = False self.irc_handler = None def start(self): if (not self.running): self.running = True self.irc_handler = IrcHandler(self.log, self.ircserver) self.irc_handler.subscribe(IrcHandler.EVT_CONNECT, self.on_irc_connected) self.irc_handler.subscribe(IrcHandler.EVT_RECV_LINE, self.on_irc_recv_line) self.irc_handler.subscribe(IrcHandler.EVT_DISCONNECT, self.on_irc_disconnected) def on_irc_connected(self, event): self.log.info("Connected to irc server : %s" % (str(self.ircserver))) channel ={MAIN : '#bitcoin', TESTNET: '#bitcoinTEST', TESTNET3: '#bitcoinTEST3'}[self.runmode] name = "x%u" % random.randint(0, 1000000000) self.irc_handler.send('NICK %s\r\n' % name) self.irc_handler.send('USER %s 8 * %s\r\n' % (name,name)) self.irc_handler.send("JOIN " + channel + "\r\n") self.irc_handler.send("WHO " + channel + "\r\n") def on_irc_disconnected(self, event): self.log.info("Disconnected from irc server : %s" % (str(self.ircserver))) self.running = False self.irc_handler = None def on_irc_recv_line(self, event): tokens = event.line.split(" ") if (tokens[1] == "352"): self.found_nick(tokens[7]) if (tokens[1] == "315"): self.stop() def found_nick(self, nick): if nick[0] == 'u': #routable try: hostaddr = decode_base58check(nick[1:]) ip = ".".join([str(struct.unpack(">B", x)[0]) for x in hostaddr[:4]]) port = struct.unpack(">H", hostaddr[4:])[0] except: self.log.info("Error unpacking nickname (errorneous peer): %s" % (nick)) return self.fire(self.EVT_FOUND_PEER, peeraddress=SockAddr(ip, port)) def stop(self): if (self.running): self.irc_handler.handle_close()
class PeerHandler(asyncore.dispatcher, Observable): EVT_CONNECT = Observable.createevent() EVT_DISCONNECT = Observable.createevent() EVT_ERROR = Observable.createevent() def __init__(self, sockaddr, sock=None): Observable.__init__(self) asyncore.dispatcher.__init__(self, sock=sock) self.sockaddr = sockaddr self.isoutbound = (sock == None) self.out_buffer = "" if not sock: self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect((sockaddr.ip, sockaddr.port)) def handle_connect(self): self.fire(self.EVT_CONNECT) def handle_error(self): type, value, traceback = sys.exc_info() self.fire(self.EVT_ERROR, error=str(value), handler=self, traceback=str(traceback)) def handle_close(self): self.fire(self.EVT_DISCONNECT) self.close() def writable(self): return (not self.connected) or len(self.out_buffer) def handle_write(self): num_sent = asyncore.dispatcher.send(self, self.out_buffer[:512]) self.out_buffer = self.out_buffer[num_sent:] def send(self, data): self.out_buffer = self.out_buffer + data def __str__(self): return str(self.sockaddr)
class AddrPool(Observable): EVT_ADDED_ADDR = Observable.createevent() EVT_ADDED_BANNED = Observable.createevent() def __init__(self): super(AddrPool, self).__init__() self.known_peers = set() self.banned_peers = set() def addpeer(self, sockaddr): if sockaddr not in self.known_peers and \ sockaddr not in self.banned_peers : self.known_peers.add(sockaddr) self.fire(self.EVT_ADDED_ADDR, addr=sockaddr) def log_failure(self, time, sockaddr): if sockaddr in self.known_peers: self.known_peers.remove(sockaddr) def log_success(self, time, sockaddr): pass def misbehaving(self, sockaddr, reason): if sockaddr in self.known_peers: self.known_peers.remove(sockaddr) if sockaddr not in self.banned_peers: self.fire(self.EVT_ADDED_BANNED, sockaddr=sockaddr, reason=reason) self.banned_peers.add(sockaddr) '''Get peers from the addr pool. Exclude peers for "exlude" list (e.g. usually allready connected/connecting peers) ''' def getpeers(self, count, exclude=[]): peers = set( random.sample(self.known_peers, min(count + len(exclude), len(self.known_peers)))) peers -= set(exclude) return random.sample(peers, min(len(peers), count))
class DnsBoostrapper(Observable): HOSTS = [ "bitseed.xf2.org", "dnsseed.bluematt.me", "seed.bitcoin.sipa.be", "dnsseed.bitcoin.dashjr.org" ] EVT_FOUND_PEER = Observable.createevent() def __init__(self, runmode, log): super(DnsBoostrapper, self).__init__() self.log = log self.runmode = runmode nodeparams = NodeParams(runmode=runmode, port=9000, version=60000, enabledservices=SERVICES_NONE, nonce=random.randint(0, 2**64), sub_version_num="/coinpy-boostrap:0.1/") self.node = BasicNode(lambda: 0, nodeparams, self.log) self.node.subscribe(VersionExchangeService.EVT_VERSION_EXCHANGED, self.on_version_exchanged) self.node.subscribe((VersionExchangeService.EVT_MESSAGE, MSG_ADDR), self.on_addr) self.bootstrap_peers = set() self.peers = set() def bootstrap(self): for s in self.HOSTS: try: ip = socket.gethostbyname(s) except: pass addr = SockAddr(ip, BITCOIN_PORT[self.runmode]) if addr not in self.bootstrap_peers: self.bootstrap_peers.add(addr) self.node.connect_peer(addr) def on_addr(self, event): addr_msg = event.message for taddr in addr_msg.timenetaddr_list: addr = SockAddr(taddr.netaddr.ip, taddr.netaddr.port) if addr not in self.peers and addr not in self.bootstrap_peers: self.fire(self.EVT_FOUND_PEER, peeraddress=addr) def on_version_exchanged(self, event): event.handler.send_message(GetaddrMessage())
class Bootstrapper(Observable): EVT_FOUND_PEER = Observable.createevent() def __init__(self, runmode, log): super(Bootstrapper, self).__init__() self.log = log #.getChild("bootstrap") self.runmode = runmode self.ircbootstrapper = IrcBootstrapper(runmode, log=log) self.dnsbootstrapper = DnsBoostrapper(runmode, log=log) self.ircbootstrapper.subscribe(IrcBootstrapper.EVT_FOUND_PEER, self.on_found_peer) self.dnsbootstrapper.subscribe(DnsBoostrapper.EVT_FOUND_PEER, self.on_found_peer) def on_found_peer(self, event): self.fire(self.EVT_FOUND_PEER, peeraddress=event.peeraddress) def bootstrap(self): if self.runmode == MAIN: self.dnsbootstrapper.bootstrap() else: self.ircbootstrapper.start()
class PeerConnection(PeerHandler): EVT_NEW_MESSAGE = Observable.createevent() #TODO: merge with PeerHandler? def __init__(self, sockaddr, msg_serializer, sock, log): PeerHandler.__init__(self, sockaddr, sock) #self.subscribe(self.EVT_CONNECT, self.on_connect) self.msg_serializer = msg_serializer self.incommingbuffer = "" self.log = log def readable(self): return (True) def handle_read(self): self.incommingbuffer += self.recv(8192*10) self._process_incomming_buffer() def clear_incomming_buffers(self): self.incommingbuffer = [] def _process_incomming_buffer(self): cursor = 0 #while cursor < len(self.incommingbuffer): try: msg, cursor = self.msg_serializer.deserialize(self.incommingbuffer, cursor) except MissingDataException: #print traceback.format_exc() #self.log.warning("Read Incomplete") #print "cursor = %d:%s" % (cursor, traceback.format_exc()) return self.incommingbuffer = self.incommingbuffer[cursor:] self.fire(self.EVT_NEW_MESSAGE, handler=self, message=msg) #self.on_message(msg) #call again for the rest of the message reactor.call(self._process_incomming_buffer) def send_message(self, message): self.send(self.msg_serializer.serialize(message))
class CoinpyGUI(Observable): EVT_CMD_CLOSE = Observable.createevent() def __init__(self): super(CoinpyGUI, self).__init__() self.mainwindow = MainWindow(None, wx.ID_ANY, "Coinpy", size=(1000, 650)) self.mainwindow.subscribe(MainWindow.EVT_CMD_CLOSE, self.on_exit) self.messages_view = MessageView(self.mainwindow) def on_exit(self, event): self.fire(self.EVT_CMD_CLOSE) def get_logger(self): return (self.mainwindow.get_logger()) def start(self): self.mainwindow.Show() def mainloop(self): pass def stop(self): self.mainwindow.Destroy()
class Wallet(Observable): ''' Wallet implementes the basic satoshi wallet logic. Database logic is implemented in WalletDatabase Features requiring a blockchain are implemented in WalletAccount. ''' EVT_NEW_TRANSACTION = Observable.createevent() def __init__(self, wallet_database, runmode): super(Wallet, self).__init__() self.wallet_database = wallet_database self.runmode = runmode self.addresses = {} self.crypter = Crypter() self.plain_masterkeys = [] def open(self): self.wallet_database.open() self.load() def create(self, passphrase): self.wallet_database.begin_updates() crypter = Crypter() #first create masterkey master_key = new_masterkey(passphrase) plain_masterkey = decrypt_masterkey(master_key, passphrase) self.wallet_database.add_master_key(master_key) #create transaction pool for i in range(100): k = KEY() k.generate(True) public_key = k.get_pubkey() crypter.set_key(plain_masterkey, doublesha256(public_key)) crypted_secret = crypter.encrypt(k.get_secret()) self.wallet_database.add_crypted_key(public_key, crypted_secret) pool_key = WalletPoolKey(i, 60000, time.time(), public_key) self.wallet_database.add_poolkey(pool_key) self.wallet_database.commit_updates() self.load() def load(self): for public_key, keypair in self.wallet_database.get_keys().iteritems(): self.addresses[BitcoinAddress.from_publickey( public_key, self.runmode)] = (public_key, False) for public_key, secret in self.wallet_database.get_crypted_keys( ).iteritems(): self.addresses[BitcoinAddress.from_publickey( public_key, self.runmode)] = (public_key, True) def begin_updates(self): self.wallet_database.begin_updates() def commit_updates(self): self.wallet_database.commit_updates() def get_receive_key(self): return self.wallet_database.get_receive_key() def allocate_key(self, public_key, label=None, ischange=False): address = BitcoinAddress.from_publickey(public_key, self.runmode) return self.wallet_database.allocate_key(public_key, address, label) def add_transaction(self, hashtx, wallet_tx): self.wallet_database.set_transaction(hashtx, wallet_tx) def get_transaction(self, hashtx): return self.wallet_database.get_transaction(hashtx) def set_transaction(self, hashtx, wallet_tx): self.wallet_database.set_transaction(hashtx, wallet_tx) def del_transaction(self, hashtx): self.wallet_database.del_transaction(hashtx) """ yields ( public_key, is_crypted, address, description ) entries. """ def iterkeys(self): names = self.wallet_database.get_names() for address, (public_key, is_crypted) in self.addresses.iteritems(): description = self.get_address_description(public_key) yield (public_key, is_crypted, address, description) def get_address_description(self, public_key): address = BitcoinAddress.from_publickey(public_key, self.runmode) description = "" if public_key in self.wallet_database.poolkeys_by_public_key: poolkey = self.wallet_database.poolkeys_by_public_key[public_key] description = "Pool (id:%d, time:%s)" % ( poolkey.poolnum, time.strftime("%Y-%m-%d %H:%m:%S", time.gmtime(poolkey.time))) else: if address in self.wallet_database.get_names(): description = "Receive (\"%s\")" % self.wallet_database.get_names( )[address].name else: description = "Change" return description def iter_my_outputs(self): for hash, wallet_tx in self.wallet_database.get_wallet_txs().iteritems( ): for index, txout in enumerate(wallet_tx.merkle_tx.tx.out_list): if not wallet_tx.is_spent(index) and self.is_mine(txout): yield (wallet_tx.merkle_tx.tx, Outpoint(hash, index), txout) #yield ControlledOutput(hash, wallet_tx.merkle_tx.tx, index, txout, self.get_keypair_for_output(txout)) '''' A TxIn is "debit" if the previous output is in the wallet and is mine. If it is, get_debit_txin will return the value spent. Debit transaction are used only for transaction history. The balance is computed using unspent transactions. ''' def get_debit_txin(self, txin): if txin.previous_output.hash not in self.wallet_database.get_wallet_txs( ): return 0 txprev = self.wallet_database.get_wallet_txs()[ txin.previous_output.hash] txout = txprev.merkle_tx.tx.out_list[txin.previous_output.index] if not self.is_mine(txout): return 0 return txout.value def get_debit_tx(self, wallet_tx): return sum( self.get_debit_txin(txin) for txin in wallet_tx.merkle_tx.tx.in_list) def get_credit_txout(self, txout): if self.is_mine(txout): return txout.value return 0 def get_credit_tx(self, wallet_tx): return sum( self.get_credit_txout(txout) for txout in wallet_tx.merkle_tx.tx.out_list) def iter_transaction_history(self): # see wallet.cpp:448 GetAmounts for hash, wallet_tx in self.wallet_database.get_wallet_txs().iteritems( ): debit = self.get_debit_tx(wallet_tx) for txout in wallet_tx.merkle_tx.tx.out_list: address = extract_txout_address(txout, self.runmode) name = "" #print self.get_names() #print encode_base58check(chr(ADDRESSVERSION[self.runmode]) + address) if address and address in self.get_names(): name = self.get_names()[address].name if debit > 0 and self.is_change(txout): pass elif debit > 0: yield (wallet_tx, hash, wallet_tx.time_received, address, name, -txout.value) elif self.is_mine(txout): yield (wallet_tx, hash, wallet_tx.time_received, address, name, txout.value) def get_wallet_txs(self): return self.wallet_database.get_wallet_txs() def get_keypairs(self): return self.wallet_database.keypairs def get_poolkeys(self): return self.wallet_database.get_poolkeys() def get_names(self): return self.wallet_database.get_names() def have_key_for_addresss(self, address): return (address in self.addresses) def is_passphrase_required(self, txout): address = extract_txout_address(txout, self.runmode) _, is_crypted = self.addresses[address] return is_crypted def unlock(self, passphrases): for pphrase in passphrases: for mkey in self.get_master_keys().values(): self.plain_masterkeys.append(decrypt_masterkey(mkey, pphrase)) """ Return a private key for a txout as binary bignum. Requires unlock() if this key in encrypted """ def get_txout_private_key_secret(self, txout): address = extract_txout_address(txout, self.runmode) public_key, is_crypted = self.addresses[address] return self.get_private_key_secret(public_key) def get_private_key_secret(self, public_key): if public_key in self.wallet_database.keys: # private key is not crypted k = KEY() k.set_privkey(self.wallet_database.keys[public_key].private_key) return k.get_secret() crypted_secret = self.wallet_database.get_crypted_keys()[public_key] for key in self.plain_masterkeys: self.crypter.set_key(key, doublesha256(public_key)) secret = self.crypter.decrypt(crypted_secret) k = KEY() is_compressed = len(public_key) == 33 k.set_secret(secret, is_compressed) if k.get_pubkey() == public_key: return secret raise KeyDecryptException( "Can't decrypt private key, wallet not unlocked or incorrect masterkey" ) def lock(self): self.plain_masterkeys = [] def is_mine(self, txout): address = extract_txout_address(txout, self.runmode) if not address: # if unknown script type, return False return False return self.have_key_for_addresss(address) """ Return the wallet's besthash as Uint256() or None if not supported """ def get_besthash_reference(self): locator = self.wallet_database.get_block_locator() if locator: return self.wallet_database.get_block_locator().highest() def is_change(self, txout): # Fix to be implemented in main client, see wallet.cpp:390 # current bad assumption is that any payment to a TX_PUBKEYHASH that # is mine but isn't in the address book is change. address = extract_txout_address(txout, self.runmode) return self.is_mine(txout) and (address not in self.get_names()) def get_master_keys(self): return self.wallet_database.get_master_keys()
def __init__(self, parent, id=-1, title="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE | wx.SUNKEN_BORDER | wx.CLIP_CHILDREN): wx.Frame.__init__(self, parent, id, title, pos, size, style) Observable.__init__(self) # Create Menu mb = wx.MenuBar() # File file_menu = wx.Menu() file_menu.Append(wx.ID_NEW, "New") self.Bind(wx.EVT_MENU, self.on_cmd_new_wallet, id=wx.ID_NEW) file_menu.Append(wx.ID_OPEN, "Open") self.Bind(wx.EVT_MENU, self.on_cmd_open_wallet, id=wx.ID_OPEN) file_menu.Append(wx.ID_EXIT, "Exit") self.Bind(wx.EVT_MENU, self.on_exit_menu, id=wx.ID_EXIT) self.Bind(wx.EVT_CLOSE, self.on_close) # Window ID_MENU_SHOWHIDE_CONNECTIONS = wx.NewId() ID_MENU_SHOWHIDE_LOGS = wx.NewId() ID_MENU_SHOWHIDE_POOLS = wx.NewId() ID_MENU_SHOWHIDE_BLOCKCHAIN_SUMMARY = wx.NewId() window_menu = wx.Menu() self.mi_showhide_connections = window_menu.Append(ID_MENU_SHOWHIDE_CONNECTIONS, "Connections", kind=wx.ITEM_CHECK) self.mi_showhide_connections.Check(True) self.mi_showhide_logs = window_menu.Append(ID_MENU_SHOWHIDE_LOGS, "Logs", kind=wx.ITEM_CHECK) self.mi_showhide_logs.Check(True) self.mi_showhide_pools = window_menu.Append(ID_MENU_SHOWHIDE_POOLS, "Pools", kind=wx.ITEM_CHECK) self.mi_showhide_pools.Check(False) self.mi_showhide_blockchain_summary = window_menu.Append(ID_MENU_SHOWHIDE_BLOCKCHAIN_SUMMARY, "Blockchain", kind=wx.ITEM_CHECK) self.mi_showhide_blockchain_summary.Check(True) mb.Append(file_menu, "File") mb.Append(window_menu, "Window") self.SetMenuBar(mb) self.Bind(wx.EVT_MENU, self.on_showhide_connections, id=ID_MENU_SHOWHIDE_CONNECTIONS) self.Bind(wx.EVT_MENU, self.on_showhide_logs, id=ID_MENU_SHOWHIDE_LOGS) self.Bind(wx.EVT_MENU, self.on_showhide_pools, id=ID_MENU_SHOWHIDE_POOLS) self.Bind(wx.EVT_MENU, self.on_showhide_blockchain_summary, id=ID_MENU_SHOWHIDE_BLOCKCHAIN_SUMMARY) # Create Child Windows self._mgr = wx.aui.AuiManager() self._mgr.SetManagedWindow(self) self.nb_wallet = WalletNotebook(self) self.log_panel = LogPanel(self) self.node_view = NodeView(self) self.pools_view = PoolsPanel(self, size=(250,300)) self.blockchain_summary_view = BlockchainSummaryView(self, size=(10, 10)) self._mgr.AddPane(self.nb_wallet, wx.aui.AuiPaneInfo(). Name("wallet_notebook").Caption("Wallet Notebook").MaximizeButton(True). CenterPane()) self._mgr.AddPane(self.pools_view, wx.aui.AuiPaneInfo(). Name("pools").Layer(2).Caption("Pools").MaximizeButton(True). Right().Hide()) self._mgr.AddPane(self.node_view, wx.aui.AuiPaneInfo(). Name("node").Layer(1).BestSize(wx.Size(300,500)).Caption("Connections").MaximizeButton(True). Right()) self._mgr.AddPane(self.log_panel, wx.aui.AuiPaneInfo(). Name("logs").Caption("Logs").MaximizeButton(True). Bottom()) blockchain_summary_paneinfo = wx.aui.AuiPaneInfo(). \ Name("blockchain_summary").BestSize(wx.Size(300,150)).Caption("Blockchain").Layer(1). \ Right().MaximizeButton(True) blockchain_summary_paneinfo.dock_proportion = 20000 self._mgr.AddPane(self.blockchain_summary_view, blockchain_summary_paneinfo) self.Bind(wx.aui.EVT_AUI_PANE_CLOSE, self.on_close_pane) self._mgr.Update() #Statusbar self.statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP) #MessageBoxes self.messages_view = MessageView(self) self.nb_wallet.subscribe(self.nb_wallet.EVT_CLOSE_WALLET, self.on_cmd_close_wallet)
class MainWindow(wx.Frame, Observable): EVT_CMD_OPEN_WALLET = Observable.createevent() EVT_CMD_NEW_WALLET = Observable.createevent() EVT_CMD_CLOSE_WALLET = Observable.createevent() EVT_CMD_CLOSE = Observable.createevent() def __init__(self, parent, id=-1, title="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE | wx.SUNKEN_BORDER | wx.CLIP_CHILDREN): wx.Frame.__init__(self, parent, id, title, pos, size, style) Observable.__init__(self) # Create Menu mb = wx.MenuBar() # File file_menu = wx.Menu() file_menu.Append(wx.ID_NEW, "New") self.Bind(wx.EVT_MENU, self.on_cmd_new_wallet, id=wx.ID_NEW) file_menu.Append(wx.ID_OPEN, "Open") self.Bind(wx.EVT_MENU, self.on_cmd_open_wallet, id=wx.ID_OPEN) file_menu.Append(wx.ID_EXIT, "Exit") self.Bind(wx.EVT_MENU, self.on_exit_menu, id=wx.ID_EXIT) self.Bind(wx.EVT_CLOSE, self.on_close) # Window ID_MENU_SHOWHIDE_CONNECTIONS = wx.NewId() ID_MENU_SHOWHIDE_LOGS = wx.NewId() ID_MENU_SHOWHIDE_POOLS = wx.NewId() ID_MENU_SHOWHIDE_BLOCKCHAIN_SUMMARY = wx.NewId() window_menu = wx.Menu() self.mi_showhide_connections = window_menu.Append( ID_MENU_SHOWHIDE_CONNECTIONS, "Connections", kind=wx.ITEM_CHECK) self.mi_showhide_connections.Check(True) self.mi_showhide_logs = window_menu.Append(ID_MENU_SHOWHIDE_LOGS, "Logs", kind=wx.ITEM_CHECK) self.mi_showhide_logs.Check(True) self.mi_showhide_pools = window_menu.Append(ID_MENU_SHOWHIDE_POOLS, "Pools", kind=wx.ITEM_CHECK) self.mi_showhide_pools.Check(False) self.mi_showhide_blockchain_summary = window_menu.Append( ID_MENU_SHOWHIDE_BLOCKCHAIN_SUMMARY, "Blockchain", kind=wx.ITEM_CHECK) self.mi_showhide_blockchain_summary.Check(True) mb.Append(file_menu, "File") mb.Append(window_menu, "Window") self.SetMenuBar(mb) self.Bind(wx.EVT_MENU, self.on_showhide_connections, id=ID_MENU_SHOWHIDE_CONNECTIONS) self.Bind(wx.EVT_MENU, self.on_showhide_logs, id=ID_MENU_SHOWHIDE_LOGS) self.Bind(wx.EVT_MENU, self.on_showhide_pools, id=ID_MENU_SHOWHIDE_POOLS) self.Bind(wx.EVT_MENU, self.on_showhide_blockchain_summary, id=ID_MENU_SHOWHIDE_BLOCKCHAIN_SUMMARY) # Create Child Windows self._mgr = wx.aui.AuiManager() self._mgr.SetManagedWindow(self) self.nb_wallet = WalletNotebook(self) self.log_panel = LogPanel(self) self.node_view = NodeView(self) self.pools_view = PoolsPanel(self, size=(250, 300)) self.blockchain_summary_view = BlockchainSummaryView(self, size=(10, 10)) self._mgr.AddPane( self.nb_wallet, wx.aui.AuiPaneInfo().Name("wallet_notebook").Caption( "Wallet Notebook").MaximizeButton(True).CenterPane()) self._mgr.AddPane( self.pools_view, wx.aui.AuiPaneInfo().Name("pools").Layer(2).Caption( "Pools").MaximizeButton(True).Right().Hide()) self._mgr.AddPane( self.node_view, wx.aui.AuiPaneInfo().Name("node").Layer(1).BestSize( wx.Size( 300, 500)).Caption("Connections").MaximizeButton(True).Right()) self._mgr.AddPane( self.log_panel, wx.aui.AuiPaneInfo().Name("logs").Caption("Logs").MaximizeButton( True).Bottom()) blockchain_summary_paneinfo = wx.aui.AuiPaneInfo(). \ Name("blockchain_summary").BestSize(wx.Size(300,150)).Caption("Blockchain").Layer(1). \ Right().MaximizeButton(True) blockchain_summary_paneinfo.dock_proportion = 20000 self._mgr.AddPane(self.blockchain_summary_view, blockchain_summary_paneinfo) self.Bind(wx.aui.EVT_AUI_PANE_CLOSE, self.on_close_pane) self._mgr.Update() #Statusbar self.statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP) #MessageBoxes self.messages_view = MessageView(self) self.nb_wallet.subscribe(self.nb_wallet.EVT_CLOSE_WALLET, self.on_cmd_close_wallet) def add_child_frame(self, childclass, title): child_frame = wx.aui.AuiMDIChildFrame(self, -1, title=title) child_inst = childclass() sizer = wx.BoxSizer() sizer.Add(child_inst, 1, wx.EXPAND) child_frame.SetSizer(sizer) def get_logger(self): logger = logging.getLogger(name="coinpy") logger.setLevel(logging.DEBUG) fmt = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') #Stdout #stdout = logging.StreamHandler(sys.stdout) handler = GuiLogHandler(self.log_panel) handler.setLevel(logging.INFO) #logger.addHandler(stdout) handler.setFormatter(fmt) logger.addHandler(handler) return logger def on_cmd_new_wallet(self, event): dlg = wx.FileDialog(self, message="Select a wallet.dat filename", defaultDir=os.getcwd(), defaultFile="wallet.dat", style=wx.SAVE | wx.CHANGE_DIR) if dlg.ShowModal() == wx.ID_OK: self.fire(self.EVT_CMD_NEW_WALLET, file=dlg.GetPath()) def on_cmd_open_wallet(self, event): dlg = wx.FileDialog(self, message="Select a wallet.dat file", defaultDir=os.getcwd(), defaultFile="", wildcard="wallet.dat (*.dat)|*.dat", style=wx.OPEN | wx.CHANGE_DIR) if dlg.ShowModal() == wx.ID_OK: self.fire(self.EVT_CMD_OPEN_WALLET, file=dlg.GetPath()) def on_cmd_close_wallet(self, event): self.fire(self.EVT_CMD_CLOSE_WALLET, id=event.id) def on_exit_menu(self, event): self.Close() def on_close(self, event): #self.Destroy() self.fire(self.EVT_CMD_CLOSE) def on_close_pane(self, event): pane = event.GetPane() if pane.name == 'node': self.mi_showhide_connections.Check(False) if pane.name == 'logs': self.mi_showhide_logs.Check(False) if pane.name == 'pools': self.mi_showhide_pools.Check(False) if pane.name == 'blockchain_summary': self.mi_showhide_blockchain_summary.Check(False) def showhide_pane(self, pane_name, menuitem): pane = self._mgr.GetPane(pane_name) isvisible = pane.IsShown() if (isvisible): pane.Hide() menuitem.Check(False) else: pane.Show() menuitem.Check(True) self._mgr.Update() def on_showhide_connections(self, event): self.showhide_pane("node", self.mi_showhide_connections) def on_showhide_pools(self, event): self.showhide_pane("pools", self.mi_showhide_pools) def on_showhide_logs(self, event): self.showhide_pane("logs", self.mi_showhide_logs) def on_showhide_blockchain_summary(self, event): self.showhide_pane("blockchain_summary", self.mi_showhide_blockchain_summary)
def __init__(self, log): Observable.__init__(self) self.log = log self.blocks = {} self.blocks_by_prev = {} # { prevhash => [blkhash1, blkhash2, ...], ...}
class WalletAccount(Observable): EVT_BALANCE_CHANGED = Observable.createevent() EVT_PUBLISH_TRANSACTION = Observable.createevent() EVT_NEW_TRANSACTION_ITEM = Observable.createevent() EVT_CONFIRMED_TRANSACTION_ITEM = Observable.createevent() EVT_NEW_ADDRESS_DESCRIPTION = Observable.createevent() def __init__(self, log, name, wallet, blockchain): super(WalletAccount, self).__init__() self.name = name self.wallet = wallet self.blockchain = blockchain self.blockchain.subscribe(blockchain.EVT_NEW_HIGHEST_BLOCK, self.on_new_highest_block) self.log = log self.lastblock_time = 0 self.last_tx_publish = {} self.confirmed_outputs = [] self.unconfirmed_outputs = [] self.confirmed_transactions = {} self.unconfirmed_transactions = {} #Satoshi wallet format doesn't store confirmations so we have #to recompute confirmations every time. self.blockchain_height = self.blockchain.get_height() self.compute_balances() self.coin_selector = CoinSelector() self.schedule_republish_transactions() ''' Set the followings attributes: - status_unknown : True if no BlockLocator() was found in the wallet. - is_blockchain_synched : only valid if not status_unknown, True if the blockchain is height is greater than the wallet height. - confirmed_outputs : - unconfirmed_outputs : - confirmed_transactions : - unconfirmed_transactions : ''' def compute_balances(self): #improvement: could only check if all my transactions are present in the blockchain wallet_besthash = self.wallet.get_besthash_reference() self.status_unknown = not wallet_besthash if not self.status_unknown: self.is_blockchain_synched = self.blockchain.contains_block( wallet_besthash) #fillin confirmed/unconfirmed outputs (allows to compute the balance) self.confirmed_outputs = [] self.unconfirmed_outputs = [] for tx, outpoint, txout in self.wallet.iter_my_outputs(): confirmed = False if (self.blockchain.contains_transaction(outpoint.hash) and self.blockchain.get_transaction_handle( outpoint.hash).get_block().is_mainchain()): height = self.blockchain.get_transaction_handle( outpoint.hash).get_block().get_height() confirmed = self.is_confirmed(tx, height) if confirmed: self.confirmed_outputs.append([tx, txout]) else: self.unconfirmed_outputs.append([tx, txout]) confirmed_transactions = {} unconfirmed_transactions = {} #compute the balance self.unconfirmed_balance = sum( txout.value for tx, txout in self.unconfirmed_outputs) self.confirmed_balance = sum(txout.value for tx, txout in self.confirmed_outputs) self.fire(self.EVT_BALANCE_CHANGED, confirmed=self.confirmed_balance, unconfirmed=self.unconfirmed_balance, height=self.blockchain_height) #fillin confirmed/unconfirmed outputs transactions (for history and confirmations) for wallet_tx, hash, date, address, name, amount in self.wallet.iter_transaction_history( ): confirmed = False if self.blockchain.contains_transaction(hash): height = self.blockchain.get_transaction_handle( hash).get_block().get_height() confirmed = self.is_confirmed(wallet_tx.merkle_tx.tx, height) if (confirmed): confirmed_transactions[hash] = [ wallet_tx, hash, date, address, name, amount ] else: unconfirmed_transactions[hash] = [ wallet_tx, hash, date, address, name, amount ] self.last_tx_publish[hash] = 0 #send newly confirmed transaction events for txhash in self.unconfirmed_transactions: if txhash in confirmed_transactions: self.fire(self.EVT_CONFIRMED_TRANSACTION_ITEM, item=(txhash, )) self.confirmed_transactions = confirmed_transactions self.unconfirmed_transactions = unconfirmed_transactions def iter_my_outputs(self): return self.wallet.iter_my_outputs() def iter_transaction_history(self): for wallet_tx, hash, date, address, name, amount in self.confirmed_transactions.values( ): yield (wallet_tx, hash, date, address, name, amount, True) for wallet_tx, hash, date, address, name, amount in self.unconfirmed_transactions.values( ): yield (wallet_tx, hash, date, address, name, amount, False) '''Return the confirmed account balance. Return the received coins seen in mainchain at a depth of minimum 6 and the minted coins in mainchain at a depth of COINBASE_MATURITY. ''' def get_confirmed_balance(self): return self.confirmed_balance '''Return the unconfirmed account balance. Return the coins not seen in mainchain, the coins seen in mainchain with a depth < 6, and the coins minted whith a depth < COINBASE_MATURITY. ''' def get_unconfirmed_balance(self): return self.unconfirmed_balance def get_blockchain_height(self): return self.blockchain_height def is_confirmed(self, tx, height): if tx.iscoinbase(): return (self.blockchain_height > height + COINBASE_MATURITY) return (self.blockchain_height > height + CONFIRMATIONS) def on_new_highest_block(self, event): self.blockchain_height = event.height self.lastblock_time = time.time() self.compute_balances() def get_besthash(self): return self.wallet.get_besthash_reference() """ Do this infrequently and randomly to avoid giving away that these are our transactions. """ def republish_transactions(self): tnow = time.time() for wallet_tx, txhash, date, address, name, amount in self.unconfirmed_transactions.values( ): # Only if at least one block was received more than 5min after last publishing. if (self.lastblock_time > self.last_tx_publish[txhash] + 5 * 60): self.fire(self.EVT_PUBLISH_TRANSACTION, txhash=txhash, tx=wallet_tx.merkle_tx.tx) self.last_tx_publish[hash] = tnow self.schedule_republish_transactions() """""" def schedule_republish_transactions(self): seconds = random.randint(5 * 60, 30 * 60) reactor.call_later(seconds, self.republish_transactions) def get_receive_address(self): public_key = self.wallet.get_receive_key() return BitcoinAddress.from_publickey(public_key, self.wallet.runmode) def set_receive_label(self, address_base58, label): address = BitcoinAddress.from_base58addr(address_base58) self.wallet.begin_updates() public_key, is_crypted = self.wallet.addresses[ address] #decode_base58check(address)[1:] self.wallet.allocate_key(public_key, label) self.wallet.commit_updates() new_description = self.wallet.get_address_description(public_key) self.fire(self.EVT_NEW_ADDRESS_DESCRIPTION, public_key=public_key, description=new_description) """ amount: value in COIN. """ def create_transaction(self, amount, address, fee): outputs = [(outpoint, txout) for (tx, outpoint, txout) in self.iter_my_outputs()] selected_outputs = self.coin_selector.select_coins( outputs, (amount + fee)) change_public_key = self.wallet.get_receive_key() change_address = BitcoinAddress.from_publickey(change_public_key, self.wallet.runmode) tx = create_pubkeyhash_transaction( selected_outputs, address.get_hash160( ), #decode_base58check(address)[1:], #remove ADDRESSVERSION[runmode] byte change_address.get_hash160( ), #decode_base58check(change_address)[1:], #remove ADDRESSVERSION[runmode] byte amount, fee) return (PlannedTransaction(selected_outputs, amount, address, change_public_key, change_address, fee, tx)) def is_passphrase_required(self, planned_transaction): for outpoint, txout in planned_transaction.selected_outputs: if self.wallet.is_passphrase_required(txout): return True return False def send_transaction(self, planned_tx, passphrases): try: self.wallet.unlock(passphrases) privkey_list = [] for outpoint, txout in planned_tx.selected_outputs: privkey_list.append( self.wallet.get_txout_private_key_secret(txout)) finally: self.wallet.lock() sign_transaction( planned_tx.tx, [txout for outpoint, txout in planned_tx.selected_outputs], privkey_list) txhash = hash_tx(planned_tx.tx) self.log.info( "Sending %f to %s (fee:%f), change address: %s, hash:%s" % (planned_tx.amount, str(planned_tx.address), planned_tx.fee, str(planned_tx.change_address), str(txhash))) #Initially, create an empty MerkleTx (the tx is not yet in a block) merkle_tx = MerkleTx(planned_tx.tx, Uint256.zero(), [], 4294967295) self.wallet.begin_updates() self.wallet.allocate_key(planned_tx.change_public_key, ischange=True) #Set the spend flags for the input transactions for outpoint, txout in planned_tx.selected_outputs: input_wallet_tx = self.wallet.get_transaction(outpoint.hash) input_wallet_tx.set_spent(outpoint.index) self.wallet.set_transaction(outpoint.hash, input_wallet_tx) #Add the wallet_tx (contains supporting transations) txtime = int(time.time()) wallet_tx = create_wallet_tx(self.blockchain, merkle_tx, txtime) self.wallet.add_transaction(txhash, wallet_tx) self.wallet.commit_updates() self.fire(self.EVT_NEW_TRANSACTION_ITEM, item=(planned_tx.tx, txhash, txtime, planned_tx.address, "", -planned_tx.amount, False)) self.compute_balances() # we could only compute delta here self.fire(self.EVT_PUBLISH_TRANSACTION, txhash=txhash, tx=planned_tx.tx) self.last_tx_publish[txhash] = txtime #update description of change address new_description = self.wallet.get_address_description( planned_tx.change_public_key) self.fire(self.EVT_NEW_ADDRESS_DESCRIPTION, public_key=planned_tx.change_public_key, description=new_description)
def __init__(self, parent, id=-1, title="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE | wx.SUNKEN_BORDER | wx.CLIP_CHILDREN): wx.Frame.__init__(self, parent, id, title, pos, size, style) Observable.__init__(self) # Create Menu mb = wx.MenuBar() # File file_menu = wx.Menu() file_menu.Append(wx.ID_NEW, "New") self.Bind(wx.EVT_MENU, self.on_cmd_new_wallet, id=wx.ID_NEW) file_menu.Append(wx.ID_OPEN, "Open") self.Bind(wx.EVT_MENU, self.on_cmd_open_wallet, id=wx.ID_OPEN) file_menu.Append(wx.ID_EXIT, "Exit") self.Bind(wx.EVT_MENU, self.on_exit_menu, id=wx.ID_EXIT) self.Bind(wx.EVT_CLOSE, self.on_close) # Window ID_MENU_SHOWHIDE_CONNECTIONS = wx.NewId() ID_MENU_SHOWHIDE_LOGS = wx.NewId() ID_MENU_SHOWHIDE_POOLS = wx.NewId() ID_MENU_SHOWHIDE_BLOCKCHAIN_SUMMARY = wx.NewId() window_menu = wx.Menu() self.mi_showhide_connections = window_menu.Append( ID_MENU_SHOWHIDE_CONNECTIONS, "Connections", kind=wx.ITEM_CHECK) self.mi_showhide_connections.Check(True) self.mi_showhide_logs = window_menu.Append(ID_MENU_SHOWHIDE_LOGS, "Logs", kind=wx.ITEM_CHECK) self.mi_showhide_logs.Check(True) self.mi_showhide_pools = window_menu.Append(ID_MENU_SHOWHIDE_POOLS, "Pools", kind=wx.ITEM_CHECK) self.mi_showhide_pools.Check(False) self.mi_showhide_blockchain_summary = window_menu.Append( ID_MENU_SHOWHIDE_BLOCKCHAIN_SUMMARY, "Blockchain", kind=wx.ITEM_CHECK) self.mi_showhide_blockchain_summary.Check(True) mb.Append(file_menu, "File") mb.Append(window_menu, "Window") self.SetMenuBar(mb) self.Bind(wx.EVT_MENU, self.on_showhide_connections, id=ID_MENU_SHOWHIDE_CONNECTIONS) self.Bind(wx.EVT_MENU, self.on_showhide_logs, id=ID_MENU_SHOWHIDE_LOGS) self.Bind(wx.EVT_MENU, self.on_showhide_pools, id=ID_MENU_SHOWHIDE_POOLS) self.Bind(wx.EVT_MENU, self.on_showhide_blockchain_summary, id=ID_MENU_SHOWHIDE_BLOCKCHAIN_SUMMARY) # Create Child Windows self._mgr = wx.aui.AuiManager() self._mgr.SetManagedWindow(self) self.nb_wallet = WalletNotebook(self) self.log_panel = LogPanel(self) self.node_view = NodeView(self) self.pools_view = PoolsPanel(self, size=(250, 300)) self.blockchain_summary_view = BlockchainSummaryView(self, size=(10, 10)) self._mgr.AddPane( self.nb_wallet, wx.aui.AuiPaneInfo().Name("wallet_notebook").Caption( "Wallet Notebook").MaximizeButton(True).CenterPane()) self._mgr.AddPane( self.pools_view, wx.aui.AuiPaneInfo().Name("pools").Layer(2).Caption( "Pools").MaximizeButton(True).Right().Hide()) self._mgr.AddPane( self.node_view, wx.aui.AuiPaneInfo().Name("node").Layer(1).BestSize( wx.Size( 300, 500)).Caption("Connections").MaximizeButton(True).Right()) self._mgr.AddPane( self.log_panel, wx.aui.AuiPaneInfo().Name("logs").Caption("Logs").MaximizeButton( True).Bottom()) blockchain_summary_paneinfo = wx.aui.AuiPaneInfo(). \ Name("blockchain_summary").BestSize(wx.Size(300,150)).Caption("Blockchain").Layer(1). \ Right().MaximizeButton(True) blockchain_summary_paneinfo.dock_proportion = 20000 self._mgr.AddPane(self.blockchain_summary_view, blockchain_summary_paneinfo) self.Bind(wx.aui.EVT_AUI_PANE_CLOSE, self.on_close_pane) self._mgr.Update() #Statusbar self.statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP) #MessageBoxes self.messages_view = MessageView(self) self.nb_wallet.subscribe(self.nb_wallet.EVT_CLOSE_WALLET, self.on_cmd_close_wallet)
class WalletPanel(wx.Panel, Observable): EVT_SEND = Observable.createevent() EVT_RECEIVE = Observable.createevent() EVT_SHOWHIDE_PRIVATE_KEYS = Observable.createevent() def __init__(self, parent): wx.Panel.__init__(self, parent) #, style=wx.SIMPLE_BORDER Observable.__init__(self) # Controls self.balance = BalancePanel(self) self.send_button = wx.Button(self, label="Send") self.receive_button = wx.Button(self, label="Receive") self.keylist = wx.ListCtrl(self, style=wx.LC_REPORT, size=(400,100)) self.keylist.InsertColumn(0, "Public Key") self.keylist.InsertColumn(1, "Private Key") self.keylist.InsertColumn(2, "Address") self.keylist.InsertColumn(3, "Description") self.keylist.SetColumnWidth(2, 250) self.keylist.SetColumnWidth(3, 250) self.show_hide_private_keys_button = wx.Button(self, label="Show Hide Private Keys") self.txhistory_list = wx.ListCtrl(self,style=wx.LC_REPORT, size=(400,100)) self.txhistory_list.InsertColumn(0, "Date") self.txhistory_list.InsertColumn(1, "Address") self.txhistory_list.InsertColumn(2, "Label") self.txhistory_list.InsertColumn(3, "Amount") self.txhistory_list.InsertColumn(4, "Confirmed") self.txhistory_list.SetColumnWidth(0, 120) self.txhistory_list.SetColumnWidth(1, 250) # Sizers self.sizer = wx.BoxSizer(orient=wx.VERTICAL) self.sizer.Add(self.balance, 0, wx.EXPAND) send_receive_sizer = wx.BoxSizer(orient=wx.HORIZONTAL) send_receive_sizer.Add(self.send_button, 0, wx.LEFT) send_receive_sizer.Add(self.receive_button, 0, wx.LEFT) self.sizer.Add(send_receive_sizer, 0, wx.EXPAND) self.sizer.Add(wx.StaticText(self, -1, "Keys: "), 0) self.sizer.Add(self.keylist, 0, wx.EXPAND) self.sizer.Add(self.show_hide_private_keys_button, 0) #self.sizer.Add(self.address_book, 0, wx.EXPAND) self.sizer.Add(wx.StaticText(self, -1, "Transactions: "), 0) self.sizer.Add(self.txhistory_list, 1, wx.EXPAND) self.SetSizer(self.sizer) # Events self.show_hide_private_keys_button.Bind(wx.EVT_BUTTON, self.on_show_hide_private_keys) self.send_button.Bind(wx.EVT_BUTTON, self.on_send) self.receive_button.Bind(wx.EVT_BUTTON, self.on_receive) # ChildViews (could be moved into some View directory service) self.send_view = SendView(self) self.receive_view = ReceiveView(self) self.enter_passphrase_view = EnterPassphraseView(self) # Initialize private data self.keylist_idpool = IdPool() self.keys = {} self.key_itemids = {} self.itemdata_ids = IdPool() self.tx_history_items = {} # id => itemdata_ids def create_enter_passphrase_view(self): return EnterPassphraseView(self) def on_send(self, event): self.fire(self.EVT_SEND) def on_receive(self, event): self.fire(self.EVT_RECEIVE) def add_key(self, id, public_key, private_key, address, description): itemid = self.keylist_idpool.get_id() self.keys[itemid] = (public_key, private_key, address, description) self.key_itemids[id] = itemid index = self.keylist.InsertStringItem(self.keylist.GetItemCount(), hexstr(public_key)) self.keylist.SetItemData(index, itemid) self.keylist.SetStringItem(index, 1, private_key) self.keylist.SetStringItem(index, 2, address) self.keylist.SetStringItem(index, 3, description) def set_key_description(self, id, description): itemid = self.key_itemids[id] index = self.keylist.FindItemData(-1, itemid) self.keylist.SetStringItem(index, 3, description) def set_key_private_key(self, id, private_key): itemid = self.key_itemids[id] index = self.keylist.FindItemData(-1, itemid) self.keylist.SetStringItem(index, 1, private_key) def select_key(self, id): itemid = self.key_itemids[id] index = self.keylist.FindItemData(-1, itemid) #Deselect all idx = self.keylist.GetFirstSelected() while idx != -1: self.keylist.Select(index, False) idx = self.keylist.GetNextSelected(idx) #Select and Focus self.keylist.Select(index) self.keylist.Focus(index) def add_transaction_history_item(self, id, txtime, address, label, amount, confirmed): itemdata = self.itemdata_ids.get_id() self.tx_history_items[id] = itemdata timestr = time.strftime("%Y-%m-%d %H:%m:%S", time.localtime(txtime)) index = self.txhistory_list.InsertStringItem(self.keylist.GetItemCount(),timestr) self.txhistory_list.SetStringItem(index, 1, address) self.txhistory_list.SetStringItem(index, 2, label) self.txhistory_list.SetStringItem(index, 3, str(amount * 1.0 / COIN )) self.txhistory_list.SetStringItem(index, 4, confirmed and "Yes" or "No") self.txhistory_list.SetItemData(index, itemdata) #if (amount < 0): # self.txhistory_list.SetItemBackgroundColour(index, (255, 200, 200)) #else: # self.txhistory_list.SetItemBackgroundColour(index, (200, 255, 200)) if confirmed: self.txhistory_list.SetItemBackgroundColour(index, (255, 255, 255)) else: self.txhistory_list.SetItemBackgroundColour(index, (255, 230, 148)) def set_confirmed(self, id, confirmed): itemdata = self.tx_history_items[id] index = self.txhistory_list.FindItemData(-1, itemdata) self.txhistory_list.SetStringItem(index, 4, confirmed and "Yes" or "No") if confirmed: self.txhistory_list.SetItemBackgroundColour(index, (255, 255, 255)) else: self.txhistory_list.SetItemBackgroundColour(index, (255, 230, 230)) def on_show_hide_private_keys(self, event): self.fire(self.EVT_SHOWHIDE_PRIVATE_KEYS)
class Node(asyncore.dispatcher, Observable): """Node: connect, disconnect peers, send and receive bitcoin messages. No logic included. """ EVT_NEED_PEERS = Observable.createevent() EVT_BASIC_MESSAGE = Observable.createevent() EVT_CONNECTING = Observable.createevent() EVT_CONNECTED = Observable.createevent() EVT_PEER_ERROR = Observable.createevent() EVT_DISCONNECTED = Observable.createevent() PEER_CONNECTING, PEER_CONNECTED, PEER_DISCONNECTING = PEER_STATES = range( 3) def __init__(self, params, log): Observable.__init__(self) asyncore.dispatcher.__init__(self) self.params = params self.log = log self.message_encoder = MessageSerializer(self.params.runmode) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind(('localhost', self.params.port)) self.listen(5) self.log.info("Listening on port :%d " % (self.params.port)) self.peers = {} # addr => handler self.peer_states = {} # handler => state def disconnect_peer(self, sockaddr): handler = self.peers[sockaddr] self.peer_states[handler] = Node.PEER_DISCONNECTING handler.clear_incomming_buffers() handler.handle_close() def connected_peer_count(self): return len(self.peers_with_state(Node.PEER_CONNECTED)) def connecting_peer_count(self): return len(self.peers_with_state(Node.PEER_CONNECTING)) def peers_with_state(self, state): result = [] for peer, s in self.peer_states.iteritems(): if s == state: result.append(peer) return result def handle_error(self): self.log.error(traceback.format_exc()) def connect_peer(self, sockaddr): if (sockaddr in self.peers): raise Exception("allready connecting/connected: %s" % (str(sockaddr))) self.log.info("Connecting: %s" % (str(sockaddr))) try: handler = PeerConnection(sockaddr, self.message_encoder, None, log=self.log) except: self.log.warning("Warning error connection to " + str(sockaddr)) else: handler.subscribe(handler.EVT_CONNECT, self.on_peer_connected) handler.subscribe(handler.EVT_DISCONNECT, self.on_peer_disconnected) self.peers[sockaddr] = handler self.peer_states[handler] = Node.PEER_CONNECTING self.fire(self.EVT_CONNECTING, handler=handler) def on_peer_connected(self, event): self.peer_states[event.source] = Node.PEER_CONNECTED self.log.info("Peer Connected(%s) (peers:%d)" % (event.source.sockaddr, self.connected_peer_count())) self.fire(self.EVT_CONNECTED, handler=event.source, outbound=True) event.source.subscribe(PeerConnection.EVT_NEW_MESSAGE, self.__on_message) event.source.subscribe(PeerConnection.EVT_ERROR, self.on_error) def on_peer_disconnected(self, event): if self.peer_states[event.source] == Node.PEER_CONNECTED: self.log.info( "Peer Disconnected(%s) (peers:%d)" % (str(event.source.sockaddr), self.connected_peer_count())) if self.peer_states[event.source] == Node.PEER_CONNECTING: self.log.info("Connection Failed(%s)" % (str(event.source.sockaddr))) self.fire(self.EVT_DISCONNECTED, handler=event.source) del self.peers[event.source.sockaddr] del self.peer_states[event.source] def handle_accept(self): sock, (remote_ip, remote_port) = self.accept() remote_addr = SockAddr(remote_ip, remote_port) handler = PeerConnection(remote_addr, self.message_encoder, sock, self.log) handler.subscribe(handler.EVT_DISCONNECT, self.on_peer_disconnected) handler.subscribe(PeerConnection.EVT_NEW_MESSAGE, self.__on_message) handler.subscribe(PeerConnection.EVT_ERROR, self.on_error) self.peers[remote_addr] = handler self.peer_states[handler] = Node.PEER_CONNECTED self.log.info("Peer Accepted(%s) (peers:%d)" % (remote_addr, self.connected_peer_count())) self.fire(self.EVT_CONNECTED, handler=handler, outbound=False) def send_message(self, peer, message): peer.send_message(message) def __on_message(self, event): handler, message = event.handler, event.message self.fire(self.EVT_BASIC_MESSAGE, message=message, handler=handler) def on_error(self, event): handler, error = event.handler, event.error self.fire(self.EVT_PEER_ERROR, error=error, handler=handler)
def __init__(self, log): Observable.__init__(self) self.log = log self.blocks = {} self.blocks_by_prev = { } # { prevhash => [blkhash1, blkhash2, ...], ...}
class VersionExchangeService(): EVT_VERSION_EXCHANGED = Observable.createevent() EVT_MESSAGE = Observable.createevent() def __init__(self, node, get_blockchain_height, params, log): self.node = node self.params = params self.log = log self.get_blockchain_height = get_blockchain_height self.version_statuses = {} self.version_exchanged_nodes = set() #todo: remove usage of node.params and node.addr_me self.addr_me = Netaddr(self.params.enabledservices, "192.168.1.1", 78) self.node.subscribe(node.EVT_BASIC_MESSAGE, self.on_message) self.node.subscribe(node.EVT_CONNECTED, self.__on_connected) def __on_connected(self, event): self.version_statuses[event.handler] = VersionStatus() if (event.handler.isoutbound): self.send_version(event.handler) def __on_version(self, event): status = self.version_statuses[event.handler] status.version_received = True status.version_message = event.message self.log.debug("received version (%s: %d)" % (str(event.handler), event.message.version)) self.send_verack(event.handler) if (not event.handler.isoutbound): self.send_version(event.handler) self.check_version_exchanged(event.handler) def __on_verack(self, event): self.log.debug("received verack (%s)" % (str(event.handler))) status = self.version_statuses[event.handler] #if (not status.sent_version or status.verack_received): # event.source.handle_close() # return status.verack_received = True self.check_version_exchanged(event.handler) def on_message(self, event): if (event.message.type == MSG_VERSION): self.__on_version(event) return if (event.message.type == MSG_VERACK): self.__on_verack(event) return if (event.handler not in self.version_exchanged_nodes): self.log.warning("peer %s sent message of type %s before message exchanging version" % (str(event.handler), MESSAGE_NAMES[event.message.type])) return handler, message = event.handler, event.message self.node.emit_message(self.EVT_MESSAGE, message=message, handler=handler) self.node.emit_message((self.EVT_MESSAGE, message.type), message=message, handler=handler) def check_version_exchanged(self, handler): if self.version_statuses[handler].versions_exchanged(): self.log.info("Accepted new peer: %s" % (handler)) self.version_exchanged_nodes.add(handler) #todo: replace 'handler' by 'peer' self.node.emit_message(self.EVT_VERSION_EXCHANGED, handler=handler, version_message=self.version_statuses[handler].version_message) def send_version(self, handler): version = VersionMessage(version = self.params.version, services=self.params.enabledservices, timestamp=time.time(), addr_me=self.addr_me, addr_you=Netaddr(SERVICES_NONE, handler.sockaddr.ip, handler.sockaddr.port), nonce=self.params.nonce, sub_version_num=self.params.sub_version_num, start_height=self.get_blockchain_height()) self.log.debug("Sending version(%s): %s" % (handler.sockaddr, version)) self.version_statuses[handler].version_sent = True handler.send_message(version) def send_verack(self, handler): verack = VerackMessage() self.log.debug("Sending verack(%s): %s" % (handler.sockaddr, verack)) self.version_statuses[handler].verack_sent = True handler.send_message(VerackMessage())
class Blockchain(Observable): """Base Blockchain logic * Independent of database. * Link between Outpoints and Inputs are not verified. * There is only a mainchain (no albranches) See Also: `ConnectingBlockchain` and `BlockchainWithAltbranches` """ EVT_APPENDED_BLOCK = Observable.createevent() EVT_CONNECTED_BLOCK = Observable.createevent() EVT_DISCONNECTED_BLOCK = Observable.createevent() EVT_SPENT_OUTPUT = Observable.createevent() EVT_UNSPENT_OUTPUT = Observable.createevent() EVT_REORGANIZE = Observable.createevent() EVT_NEW_HIGHEST_BLOCK = Observable.createevent() def __init__(self, log, database): super(Blockchain, self).__init__() self.log = log self.database = database #set of unit256(): Not currency persisted as not supported by blkindex.dat def contains_transaction(self, transaction_hash): return self.database.contains_transaction(transaction_hash) def get_transaction_handle(self, transaction_hash): return self.database.get_transaction_handle(transaction_hash) """ return a list of (hash, block) from firsthash(not included) to lasthash """ def get_branch_blocks(self, lasthash, firsthash=None): blocks = [] pos = self.get_block_handle(lasthash) while pos.hasprev() and pos.hash != firsthash: blocks.append((pos.hash, pos.get_block())) pos = self.database.get_block_handle(pos.blockindex.blockheader.hash_prev) blocks.reverse() return blocks """ Return (txout, height, iscoinbase) corresponding to an outpoint """ def get_outpoint(self, outpoint): tx_handle = self.database.get_transaction_handle(outpoint.hash) tx = tx_handle.get_transaction() if not (0 <= outpoint.index < len(tx.out_list)): return Exception("Outpoint not found") return (tx.out_list[outpoint.index], tx_handle.get_block().get_height(), tx.iscoinbase()) def get_work_after_block(self, blckhash): pos = self.database.get_block_handle(self.get_bestblock()) work = 0 while pos.hasprev() and pos.hash != blckhash: blockheader = pos.get_blockheader() work += blockheader.work() pos = self.database.get_block_handle(blockheader.hash_prev) return work def apply_modifications(self, updates): self.database.begin_updates() for upd in updates: if type(upd) is Reorganize: for blkhash, blk in reversed(upd.old_mainchain): self._mark_blockinputs_spent(blk, False) self.database.reorganize(upd) for blkhash, blk in upd.new_mainchain: self._mark_blockinputs_spent(blk, True) if type(upd) is Append: self.database.append_block(upd.blockhash, upd.block) self.database.commit_updates() for upd in updates: if type(upd) is Append: self.fire(self.EVT_NEW_HIGHEST_BLOCK, blkhash=upd.blockhash, height=self.get_height()) if type(upd) is Reorganize: for blkhash, blk in upd.new_mainchain: self.fire(self.EVT_NEW_HIGHEST_BLOCK, blkhash=blkhash, height=self.get_height()) def _mark_blockinputs_spent(self, block, spent=True): for tx in block.transactions: txhash = hash_tx(tx) if not tx.iscoinbase(): for txin in tx.in_list: txprev_handle = self.database.get_transaction_handle(txin.previous_output.hash) txprev_handle.mark_spent(txin.previous_output.index, spent, txhash) def _fire_connect_block_events(self, block, blockhash): self.fire(self.EVT_CONNECTED_BLOCK, block=block, blockhash=blockhash) for tx in block.transactions: if not tx.iscoinbase(): for index in range(len(tx.in_list)): #txhash = hash_tx(tx) handle = self.database.get_transaction_handle(tx.in_list[index].previous_output.hash) self.fire(self.EVT_SPENT_OUTPUT, txhash=handle.hash, index=index) def get_next_in_mainchain(self, blockhash): return self.database.get_next_in_mainchain(blockhash) def contains_block(self, blockhash): return self.database.contains_block(blockhash) def get_block_handle(self, blockhash): return self.database.get_block_handle(blockhash) def get_block(self, blockhash): return self.database.get_block_handle(blockhash).get_block() def get_bestblock(self): return self.database.get_mainchain() def get_height(self): handlebest = self.database.get_block_handle(self.database.get_mainchain()) return handlebest.get_height() def get_block_locator(self): block_locator = [] it = BlockIterator(self.database, self.database.get_mainchain()) stepsize = 1 while (it.hash != self.database.genesishash): block_locator.append(it.hash) i = 0 while it.hasprev() and i < stepsize: it.prev() i += 1 stepsize*= 2 #tmp speedup hack #if stepsize >= 128: # break block_locator.append(self.database.genesishash) return BlockLocator(1, block_locator) # Get testnet work required after 15 Feb 2012 def get_testnet_work_required_15feb1012(self, blkprev, block): #If there is not block during 2*TARGET_SPACING, reset difficulty to min-difficilty if (block.blockheader.time - blkprev.get_blockheader().time > TARGET_SPACING * 2 or block.blockheader.time < blkprev.get_blockheader().time): new_target = PROOF_OF_WORK_LIMIT[self.database.runmode] else: #otherwise, keep the last non-special difficulty while blkprev and blkprev.get_height() % TARGET_INTERVAL != 0 and blkprev.get_blockheader().bits == compact_difficulty(PROOF_OF_WORK_LIMIT[self.database.runmode]): blkprev = self.database.get_block_handle(blkprev.get_blockheader().hash_prev) new_target = blkprev.get_blockheader().target() return compact_difficulty(new_target) #GetNextWorkRequired: main.cpp:819 def get_next_work_required(self, blkprevhash, block): blkprev = self.database.get_block_handle(blkprevhash) # Difficulty changes only once every TARGET_INTERVAL blocks (except for testnet) if ((blkprev.get_height() + 1) % TARGET_INTERVAL): # Special rules for testnet after 15 Feb 2012 if ((self.database.runmode == TESTNET and (block.blockheader.time > 1329264000)) or (self.database.runmode == TESTNET3)): return self.get_testnet_work_required_15feb1012(blkprev, block) # Difficulty unchanged return (blkprev.get_blockheader().bits) # Locate the block 2 weeks ago blk2weekago = blkprev for i in range(TARGET_INTERVAL-1): blk2weekago = self.database.get_block_handle(blk2weekago.get_blockheader().hash_prev) header_block2weekago = blk2weekago.get_blockheader() header_blocknow = blkprev.get_blockheader() actual_timespan = header_blocknow.time - header_block2weekago.time # Limit adjustment step if actual_timespan < TARGET_TIMESPAN/4: actual_timespan = TARGET_TIMESPAN/4; if actual_timespan > TARGET_TIMESPAN*4: actual_timespan = TARGET_TIMESPAN*4; # Retarget new_target = Uint256.from_bignum(header_blocknow.target().get_bignum() * actual_timespan / TARGET_TIMESPAN) if new_target > PROOF_OF_WORK_LIMIT[self.database.runmode]: new_target = PROOF_OF_WORK_LIMIT[self.database.runmode] new_bits = compact_difficulty(new_target) self.log.info("Retarget: targetTimespan:%d actualTimespan:%d, %08x -> %08x " % (TARGET_TIMESPAN, actual_timespan, header_blocknow.bits, new_bits)) return (new_bits) #ref main.h:1109 def get_median_time_past(self, hashprev): block_times = [] i = 0 while hashprev != Uint256.zero() and i < MEDIAN_TIME_SPAN: blk = self.database.get_block_handle(hashprev) blkheader = blk.get_blockheader() block_times.append(blkheader.time) hashprev = blkheader.hash_prev i += 1 return median(block_times)
def __init__(self): Observable.__init__(self) self.txs = {} self.spent_outpoints = {} # outpoint => (txhash, in_index)