<commit_before>import obelisk
from twisted.internet import reactor

def build_output_info_list(unspent_rows):
    unspent_infos = []
    for row in unspent_rows:
        assert len(row) == 4
        outpoint = obelisk.OutPoint()
        outpoint.hash = row[0]
        outpoint.index = row[1]
        value = row[3]
        unspent_infos.append(
            obelisk.OutputInfo(outpoint, value))
    return unspent_infos

class Multisig:

    def __init__(self, client, number_required, pubkeys):
        if number_required > len(pubkeys):
            raise Exception("number_required > len(pubkeys)")
        self.client = client
        self.number_required = number_required
        self.pubkeys = pubkeys

    @property
    def script(self):
        result = chr(80 + self.number_required)
        for pubkey in self.pubkeys:
            result += chr(33) + pubkey
        result += chr(80 + len(self.pubkeys))
        # checkmultisig
        result += "\xae"
        return result

    @property
    def address(self):
        raw_addr = obelisk.hash_160(self.script)
        return obelisk.hash_160_to_bc_address(raw_addr, addrtype=0x05)

    def create_unsigned_transaction(self, destination, finished_cb):
        def fetched(ec, history):
            if ec is not None:
                print >> sys.stderr, "Error fetching history:", ec
                return
            self._fetched(history, destination, finished_cb)
        self.client.fetch_history(self.address, fetched)

    def _fetched(self, history, destination, finished_cb):
        unspent = [row[:4] for row in history if row[4] is None]
        tx = self._build_actual_tx(unspent, destination)
        finished_cb(tx)

    def _build_actual_tx(self, unspent, destination):
        # Send all unspent outputs (everything in the address) minus the fee
        tx = obelisk.Transaction()
        total_amount = 0
        for row in unspent:
            assert len(row) == 4
            outpoint = obelisk.OutPoint()
            outpoint.hash = row[0]
            outpoint.index = row[1]
            value = row[3]
            total_amount += value
            add_input(tx, outpoint)
        # Constrain fee so we don't get negative amount to send
        fee = min(total_amount, 10000)
        send_amount = total_amount - fee
        add_output(tx, destination, send_amount)
        return tx

    def sign_all_inputs(self, tx, secret):
        signatures = []
        key = obelisk.EllipticCurveKey()
        key.set_secret(secret)
        for i, input in enumerate(tx.inputs):
            sighash = generate_signature_hash(tx, i, self.script)
            # Add sighash::all to end of signature.
            signature = key.sign(sighash) + "\x01"
            signatures.append(signature)
        return signatures

def add_input(tx, prevout):
    input = obelisk.TxIn()
    input.previous_output.hash = prevout.hash
    input.previous_output.index = prevout.index
    tx.inputs.append(input)

def add_output(tx, address, value):
    output = obelisk.TxOut()
    output.value = value
    output.script = obelisk.output_script(address)
    tx.outputs.append(output)

def generate_signature_hash(parent_tx, input_index, script_code):
    tx = obelisk.copy_tx(parent_tx)
    if input_index >= len(tx.inputs):
        return None
    for input in tx.inputs:
        input.script = ""
    tx.inputs[input_index].script = script_code
    raw_tx = tx.serialize() + "\x01\x00\x00\x00"
    return obelisk.Hash(raw_tx)

class Escrow:

    def __init__(self, buyer_pubkey, seller_pubkey, arbit_pubkey):
        pubkeys = (buyer_pubkey, seller_pubkey, arbit_pubkey)
        self.multisig = Multisig(2, pubkeys)

    @property
    def deposit_address(self):
        return self.multisig.address

    def release_funds(self, value):
        # Returns signed transaction
        pass

def main():
    pubkeys = [
        "035b175132eeb8aa6e8455b6f1c1e4b2784bea1add47a6ded7fc9fc6b7aff16700".decode("hex"),
        "<KEY>".decode("hex"),
        "<KEY>".decode("hex")
    ]
    client = obelisk.ObeliskOfLightClient("tcp://85.25.198.97:9091")
    msig = Multisig(client, 2, pubkeys)
    print msig.address
    def finished(tx):
        print tx<commit_msg>multisig escrow working<commit_after>import obelisk
from twisted.internet import reactor

# Create new private key:
#
#   $ sx newkey > key1
#
# Show private secret:
#
#   $ cat key1 | sx wif-to-secret
#
# Show compressed public key:
#
#   $ cat key1 | sx pubkey
#
# You will need 3 keys for buyer, seller and arbitrer

def build_output_info_list(unspent_rows):
    unspent_infos = []
    for row in unspent_rows:
        assert len(row) == 4
        outpoint = obelisk.OutPoint()
        outpoint.hash = row[0]
        outpoint.index = row[1]
        value = row[3]
        unspent_infos.append(
            obelisk.OutputInfo(outpoint, value))
    return unspent_infos

class Multisig:

    def __init__(self, client, number_required, pubkeys):
        if number_required > len(pubkeys):
            raise Exception("number_required > len(pubkeys)")
        self.client = client
        self.number_required = number_required
        self.pubkeys = pubkeys

    @property
    def script(self):
        result = chr(80 + self.number_required)
        for pubkey in self.pubkeys:
            result += chr(33) + pubkey
        result += chr(80 + len(self.pubkeys))
        # checkmultisig
        result += "\xae"
        return result

    @property
    def address(self):
        raw_addr = obelisk.hash_160(self.script)
        return obelisk.hash_160_to_bc_address(raw_addr, addrtype=0x05)

    def create_unsigned_transaction(self, destination, finished_cb):
        def fetched(ec, history):
            if ec is not None:
                print >> sys.stderr, "Error fetching history:", ec
                return
            self._fetched(history, destination, finished_cb)
        self.client.fetch_history(self.address, fetched)

    def _fetched(self, history, destination, finished_cb):
        unspent = [row[:4] for row in history if row[4] is None]
        tx = self._build_actual_tx(unspent, destination)
        finished_cb(tx)

    def _build_actual_tx(self, unspent, destination):
        # Send all unspent outputs (everything in the address) minus the fee
        tx = obelisk.Transaction()
        total_amount = 0
        for row in unspent:
            assert len(row) == 4
            outpoint = obelisk.OutPoint()
            outpoint.hash = row[0]
            outpoint.index = row[1]
            value = row[3]
            total_amount += value
            add_input(tx, outpoint)
        # Constrain fee so we don't get negative amount to send
        fee = min(total_amount, 10000)
        send_amount = total_amount - fee
        add_output(tx, destination, send_amount)
        return tx

    def sign_all_inputs(self, tx, secret):
        signatures = []
        key = obelisk.EllipticCurveKey()
        key.set_secret(secret)
        for i, input in enumerate(tx.inputs):
            sighash = generate_signature_hash(tx, i, self.script)
            # Add sighash::all to end of signature.
            signature = key.sign(sighash) + "\x01"
            signatures.append(signature)
        return signatures

def add_input(tx, prevout):
    input = obelisk.TxIn()
    input.previous_output.hash = prevout.hash
    input.previous_output.index = prevout.index
    tx.inputs.append(input)

def add_output(tx, address, value):
    output = obelisk.TxOut()
    output.value = value
    output.script = obelisk.output_script(address)
    tx.outputs.append(output)

def generate_signature_hash(parent_tx, input_index, script_code):
    tx = obelisk.copy_tx(parent_tx)
    if input_index >= len(tx.inputs):
        return None
    for input in tx.inputs:
        input.script = ""
    tx.inputs[input_index].script = script_code
    raw_tx = tx.serialize() + "\x01\x00\x00\x00"
    return obelisk.Hash(raw_tx)

class Escrow:

    def __init__(self, client, buyer_pubkey, seller_pubkey, arbit_pubkey):
        pubkeys = (buyer_pubkey, seller_pubkey, arbit_pubkey)
        self.multisig = Multisig(client, 2, pubkeys)

    # 1. BUYER: Deposit funds for seller
    @property
    def deposit_address(self):
        return self.multisig.address

    # 2. BUYER: Send unsigned tx to seller
    def initiate(self, destination_address, finished_cb):
        self.multisig.create_unsigned_transaction(
            destination_address, finished_cb)

    # ...
    # 3. BUYER: Release funds by sending signature to seller
    def release_funds(self, tx, secret):
        return self.multisig.sign_all_inputs(tx, secret)

    # 4. SELLER: Claim your funds by generating a signature.
    def claim_funds(self, tx, secret, buyer_sigs):
        seller_sigs = self.multisig.sign_all_inputs(tx, secret)
        return Escrow.complete(tx, buyer_sigs, seller_sigs,
                               self.multisig.script)

    @staticmethod
    def complete(tx, buyer_sigs, seller_sigs, script_code):
        for i, input in enumerate(tx.inputs):
            sigs = (buyer_sigs[i], seller_sigs[i])
            script = "\x00"
            for sig in sigs:
                script += chr(len(sig)) + sig
            script += "\x4c"
            assert len(script_code) < 255
            script += chr(len(script_code)) + script_code
            tx.inputs[i].script = script
        return tx

def main():
    ##########################################################
    # ESCROW TEST
    ##########################################################
    pubkeys = [
        "035b175132eeb8aa6e8455b6f1c1e4b2784bea1add47a6ded7fc9fc6b7aff16700".decode("hex"),
        "<KEY>".decode("hex"),
        "<KEY>".decode("hex")
    ]
    client = obelisk.ObeliskOfLightClient("tcp://192.168.127.127:9091")
    escrow = Escrow(client, pubkeys[0], pubkeys[1], pubkeys[2])
    def finished(tx):
        buyer_sigs = escrow.release_funds(tx,
            "b28c7003a7b6541cd1cd881928863abac0eff85f5afb40ff5561989c9fb95fb2".decode("hex"))
        completed_tx = escrow.claim_funds(tx,
            "5b05667dac199c48051932f14736e6f770e7a5917d2994a15a1508daa43bc9b0".decode("hex"),
            buyer_sigs)
        print completed_tx.serialize().encode("hex")
    escrow.initiate("1Fufjpf9RM2aQsGedhSpbSCGRHrmLMJ7yY", finished)
    reactor.run()
    return
    ##########################################################
    # MULTISIGNATURE TEST
    ##########################################################
    msig = Multisig(client, 2, pubkeys)
    print msig.address
    def finished(tx):
        print tx
{'fraction_non_alphanumeric': 0.1170142493158441, 'fraction_numerical': 0.027460602057185996, 'mean_word_length': 2.5864636209813874, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1139315', 'n_tokens_mistral': 3517, 'n_tokens_neox': 3323, 'n_words': 737}