コード例 #1
0
class Tx(js.Entity, bs.Entity):
    type = "tx"
    fields = {
        "version": js.Int,
        "inputs": js.List(TxInput),
        "outputs": js.List(TxOutput),
        "locktime": js.Int,
    }
    bfields = [
        ("version", bs.structfmt("<I")),
        ("inputs", bs.VarList(TxInput)),
        ("outputs", bs.VarList(TxOutput)),
        ("locktime", bs.structfmt("<I")),
    ]

    @cachedproperty
    def hash(self):
        return doublesha(self.tobinary())

    @property
    def coinbase(self):
        return len(
            self.inputs
        ) == 1 and self.inputs[0].outpoint.tx == nullhash and self.inputs[
            0].outpoint.index == 0xffffffff

    def __repr__(self):
        return "<Tx %s, %d inputs, %d outputs, coinbase: %s>" % (h2h(
            self.hash), len(self.inputs), len(self.outputs), str(
                self.coinbase))
コード例 #2
0
class Blockmsg(js.Entity, bs.Entity):
    type = "block"
    fields = {"type": js.Str, "block": Block, "txs": js.List(Tx)}
    bfields = [
        ("block", Block),
        ("txs", bs.VarList(Tx)),
    ]
コード例 #3
0
class merkelproof(js.Entity, bs.Entity):
    bfields = [
        ("pairs", bs.VarList(bs.structfmt("<32s32s"))),
        ("leaf", bs.Hash),
        ("block", bs.Hash),
    ]

    @constructor
    def make(self, block, tx):
        self.block = block.hash
        self.leaf = tx.hash
        self.pairs = utils.get_merkel_tree(block.txs, tx.hash)

    @cachedproperty
    def merkelroot(self):
        return doublesha("".join(self.pairs[-1]))

    @cachedproperty
    def valid(self, n):
        leaf = self.leaf
        for pair in pairs:
            if leaf not in pair:
                return False
            leaf = doublesha("".join(pair))
        return True
コード例 #4
0
ファイル: msgs.py プロジェクト: cradle/pycoin
class BlockAux(js.Entity, bs.Entity):
    fields = {
        "block": Block,
        "txs": js.List(js.Hash),
        "number": js.Int,
        "totaldiff": js.Int,
        "invalid": js.Bool,
        "mainchain": js.Bool,
        "chained": js.Bool,
        "succ": js.Hash,
    }
    bfields = [
        ("block", Block),
        ("txs", bs.VarList(bs.Hash)),
        ("number", bs.structfmt("<I")),
        ("totaldiff", bs.structfmt("<Q")),
        ("invalid", bs.structfmt("<?")),
        ("mainchain", bs.structfmt("<?")),
        ("chained", bs.structfmt("<?")),
        ("succ", bs.Hash),
    ]

    @constructor
    def make(self, block):
        self.block, self.txs, self.number = block, [], 2**32 - 1
        self.totaldiff, self.invalid, self.mainchain = 0, False, False
        self.chained, self.succ = False, nullhash
コード例 #5
0
class Addr(js.Entity, bs.Entity):
    type = "addr"
    fields = {
        "type": js.Str,
        "addrs": js.List(AddressTimestamp),
    }
    bfields = [
        ("addrs", bs.VarList(AddressTimestamp)),
    ]
コード例 #6
0
ファイル: msgs.py プロジェクト: cradle/pycoin
class Tx(js.Entity, bs.Entity):
    type = "tx"
    fields = {
        "version": js.Int,
        "inputs": js.List(TxInput),
        "outputs": js.List(TxOutput),
        "locktime": js.Int,
    }
    bfields = [
        ("version", bs.structfmt("<I")),
        ("inputs", bs.VarList(TxInput)),
        ("outputs", bs.VarList(TxOutput)),
        ("locktime", bs.structfmt("<I")),
    ]

    @cachedproperty
    def hash(self):
        return doublesha(self.tobinary())
コード例 #7
0
class Inv(js.Entity, bs.Entity):
    type = "inv"
    fields = {
        "type": js.Str,
        "objs": js.List(InvVect),
    }
    bfields = [("objs", bs.VarList(InvVect))]

    @constructor
    def make(self, objs):
        self.objs = objs
コード例 #8
0
class Getdata(js.Entity, bs.Entity):
    type = "getdata"
    # Content-wise identical to "inv"
    fields = {
        "type": js.Str,
        "objs": js.List(InvVect),
    }
    bfields = [("objs", bs.VarList(InvVect))]

    @constructor
    def make(self, objs):
        self.objs = objs
コード例 #9
0
ファイル: addressmanager.py プロジェクト: XertroV/pycoin-1
class Tx(js.Entity, bs.Entity):
    fields = {
        "tx": msgs.Tx,
        "block": js.Hash,
        "blkindex": js.Int,
        #"flags":js.Int,
        "redeemed": js.List(js.Hash),
    }
    bfields = [
        ("tx", msgs.Tx),
        ("block", bs.Hash),
        ("blkindex", bs.structfmt("<L")),
        #("flags", bs.VarInt),
        ("redeemed", bs.VarList(bs.Hash)),
    ]
コード例 #10
0
ファイル: msgs.py プロジェクト: cradle/pycoin
class TxAux(js.Entity, bs.Entity):
    fields = {
        "tx": Tx,
        "block": js.Hash,
        "redeemed": js.List(js.Hash),
    }
    bfields = [
        ("tx", Tx),
        ("block", bs.Hash),
        ("redeemed", bs.VarList(bs.Hash)),
    ]

    @constructor
    def make(self, tx):
        self.tx, self.block = tx, nullhash
        self.redeemed = [nullhash] * len(tx.outputs)
コード例 #11
0
class Getblocks(js.Entity, bs.Entity):
    type = "getblocks"
    fields = {
        "type": js.Str,
        "version": js.Int,
        "starts": js.List(js.Hash),
        "end": js.Hash,
    }
    bfields = [
        ("version", bs.structfmt("<I")),
        ("starts", bs.VarList(bs.Hash)),
        ("end", bs.Hash),
    ]

    @constructor
    def make(self, starts, end=b"\x00" * 32):
        self.version = 31900
        self.starts = starts
        self.end = end
コード例 #12
0
class Tx(js.Entity, bs.Entity):
    fields = {
        "tx": msgs.Tx,
        "block": js.Hash,
        "blkindex": js.Int,
        #"flags":js.Int,
        "redeemed": js.List(js.Hash),
    }
    bfields = [
        ("tx", msgs.Tx),
        ("block", bs.Hash),
        ("blkindex", bs.structfmt("<L")),
        #("flags", bs.VarInt),
        ("redeemed", bs.VarList(bs.Hash)),
    ]

    @staticmethod
    def get_by_hash(h):
        """get a transaction from the database.
        throws KeyError, if not found.
        """
        log.debug("getting tx %s", h2h(h))
        return Tx.frombinary(txs.get(h))[0]

    @txn_required
    def put(self):
        """update the database record of the transaction."""
        log.debug("putting tx %s", h2h(self.hash))
        if not Tx.exist(self.hash):
            for cb in settings.NEW_TX_HOOKS:
                cb(self)
        txs.put(self.hash, self.tobinary())

    @staticmethod
    def iter_tx():
        """loop though all transactions known to the database."""
        try:
            cur = txs.cursor()
            while True:
                try:
                    h, data = cur.next()
                except KeyError:
                    break
                yield Tx.frombinary(data)[0]
        finally:
            cur.close()

    @staticmethod
    @txn_required
    def get_or_make(txmsg):
        """get the database transaction, or make it from txmsg if it does not exist."""
        if not Tx.exist(txmsg.hash):
            tx = Tx.make(txmsg)
            tx.put()
        else:
            tx = Tx.get_by_hash(txmsg.hash)
        return tx

    @staticmethod
    def exist(h):
        """check if a transactions exist in the database"""
        return txs.has_key(h)

    @property
    def hash(self):
        return self.tx.hash

    @property
    def hexhash(self):
        return h2h(self.hash)

    @property
    def confirmed(self):
        return self.block != nullhash

    @property
    def inputs(self):
        return self.tx.inputs

    @property
    def outputs(self):
        return self.tx.outputs

    @property
    def coinbase(self):
        return self.tx.coinbase

    def get_block(self):
        """get the block in which this transaction is included. returns None, if the transaction is not confirmed"""
        if self.confirmed:
            return blockchain.Block.get_by_hash(self.block)
        else:
            return None

    def get_confirmations(self):
        """get the number of confirmations this transactions haves."""
        blk0 = self.get_block()
        if not blk0:
            return 0
        blk1 = blockchain.get_bestblock()
        return blk1.number - blk0.number + 1

    @txn_required
    def confirm(self, block, blkidx=0, coinbase=False):
        log.info("confirming tx %s", h2h(self.hash))
        self.block = block.hash
        self.blkindex = blkidx
        if settings.TX_CHECK_SCRIPTS:
            self.check_signatures()
        if coinbase:
            return
        for inp in self.tx.inputs:
            inp_tx = Tx.get_by_hash(inp.outpoint.tx)
            inp_tx.redeem_output(inp.outpoint, self)
            inp_tx.put()

    def revert(self, block=None, coinbase=False):
        log.info("reverting tx %s", h2h(self.hash))
        self.block = nullhash
        if coinbase:
            return
        for inp in self.tx.inputs:
            inp_tx = Tx.get_by_hash(inp.outpoint.tx)
            inp_tx.unredeem_output(inp.outpoint)
            inp_tx.put()

    def get_amount_out(self):
        return sum(i.amount for i in self.tx.outputs)

    def get_amount_in(self, block=None, coinbase=False):
        if self.coinbase:
            if coinbase and block:
                reward = 50 * COIN >> (block.number / 210000)
                fees = block.get_all_fees()
                return reward + fees
            else:
                return 0
        amount = 0
        for inp in self.tx.inputs:
            amount += Tx.get_outpoint(inp.outpoint).amount
        return amount

    def get_fee(self):
        if self.coinbase:
            return 0
        return self.get_amount_in() - self.get_amount_out()

    def verify(self,
               check_spend=False,
               check_scripts=False,
               block=None,
               coinbase=False):
        if self.total_amount_out() > self.total_amount_in(block=block):
            return False
        if check_spend:
            pass  #TODO: check if all inputs are not spend yet
        if check_scripts:
            return self.check_signatures()
        return True

    def __repr__(self):
        return "<Tx %s, %d inputs, %d outputs, coinbase: %s, in: %d, out: %d, fees: %d>" % (
            h2h(self.hash), len(self.tx.inputs), len(
                self.tx.outputs), str(self.coinbase), self.get_amount_in(),
            self.get_amount_out(), self.get_fee())

    def get_simple_hash(self, inputidx, hashtype):
        assert hashtype == 1
        tx = msgs.Tx.frombinary(self.tx.tobinary())[0]  # copy the transaction
        for i, inp in enumerate(tx.inputs):
            if i == inputidx:
                outp = Tx.get_outpoint(inp.outpoint)
                inp.script = outp.script
            else:
                inp.script = ""
        return doublesha(tx.tobinary() + struct.pack("<L", hashtype))

    def extract_info(self, inputidx):
        inp = self.inputs[inputidx]
        outp = Tx.get_outpoint(inp.outpoint)
        if outp.script[:2] == "\x76\xa9" and outp.script[
                -2:] == "\x88\xac":  #to address
            address = outp.script[3:-2]
            rest = inp.script
            sig_l, rest = ord(rest[0]), rest[1:]
            sig, rest = rest[:sig_l], rest[sig_l:]
            key_l, rest = ord(rest[0]), rest[1:]
            key, rest = rest[:key_l], rest[key_l:]
            if rest != "" or len(key) != key_l or len(sig) != sig_l:
                return (0, None, None, None)
            return (1, address, sig, key)
        if outp.script[-1:] == "\xac":  # generation
            rest = outp.script
            key_l, rest = ord(rest[0]), rest[1:]
            key, rest = rest[:key_l], rest[key_l:]
            if rest != "\xac":
                return (0, None, None, None)
            rest = inp.script
            sig_l, rest = ord(rest[0]), rest[1:]
            sig, rest = rest[:sig_l], rest[sig_l:]
            if rest != "":
                return (0, None, None, None)
            if len(key) != key_l or len(sig) != sig_l:
                return (0, None, None, None)
            return (2, None, sig, key)

    def check_signatures(self):
        if self.coinbase:
            return True
        for idx in range(len(self.inputs)):
            sc_type, address, sig, keystr = self.extract_info(idx)

            if sc_type == 0:
                log.warning(
                    "tx input %s:%d could not be validated, could not extract info",
                    h2h(self.hash), idx)
                continue
            if sc_type == 1:
                log.info("tx input %s:%d, is in address-form", h2h(self.hash),
                         idx)
                if hash160(key) != address:
                    log.warning(
                        "tx input %s:%d is invalid, address and publickey does not match",
                        h2h(self.hash), idx)
                    return False
            elif sc_type == 2:
                log.info("tx input %s:%d is from coin generation",
                         h2h(self.hash), idx)

            sig, hashtype = ec.load_sig(sig)
            if hashtype != 1:
                log.warning(
                    "tx input %s:%d could not be validated, hashtype is wrong",
                    h2h(self.hash), idx)
                continue

            simple_hash = self.get_simple_hash(idx, hashtype)
            key = ec.Key.from_pubkey(keystr)
            res = key.verify(simple_hash, sig)
            if res:
                log.info("tx input %s:%d, is valid", h2h(self.hash), idx)
            else:
                log.warning("tx input %s:%d, is invalid, signature is invalid",
                            h2h(self.hash), idx)
                return False
        return True

    @constructor
    def make(self, tx):
        self.tx, self.block = tx, nullhash
        self.blkindex = 0
        self.redeemed = [nullhash] * len(tx.outputs)

    def get_inpoint(self, i):
        return msgs.TxPoint(self.hash, i)

    @staticmethod
    def get_outpoint(outpoint, txn=None):
        tx = Tx.get_by_hash(outpoint.tx)
        assert outpoint.index < len(tx.outputs), "outpoint index out of range"
        return tx.outputs[outpoint.index]

    def is_redeemed(self, outpoint):
        assert self.hash == outpoint.tx, "outpoint hash does not point to tx"
        assert outpoint.index < len(
            self.tx.outputs), "outpoint index out of range"
        return self.redeemed[outpoint.index] != nullhash

    def redeem_output(self, outpoint, tx):
        assert self.hash == outpoint.tx, "outpoint hash does not point to tx"
        assert outpoint.index < len(
            self.tx.outputs), "outpoint index out of range"
        if self.redeemed[outpoint.index] != nullhash:
            raise TxInputAlreadySpend(outpoint)
        self.redeemed[outpoint.index] = tx.hash

    def unredeem_output(self, outpoint):
        assert self.hash == outpoint.tx, "outpoint hash does not point to tx"
        assert outpoint.index < len(
            self.tx.outputs), "outpoint index out of range"
        self.redeemed[outpoint.index] = nullhash

    def fully_redeemed(self):
        return nullhash not in self.redeemed
コード例 #13
0
class Block(js.Entity, bs.Entity):
    fields = {
        "block": msgs.Block,
        "number": js.Int,
        "totaldiff": js.Int,
        "chain": js.Int,
        "txs": js.List(js.Hash),
        "nexts": js.List(js.Hash)
    }
    bfields = [("block", msgs.Block), ("number", bs.structfmt("<L")),
               ("totaldiff", bs.structfmt("<Q")),
               ("chain", bs.structfmt("<B")), ("txs", bs.VarList(bs.Hash)),
               ("nexts", bs.VarList(bs.Hash))]

    @property
    def hash(self):
        return self.block.hash

    @property
    def hexhash(self):
        return h2h(self.hash)

    @property
    def prev(self):
        return self.block.prev

    @staticmethod
    def iter_blocks():
        i = 0
        while True:
            try:
                yield Block.get_by_number(i)
            except KeyError:
                raise StopIteration
            i += 1

    @staticmethod
    def get_by_hash(h):
        log.debug("getting block %s", h2h(h))
        return Block.frombinary(chain.get(h))[0]

    @staticmethod
    def get_by_number(num):
        h = blknums.get(str(num))
        return Block.get_by_hash(h)

    @staticmethod
    def exist_by_hash(h):
        return chain.has_key(h)

    @staticmethod
    def exist_by_number(num):
        return blknums.has_key(str(num))

    @staticmethod
    def get_or_make(blkmsg):
        if not Block.exist_by_hash(blkmsg.hash):
            blk = Block.make(blkmsg)
            for txmsg in blkmsg.txs:
                tx = transactions.Tx.get_or_make(txmsg)
                if tx: tx.put()
            blk.link()
            blk.put()
        else:
            blk = Block.get_by_hash(blkmsg.hash)
        return blk

    @txn_required
    def put(self):
        if self.chain == ORPHAN_CHAIN:
            log.debug("trying to put orphan block in db? (bug?)")
            return False
        log.debug("putting block %s", h2h(self.hash))
        chain.put(self.hash, self.tobinary())
        if self.chain == MAIN_CHAIN:
            blknums.put(str(self.number), self.hash)

    def get_tx(self, idx):
        return transactions.Tx.get_by_hash(self.txs[idx])

    def iter_tx(self, from_idx=0, to_idx=None, reverse=False, enum=False):
        sl = list(enumerate(self.txs[from_idx:to_idx], start=from_idx))
        if reverse: sl.reverse()
        if enum:
            for blkidx, tx_h in sl:
                yield (blkidx, transactions.Tx.get_by_hash(tx_h))
        else:
            for blkidx, tx_h in sl:
                yield transactions.Tx.get_by_hash(tx_h)

    def get_prev(self):
        return Block.get_by_hash(self.prev)

    def get_next_bits(self):
        targettimespan = 14 * 24 * 60 * 60  # 2 weeks in secounds
        spacing = 10 * 60  # 10 min in secounds
        interval = targettimespan / spacing  # 2 weeks of blocks(2016)
        if (self.number + 1) % interval != 0:
            return self.block.bits
        log.info("DIFF: retarget, current bits: %s", hex(self.block.bits))
        first_blk = self
        for i in range(interval - 1):
            first_blk = first_blk.get_prev()
        realtimespan = self.block.time - first_blk.block.time
        log.info("DIFF: timespan before limits: %d", realtimespan)
        if realtimespan < targettimespan / 4: realtimespan = targettimespan / 4
        if realtimespan > targettimespan * 4: realtimespan = targettimespan * 4
        log.info("DIFF: timespan after limits: %d", realtimespan)
        newtarget = (bits_to_target(self.block.bits) *
                     realtimespan) / targettimespan
        if newtarget > bits_to_target(0x1d00ffff):
            newtarget = bits_to_target(0x1d00ffff)
        bits = target_to_bits(newtarget)
        log.info("DIFF: next bits: %s", hex(bits))
        return bits

    def verify(self):
        if self.chain == INVALID_CHAIN:
            return False
        if not check_bits(self.block.bits, self.hash):
            return False
        prev_blk = self.get_prev()
        if prev_blk.chain == INVALID_CHAIN:
            return False
        if prev_blk.get_next_bits() != self.block.bits:
            return False
        return True

    @txn_required
    def confirm(self):
        log.info("confirming block %s(%d)", h2h(self.hash), self.number)
        if not self.verify():
            raise Invalidblock()
        if not set_bestblock(self):
            log.info("block %s(%d) not good", h2h(self.hash), self.number)
            return False
        self.chain = MAIN_CHAIN
        self.put()
        run_hooks(settings.BLOCK_CONFIRM_HOOKS, self)

    @txn_required
    def revert(self):
        log.info("reverting block %s(%d)", h2h(self.hash), self.number)
        set_bestblock(self.get_prev(), check=False)
        if self.chain != MAIN_CHAIN:
            log.debug("reverting a block, that is not in main chain? (bug?)")
        self.chain = SIDE_CHAIN
        self.put()
        run_hooks(settings.BLOCK_REVERT_HOOKS, self)

    @txn_required
    def invalidate(self):
        self.chain = CHAIN_INVALID
        self.put()
        blocks_to_invalidate = set(self.nexts)
        while blocks_to_invalidate:
            blk_h = blocks_to_invalidate.pop()
            blk = Block.get_by_hash(blk_h)
            blk.chain = CHAIN_INVALID
            blk.put()
            blocks_to_invalidate.update(blk.nexts)

    @txn_required
    def link(self):
        prev = self.get_prev()
        prev.nexts.append(self.hash)
        prev.put()
        if prev.chain == MAIN_CHAIN:
            self.chain = SIDE_CHAIN
        else:
            self.chain = prev.chain
        if not self.verify():
            self.invalidate()
        self.number = prev.number + 1
        self.totaldiff = prev.totaldiff + bits_to_diff(self.block.bits)
        self.put()

    def get_all_fees(self):
        return sum(tx.get_fee() for tx in self.iter_tx())

    def changes_since(self):
        bestblock = get_bestblock()
        split, reverted, confirmed = find_split(self, bestblock)
        result = {}
        result["reverted"] = [(blk.hash, [tx_h for tx_h in blk.txs])
                              for blk in reverted]
        result["confirmed"] = [(blk.hash, [tx_h for tx_h in blk.txs])
                               for blk in confirmed]
        return result

    def __repr__(self):
        return "<Block %s(%d) - diff: %d - chain: %s, txs: %s>" % (
            self.hexhash, self.number, bits_to_diff(self.block.bits),
            _chains.get(self.chain, "unknown(BUG)"), len(self.txs))

    @constructor
    def make(self, blockmsg):
        self.block, self.txs = blockmsg.block, [tx.hash for tx in blockmsg.txs]
        self.number, self.totaldiff, self.chain, self.nexts = 0, 0, ORPHAN_CHAIN, []

    def tonetwork(self):
        return msgs.Blockmsg(self.block, list(self.iter_tx()))
コード例 #14
0
class KeyEntry(bs.Entity, js.Entity):
    bfields = [
        ("privatkey", bs.VarBytes),
        ("publickey", bs.VarBytes),
        ("txs", bs.VarList(bs.Hash))
    ]
    fields = {
        "privatkey": js.Bytes,
        "publickey": js.Bytes,
        "txs": js.List(js.Hash)
    }
    @property
    def hash(self):
        return hash160(self.publickey)
        
    @staticmethod
    def iter_keys(txn=None):
        try:
            cur = keychain.cursor(txn=txn)
            while True:
                try:
                    h, data = cur.next()
                except KeyError:
                    break
                yield KeyEntry.frombinary(data)[0]
        finally:
            cur.close()
            
    def __init_key(self):
        self._key = ec.Key.from_privkey(self.privatkey)
    
    @property
    def bitcoinaddress(self):
        return hash2addr(self.hash)
        
    @staticmethod
    def get_by_hash(h, txn=None):
        ret = KeyEntry.frombinary(keychain.get(h, txn=txn))[0]
        ret.__init_key()
        return ret
        
    @staticmethod
    def get_by_publickey(key, txn=None):
        return KeyEntry.get_by_hash(hash160(key), txn=txn)
    
    def tosecret(self):
        secret = "\x80" + self._key.get_secret()
        if self._key.get_compressed():
            secret += "\x01"
        secret = secret+doublesha(secret)[:4]
        return b58encode(secret)
        
    @classmethod    
    def fromsecret(cls, secret):
        self = cls()
        secret = b58decode(secret, None)
        secret, cksum = secret[:-4], secret[-4:]
        if doublesha(secret)[:4] != cksum:
            return None
        valid, secret, compressed = secret[0]=="\x80", secret[1:33], secret[33:] == "\x01" 
        if not valid:
            return None
        self._key = ec.Key.generate(secret, compressed)
        self.privatkey = self._key.get_privkey()
        self.publickey = self._key.get_pubkey() 
        self.txs = []
        return self
        
    @classmethod
    def generate(cls):
        self = cls()
        self._key = ec.Key.generate()
        self.privatkey = self._key.get_privkey()
        self.publickey = self._key.get_pubkey() 
        self.txs = []
        return self
        
    @txn_required
    def put(self, txn=None):
        keychain.put(self.hash, self.tobinary(), txn=txn)