Example #1
0
class LedgerState:

    save_path = 'utxo.bin'
    do_yields = True

    def __init__(self, load=False):
        self.outpoints = UTXO_Map()
        self.block_name = ZERO_NAME
        self.height = -1
        self.total = 0
        self.lost = 0
        self.fees = 0
        if load:
            from __main__ import G
            save_path = os.path.join(G.args.base, self.save_path)
            self.load_state(save_path)

    def clone(self):
        ob = LedgerState()
        ob.block_name = self.block_name
        ob.height = self.height
        ob.total = self.total
        ob.lost = self.lost
        ob.fees = self.fees
        ob.outpoints = self.outpoints.copy()
        return ob

    def extend(self, block, height, verify=True):
        ob = self.clone()
        ob.feed_block(block, height, verify)
        return ob

    def get_total_outpoints(self):
        total = 0
        for k, v in self.outpoints:
            total += len(v)
        return total

    cache_version = 3

    def save_state(self):
        from coro.asn1.data_file import DataFileWriter
        from __main__ import G
        save_path = os.path.join(G.args.base, self.save_path)
        f = open(save_path + '.tmp', 'wb')
        df = DataFileWriter(f)
        t0 = timer()
        df.write_object([
            self.cache_version, self.height,
            str(self.block_name), self.total, self.lost, self.fees,
            len(self.outpoints)
        ])
        n = 0
        for item in self.outpoints:
            df.write_object(item)
            n += 1
            if n % 1000 == 999:
                coro.yield_slice()
        f.close()
        os.rename(save_path + '.tmp', save_path)
        LOG('saved outpoints', len(self.outpoints), n, t0.end())

    def load_state(self, path=None):
        from coro.asn1.data_file import DataFileReader
        from __main__ import G
        if path is None:
            path = os.path.join(G.args.base, self.save_path)
        LOG('cache', 'start')
        t0 = timer()
        try:
            f = open(path, 'rb')
            df = DataFileReader(f)
            info = df.read_object()
            if info[0] < self.cache_version:
                LOG('old cache version, ignoring')
                return
            assert (info[0] == self.cache_version)  # version
            [
                _, self.height, self.block_name, self.total, self.lost,
                self.fees, size
            ] = info
            LOG('cache', self.height, size)
            self.block_name = Name(self.block_name)
            n = [0]
            df.next = df.read_object
            self.outpoints.build(df, size)
            f.close()
            LOG('cache', 'stop', len(self.outpoints), n[0])
            LOG('cache', self.height, repr(self.block_name))
        except IOError:
            pass
        LOG('cache', 'stop', t0.end())

    def store_outputs(self, tx):
        output_sum = 0
        outputs = []
        for i, (amt, lock_script) in enumerate(tx.outputs):
            #if len(lock_script) > 500:
            #    W ('%r len(script) = %d\n' % (tx.name, len(lock_script)))
            if not is_unspendable(lock_script):
                outputs.append((i, amt, lock_script))
            output_sum += amt
        self.outpoints.new_entry(str(tx.name), outputs)
        self.total += output_sum
        return output_sum

    def get_utxo(self, name, index):
        return self.outpoints.get_utxo(name, index)

    def feed_tx(self, index, tx, timestamp, verify=False):
        input_sum = 0
        for j in range(len(tx.inputs)):
            (outpoint, index), script, sequence = tx.inputs[j]
            outstr = str(outpoint)
            amt, lock_script = self.outpoints.pop_utxo(outstr, index)
            if verify:
                try:
                    tx.verify(j, lock_script, timestamp)
                except VerifyError:
                    self.outpoints.new_entry(outstr,
                                             [(index, amt, lock_script)])
                    raise
            input_sum += amt
            if self.do_yields and j % 20 == 19:
                coro.yield_slice()
        output_sum = self.store_outputs(tx)
        return input_sum, output_sum

    def feed_block(self, b, height, verify=False):
        if b.prev_block != self.block_name:
            raise ValueError(b.prev_block, self.block_name)
        # assume coinbase is ok for now
        tx0 = b.transactions[0]
        reward0 = self.store_outputs(tx0)
        fees = 0
        for i, tx in enumerate(b.transactions):
            if i == 0:
                continue
            input_sum, output_sum = self.feed_tx(i, tx, b.timestamp, verify)
            fees += input_sum - output_sum
            self.total -= input_sum
        self.fees += fees
        reward1 = compute_reward(height)
        if reward1 + fees != reward0:
            lost = (reward1 + fees) - reward0
            #W ('reward mismatch height=%d lost=%s\n' % (height, lost))
            self.lost += lost
        self.height = height
        self.block_name = b.name
Example #2
0
class LedgerState:

    save_path = 'utxo.bin'
    do_yields = True

    def __init__(self, load=False):
        self.outpoints = UTXO_Map()
        self.block_name = ZERO_NAME
        self.height = -1
        self.total = 0
        self.lost = 0
        self.fees = 0
        if load:
            from __main__ import G
            save_path = os.path.join(G.args.base, self.save_path)
            self.load_state(save_path)

    def clone(self):
        ob = LedgerState()
        ob.block_name = self.block_name
        ob.height = self.height
        ob.total = self.total
        ob.lost = self.lost
        ob.fees = self.fees
        ob.outpoints = self.outpoints.copy()
        return ob

    def extend(self, block, height, verify=True):
        ob = self.clone()
        ob.feed_block(block, height, verify)
        return ob

    def get_total_outpoints(self):
        total = 0
        for k, v in self.outpoints:
            total += len(v)
        return total

    cache_version = 2

    def save_state(self):
        from coro.asn1.data_file import DataFileWriter
        from __main__ import G
        save_path = os.path.join(G.args.base, self.save_path)
        f = open(save_path + '.tmp', 'wb')
        df = DataFileWriter(f)
        t0 = timer()
        df.write_object([
            self.cache_version, self.height,
            str(self.block_name), self.total, self.lost, self.fees,
            len(self.outpoints)
        ])
        n = 0
        for item in self.outpoints:
            df.write_object(item)
            n += 1
            if n % 1000 == 999:
                coro.yield_slice()
        f.close()
        os.rename(save_path + '.tmp', save_path)
        W('[saved outpoints %d/%d entries %.02fs]' %
          (len(self.outpoints), n, t0.end()))

    def load_state(self, path=None):
        from coro.asn1.data_file import DataFileReader
        from __main__ import G
        if path is None:
            path = os.path.join(G.args.base, self.save_path)
        W('loading outpoints cache...')
        t0 = timer()
        try:
            f = open(path, 'rb')
            df = DataFileReader(f)
            info = df.read_object()
            assert (info[0] == self.cache_version)  # version
            [
                _, self.height, self.block_name, self.total, self.lost,
                self.fees, size
            ] = info
            W(' height = %d ...' % (self.height, ))
            self.block_name = Name(self.block_name)
            n = [0]

            def gen():
                while 1:
                    try:
                        x = df.read_object()
                        n[0] += 1
                        yield x
                    except EOFError:
                        break

            self.outpoints.build(gen(), size)
            f.close()
            W('[loaded outpoints %d/%d entries]' % (len(self.outpoints), n[0]))
            W('\nlast block: %d %064x\n' % (self.height, self.block_name))
        except IOError:
            pass
        W('...done (%.2fs)\n' % (t0.end(), ))

    def store_outputs(self, tx):
        output_sum = 0
        i = 0
        outputs = []
        for amt, pk_script in tx.outputs:
            #if len(pk_script) > 500:
            #    W ('%r len(script) = %d\n' % (tx.name, len(pk_script)))
            outputs.append((i, amt, pk_script))
            if amt > 0:
                output_sum += amt
            i += 1
        self.outpoints.new_entry(str(tx.name), outputs)
        self.total += output_sum
        return output_sum

    def get_utxo(self, name, index):
        return self.outpoints.get_utxo(name, index)

    def feed_block(self, b, height, verify=False):
        if b.prev_block != self.block_name:
            raise ValueError(b.prev_block, self.block_name)
        # assume coinbase is ok for now
        tx0 = b.transactions[0]
        reward0 = self.store_outputs(tx0)
        fees = 0
        for i, tx in enumerate(b.transactions):
            if i == 0:
                continue
            # verify each transaction
            # first, we need the output script for each of the inputs
            input_sum = 0
            for j in range(len(tx.inputs)):
                (outpoint, index), script, sequence = tx.inputs[j]
                amt, oscript = self.outpoints.pop_utxo(str(outpoint), index)
                #W ('.')
                if verify:
                    tx.verify(j, oscript, b.timestamp)
                # XXX if it fails to verify, put it back!
                input_sum += amt
            output_sum = self.store_outputs(tx)
            fees += input_sum - output_sum
            self.total -= input_sum
            if self.do_yields and i % 50 == 0:
                coro.yield_slice()
        self.fees += fees
        reward1 = compute_reward(height)
        if reward1 + fees != reward0:
            lost = (reward1 + fees) - reward0
            #W ('reward mismatch height=%d lost=%s\n' % (height, lost))
            self.lost += lost
        self.height = height
        self.block_name = b.name
Example #3
0
class LedgerState:

    save_path = 'utxo.bin'
    do_yields = True

    def __init__ (self, load=False):
        self.outpoints = UTXO_Map()
        self.block_name = ZERO_NAME
        self.height = -1
        self.total = 0
        self.lost = 0
        self.fees = 0
        if load:
            from __main__ import G
            save_path = os.path.join (G.args.base, self.save_path)
            self.load_state (save_path)

    def clone (self):
        ob = LedgerState()
        ob.block_name = self.block_name
        ob.height = self.height
        ob.total = self.total
        ob.lost = self.lost
        ob.fees = self.fees
        ob.outpoints = self.outpoints.copy()
        return ob

    def extend (self, block, height, verify=True):
        ob = self.clone()
        ob.feed_block (block, height, verify)
        return ob

    def get_total_outpoints (self):
        total = 0
        for k, v in self.outpoints:
            total += len(v)
        return total

    cache_version = 2

    def save_state (self):
        from coro.asn1.data_file import DataFileWriter
        from __main__ import G
        save_path = os.path.join (G.args.base, self.save_path)
        f = open (save_path + '.tmp', 'wb')
        df = DataFileWriter (f)
        t0 = timer()
        df.write_object ([
            self.cache_version,
            self.height,
            str(self.block_name),
            self.total,
            self.lost,
            self.fees,
            len(self.outpoints)
        ])
        n = 0
        for item in self.outpoints:
            df.write_object (item)
            n += 1
            if n % 1000 == 999:
                coro.yield_slice()
        f.close()
        os.rename (save_path + '.tmp', save_path)
        W ('[saved outpoints %d/%d entries %.02fs]' % (len(self.outpoints), n, t0.end()))

    def load_state (self, path=None):
        from coro.asn1.data_file import DataFileReader
        from __main__ import G
        if path is None:
            path = os.path.join (G.args.base, self.save_path)
        W ('loading outpoints cache...')
        t0 = timer()
        try:
            f = open (path, 'rb')
            df = DataFileReader (f)
            info = df.read_object()
            assert (info[0] == self.cache_version)  # version
            [_, self.height, self.block_name, self.total, self.lost, self.fees, size] = info
            W (' height = %d ...' % (self.height,))
            self.block_name = Name (self.block_name)
            n = [0]
            def gen():
                while 1:
                    try:
                        x = df.read_object()
                        n[0] += 1
                        yield x
                    except EOFError:
                        break
            self.outpoints.build (gen(), size)
            f.close()
            W ('[loaded outpoints %d/%d entries]' % (len(self.outpoints),n[0]))
            W ('\nlast block: %d %064x\n' % (self.height, self.block_name))
        except IOError:
            pass
        W ('...done (%.2fs)\n' % (t0.end(),))

    def store_outputs (self, tx):
        output_sum = 0
        i = 0
        outputs = []
        for amt, pk_script in tx.outputs:
            #if len(pk_script) > 500:
            #    W ('%r len(script) = %d\n' % (tx.name, len(pk_script)))
            outputs.append ((i, amt, pk_script))
            if amt > 0:
                output_sum += amt
            i += 1
        self.outpoints.new_entry (str(tx.name), outputs)
        self.total += output_sum
        return output_sum

    def get_utxo (self, name, index):
        return self.outpoints.get_utxo (name, index)

    def feed_block (self, b, height, verify=False):
        if b.prev_block != self.block_name:
            raise ValueError (b.prev_block, self.block_name)
        # assume coinbase is ok for now
        tx0 = b.transactions[0]
        reward0 = self.store_outputs (tx0)
        fees = 0
        for i, tx in enumerate (b.transactions):
            if i == 0:
                continue
            # verify each transaction
            # first, we need the output script for each of the inputs
            input_sum = 0
            for j in range (len (tx.inputs)):
                (outpoint, index), script, sequence = tx.inputs[j]
                amt, oscript = self.outpoints.pop_utxo (str(outpoint), index)
                #W ('.')
                if verify:
                    tx.verify (j, oscript, b.timestamp)
                # XXX if it fails to verify, put it back!
                input_sum += amt
            output_sum = self.store_outputs (tx)
            fees += input_sum - output_sum
            self.total -= input_sum
            if self.do_yields and i % 50 == 0:
                coro.yield_slice()
        self.fees += fees
        reward1 = compute_reward (height)
        if reward1 + fees != reward0:
            lost = (reward1 + fees) - reward0
            #W ('reward mismatch height=%d lost=%s\n' % (height, lost))
            self.lost += lost
        self.height = height
        self.block_name = b.name
Example #4
0
class LedgerState:

    save_path = 'utxo.bin'
    do_yields = True

    def __init__ (self, load=False):
        self.outpoints = UTXO_Map()
        self.block_name = ZERO_NAME
        self.height = -1
        self.total = 0
        self.lost = 0
        self.fees = 0
        if load:
            from __main__ import G
            save_path = os.path.join (G.args.base, self.save_path)
            self.load_state (save_path)

    def clone (self):
        ob = LedgerState()
        ob.block_name = self.block_name
        ob.height = self.height
        ob.total = self.total
        ob.lost = self.lost
        ob.fees = self.fees
        ob.outpoints = self.outpoints.copy()
        return ob

    def extend (self, block, height, verify=True):
        ob = self.clone()
        ob.feed_block (block, height, verify)
        return ob

    def get_total_outpoints (self):
        total = 0
        for k, v in self.outpoints:
            total += len(v)
        return total

    cache_version = 3

    def save_state (self):
        from coro.asn1.data_file import DataFileWriter
        from __main__ import G
        save_path = os.path.join (G.args.base, self.save_path)
        f = open (save_path + '.tmp', 'wb')
        df = DataFileWriter (f)
        t0 = timer()
        df.write_object ([
            self.cache_version,
            self.height,
            str(self.block_name),
            self.total,
            self.lost,
            self.fees,
            len(self.outpoints)
        ])
        n = 0
        for item in self.outpoints:
            df.write_object (item)
            n += 1
            if n % 1000 == 999:
                coro.yield_slice()
        f.close()
        os.rename (save_path + '.tmp', save_path)
        LOG ('saved outpoints', len(self.outpoints), n, t0.end())

    def load_state (self, path=None):
        from coro.asn1.data_file import DataFileReader
        from __main__ import G
        if path is None:
            path = os.path.join (G.args.base, self.save_path)
        LOG ('cache', 'start')
        t0 = timer()
        try:
            f = open (path, 'rb')
            df = DataFileReader (f)
            info = df.read_object()
            if info[0] < self.cache_version:
                LOG ('old cache version, ignoring')
                return
            assert (info[0] == self.cache_version)  # version
            [_, self.height, self.block_name, self.total, self.lost, self.fees, size] = info
            LOG ('cache', self.height, size)
            self.block_name = Name (self.block_name)
            n = [0]
            df.next = df.read_object
            self.outpoints.build (df, size)
            f.close()
            LOG ('cache', 'stop', len(self.outpoints), n[0])
            LOG ('cache', self.height, repr(self.block_name))
        except IOError:
            pass
        LOG ('cache', 'stop', t0.end())

    def store_outputs (self, tx):
        output_sum = 0
        outputs = []
        for i, (amt, lock_script) in enumerate (tx.outputs):
            #if len(lock_script) > 500:
            #    W ('%r len(script) = %d\n' % (tx.name, len(lock_script)))
            if not is_unspendable (lock_script):
                outputs.append ((i, amt, lock_script))
            output_sum += amt
        self.outpoints.new_entry (str(tx.name), outputs)
        self.total += output_sum
        return output_sum

    def get_utxo (self, name, index):
        return self.outpoints.get_utxo (name, index)

    def feed_tx (self, index, tx, timestamp, verify=False):
        input_sum = 0
        for j in range (len (tx.inputs)):
            (outpoint, index), script, sequence = tx.inputs[j]
            outstr = str(outpoint)
            amt, lock_script = self.outpoints.pop_utxo (outstr, index)
            if verify:
                try:
                    tx.verify (j, lock_script, timestamp)
                except VerifyError:
                    self.outpoints.new_entry (outstr, [(index, amt, lock_script)])
                    raise
            input_sum += amt
            if self.do_yields and j % 20 == 19:
                coro.yield_slice()
        output_sum = self.store_outputs (tx)
        return input_sum, output_sum

    def feed_block (self, b, height, verify=False):
        if b.prev_block != self.block_name:
            raise ValueError (b.prev_block, self.block_name)
        # assume coinbase is ok for now
        tx0 = b.transactions[0]
        reward0 = self.store_outputs (tx0)
        fees = 0
        for i, tx in enumerate (b.transactions):
            if i == 0:
                continue
            input_sum, output_sum = self.feed_tx (i, tx, b.timestamp, verify)
            fees += input_sum - output_sum
            self.total -= input_sum
        self.fees += fees
        reward1 = compute_reward (height)
        if reward1 + fees != reward0:
            lost = (reward1 + fees) - reward0
            #W ('reward mismatch height=%d lost=%s\n' % (height, lost))
            self.lost += lost
        self.height = height
        self.block_name = b.name