Beispiel #1
0
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)
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
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))
Beispiel #4
0
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)
Beispiel #5
0
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()
Beispiel #6
0
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)
Beispiel #7
0
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))
Beispiel #8
0
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())
Beispiel #9
0
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()
Beispiel #10
0
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))
Beispiel #11
0
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()
Beispiel #12
0
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)
Beispiel #13
0
class ReceiveView(wx.Dialog, Observable):
    EVT_SET_LABEL = Observable.createevent()

    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)

    @guithread
    def open(self):
        self.Show(True)

    def on_ok(self, event):
        self.Show(False)
        self.fire(self.EVT_SET_LABEL)

    def on_cancel(self):
        self.Show(False)

    def set_receive_address(self, address):
        self.address_textctrl.SetValue(address)

    def get_receive_address(self):
        return self.address_textctrl.GetValue()

    def get_label(self):
        return self.label_textctrl.GetValue()
Beispiel #14
0
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()
Beispiel #15
0
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)
Beispiel #16
0
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)
Beispiel #17
0
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)
Beispiel #18
0
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)
Beispiel #19
0
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())