def open(secp: Secp256k1, location: str):
     abs_dir = absolute(location)
     assert os.path.isdir(abs_dir)
     seed_file = absolute(abs_dir, "wallet.seed")
     assert os.path.exists(seed_file)
     f = open(seed_file, "r")
     wallet = Wallet(secp, location, unhexlify(f.readline().encode()))
     wallet.load()
     return wallet
 def __init__(self, secp: Secp256k1, location: str, seed: bytes):
     self.dir_in = location
     self.dir = absolute(location)
     assert os.path.isdir(self.dir)
     self.chain = Keychain.from_seed(secp, seed)
     self.details = WalletDetails(absolute(self.dir, "wallet.det"))
     self.cache = {}
     self.outputs = {}
     if not os.path.exists(absolute(self.dir, "wallet.dat")):
         self.save()
     else:
         self.load()
 def create(secp: Secp256k1, location: str, seed: bytes):
     assert len(seed) == 32, "Invalid seed length"
     abs_dir = absolute(location)
     if not os.path.exists(abs_dir):
         os.makedirs(abs_dir)
     else:
         assert os.path.isdir(abs_dir), "Wallet location not a directory"
     seed_file = absolute(abs_dir, "wallet.seed")
     assert not os.path.exists(seed_file), "Wallet already exists"
     f = open(seed_file, "x")
     f.write(hexlify(seed).decode())
     f.close()
     return Wallet(secp, location, seed)
 def save(self):
     self.details.save()
     lst = [y.to_dict() for x, y in self.outputs.items()]
     lst = sorted(lst, key=lambda x: x['n_child'])
     f = open(absolute(self.dir, "wallet.dat"), "w")
     f.write(json.dumps(lst, indent=2))
     f.close()
예제 #5
0
    def __init__(self, secp: Secp256k1, role: Role, id: UUID):
        self.secp = secp
        self.role = role
        self.id = id
        self.swap_file = absolute(
            "swap_data", "sell" if self.role is Role.SELLER else "buy",
            "{}.json".format(str(id)))

        # Default values
        self.stage = self.wallet = self.grin_amount = self.swap_currency = self.swap_amount \
            = self.swap_receive_address = self.swap_cosign = self.public_swap_cosign = self.lock_height \
            = self.refund_lock_height = self.input_entries = self.inputs = self.fee_amount = self.refund_fee_amount \
            = self.input_amount = self.change_amount = self.change_entry = self.change_child = self.change_output \
            = self.partial_entry = self.partial_child = self.partial_commit = self.offset = self.public_excess \
            = self.refund_entry = self.refund_child = self.refund_output = self.refund_offset = self.nonce \
            = self.public_nonce = self.refund_nonce = self.public_refund_nonce = self.foreign_partial_commit \
            = self.foreign_public_nonce = self.foreign_public_refund_nonce = self.secret_lock \
            = self.public_lock = self.eth_address_lock = self.eth_contract_address = self.btc_lock_time \
            = self.btc_refund_key = self.public_btc_refund_key = self.btc_lock_address = self.commit \
            = self.public_refund_excess = self.partial_signature = self.partial_refund_signature = self.t_1 = self.t_2 \
            = self.foreign_partial_signature = self.foreign_partial_refund_signature = self.foreign_t_1 \
            = self.foreign_t_2 = self.tau_x = self.foreign_tau_x = self.range_proof = self.refund_tx = self.tx \
            = self.tx_height = self.btc_output_points = self.swap_nonce = self.public_swap_nonce \
            = self.foreign_public_swap_nonce = self.swap_entry = self.swap_child = self.swap_fee_amount \
            = self.swap_lock_height = self.swap_output = self.swap_offset = self.public_swap_excess \
            = self.partial_swap_signature = self.partial_swap_adaptor \
            = self.foreign_partial_swap_adaptor = self.foreign_partial_swap_signature = self.swap_tx \
            = self.claim = None
        self.time_start = int(time())

        if os.path.exists(self.swap_file):
            self.load()
 def load(self):
     self.details.load()
     f = open(absolute(self.dir, "wallet.dat"), "r")
     entries = json.loads(f.read())
     f.close()
     outputs = {}
     for entry in entries:
         key_id = entry['key_id']
         if key_id in self.outputs:
             output = self.outputs[key_id]
             output.update_from_dict(entry)
             outputs[key_id] = output
         outputs[key_id] = OutputEntry.from_dict(entry)
     self.outputs = outputs
예제 #7
0
def buy(file=None):
    assert file is None or isinstance(file, str), "Invalid argument"

    secp = Secp256k1(None, FLAG_ALL)

    print("################################")

    if file is None:
        print("# BTC/ETH -> grin atomic swap\n# ")
        print(
            "# This script is used for buying grin coins with Bitcoin or Ether through an atomic swap\n"
            "# The seller initiates the process, wait for them to send you a file and run './swap buy <file.json>"
        )
        return

    f = open(absolute(file), "r")
    dct = json.loads(f.read())
    f.close()

    if dct['target'] != "buyer":
        print("# This file is not meant for me")
        return

    id = UUID.from_str(dct['id'])
    swap_file = absolute("swap_data", "buy", "{}.json".format(str(id)))

    swap = AtomicSwap(secp, Role.BUYER, id)

    if not os.path.exists(swap_file):
        if dct['stage'] != Stage.INIT.value:
            print("# Unexpected transaction stage")
            return

        if dct['swap_currency'] != "BTC" and dct['swap_currency'] != "ETH":
            print("# Unsupported currency {}".format(dct['swap_currency']))
            return

        print("# {} -> grin atomic swap\n# ".format(dct['swap_currency']))

        normalize_unit = BITCOIN_UNIT if dct[
            'swap_currency'] == "BTC" else ETHER_UNIT

        print(
            "# You are about to start the process of buying {} grin for {} {}".
            format(dct['grin_amount'] / GRIN_UNIT,
                   dct['swap_amount'] / normalize_unit, dct['swap_currency']))

        if input("# Are you sure? [Y/n]: ") not in ["", "Y", "y"]:
            print("# You declined the offer")
            return

        wallet_dir = input(
            "# What is the name of the wallet you want to use? ")
        if not os.path.isdir(wallet_dir):
            print("# Wallet directory not found")
            return
        dct['wallet'] = wallet_dir

        swap.receive(dct)

        last_block = float(
            input("# What is the height of the last Grin T4 block? "))
        assert last_block > 0 and math.isfinite(last_block), "Invalid input"

        print("# ")
        diff = swap.lock_height - last_block
        diff_hour = int(abs(diff) / 60)
        diff_min = int(abs(diff) % 60)
        if diff >= 10 or diff <= -60:
            print(
                "#\n"
                "# WARNING: transaction unlocks ~{}h{}m minutes in the {} (height {})\n"
                "# ".format(diff_hour, diff_min,
                            "future" if diff > 0 else "past",
                            swap.lock_height))

        diff_refund = swap.refund_lock_height - last_block
        diff_refund_hour = int(diff_refund / 60)
        diff_refund_min = int(diff_refund % 60)
        if diff_refund < 360:
            print(
                "# Refund unlocks less than 6 hours from now (~{}h{}m, height {})"
                .format(diff_refund_hour, diff_refund_min,
                        swap.refund_lock_height))
            return

        print("# Refund unlocks in ~{}h{}m (height {})\n"
              "# ".format(diff_refund_hour, diff_refund_min,
                          swap.refund_lock_height))

        if swap.is_bitcoin_swap():
            print("# Please send (at least) {} tBTC to {}".format(
                swap.swap_amount / BITCOIN_UNIT,
                swap.btc_lock_address.to_base58check().decode()))
            input("# When you are done, press Enter: ")

        if swap.is_ether_swap():
            print(
                "# Please deploy the GrinSwap.sol contract on the Ethereum Ropsten testnet"
                "with the following arguments:\n"
                "#   _sign_address = {}\n"
                "#   _receive_address = {}\n"
                "# and deposit (at least) {} ETH in it".format(
                    swap.eth_address_lock, swap.swap_receive_address,
                    swap.swap_amount / ETHER_UNIT))

            eth_contract_address = input(
                "# When you are done, enter the contract address: ")
            assert is_eth_address(eth_contract_address), "Invalid input"
            swap.eth_contract_address = eth_contract_address

        swap.fill_signatures()
    else:
        print("# {} -> grin atomic swap\n# ".format(swap.swap_currency))

        if dct['stage'] != swap.stage.value + 1:
            print("# Unexpected stage")
            return

        diff = int((time() - swap.time_start) / 60)
        diff_hour = int(abs(diff) / 60)
        diff_min = int(abs(diff) % 60)
        if diff_hour >= 6:
            print("# WARNING: this swap was initiated {}h{}m ago".format(
                diff_hour, diff_min))
            if input("# Do you want to continue? [Y/n]: ") not in [
                    "", "Y", "y"
            ]:
                return
            print("# ")

        swap.receive(dct)

        if swap.stage == Stage.SIGN:
            swap.finalize_range_proof()
        elif swap.stage == Stage.LOCK:
            print(
                "# The seller claims the multisig output {} was mined in block {}"
                .format(swap.commit.to_hex(secp).decode(), swap.tx_height))
            if input(
                    "# Does this block (or another) contain the output? [Y/n]: "
            ) not in ["", "Y", "y"]:
                print(
                    "# You can rerun this command when the output has been mined"
                )
                return
            swap.prepare_swap()
        elif swap.stage == Stage.SWAP:
            swap.finalize_swap()

            swap_name = "{}_swap_tx.json".format(swap.short_id())
            swap_tx_wrapper = {"tx_hex": swap.swap_tx.to_hex(secp).decode()}
            f = open(swap_name, "w")
            f.write(json.dumps(swap_tx_wrapper, indent=2))
            f.close()

            print(
                "# Transaction written to {}, please submit it to a node to claim your Grin"
                .format(swap_name))
            input("# Press [Enter] when the transaction has been mined: ")
            print("# \n" "# Congratulations, you completed the swap!")
        else:
            print("# Not sure what to do")
            return

    swap.save()

    out_name = "{}_buyer_{}.json".format(swap.short_id(), swap.stage.value)
    f = open(absolute(out_name), "x")
    f.write(json.dumps(swap.send(), indent=2))
    f.close()

    print("# \n"
          "# Created file '{}', please send it to the seller\n"
          "################################".format(out_name))
예제 #8
0
def sell(file=None):
    assert file is None or isinstance(file, str), "Invalid argument"

    secp = Secp256k1(None, FLAG_ALL)

    print("################################")

    swap = None
    if file is None:
        print("# Grin -> BTC/ETH atomic swap\n# ")

        id = UUID.random()
        swap = AtomicSwap(secp, Role.SELLER, id)

        print(
            "# This script is used for selling grin coins for Bitcoin or Ether through an atomic swap\n"
            "# ")

        wallet_dir = input(
            "# What is the name of the wallet you want to use? ")
        if not os.path.isdir(wallet_dir):
            print("# Wallet directory not found")
            return
        swap.wallet = Wallet.open(secp, wallet_dir)

        grin_amount = float(input("# How much grin do you want to sell? "))
        assert grin_amount > 0 and math.isfinite(grin_amount), "Invalid input"
        swap.grin_amount = int(grin_amount * GRIN_UNIT)

        currency = input(
            "# Which currency would you like to receive? [BTC/ETH]: ")
        if currency != "BTC" and currency != "ETH":
            print("# Unknown currency, please enter BTC or ETH")
            return
        swap.swap_currency = currency

        swap_amount = float(
            input("# How much {} do you want to receive? ".format(currency)))
        assert swap_amount > 0 and math.isfinite(swap_amount), "Invalid input"
        if swap.is_bitcoin_swap():
            swap.swap_amount = int(swap_amount * BITCOIN_UNIT)
            swap_receive_address = input(
                "# At which Bitcoin address would you like to receive this? ")
            assert is_btc_address(swap_receive_address, False), "Invalid input"
        elif swap.is_ether_swap():
            swap.swap_amount = int(swap_amount * ETHER_UNIT)
            swap_receive_address = input(
                "# At which Ethereum address would you like to receive this? ")
            assert is_eth_address(swap_receive_address), "Invalid input"
        else:
            print("# Unknown swap currency")
            return

        swap.swap_receive_address = swap_receive_address

        last_block = float(
            input("# What is the height of the last Grin T4 block? "))
        assert last_block > 0 and math.isfinite(last_block), "Invalid input"
        swap.lock_height = int(last_block)
        swap.refund_lock_height = swap.lock_height + 720  # ~12h

        try:
            swap.select_inputs()
        except NotEnoughFundsException:
            print("# Not enough funds available!")
            return
    else:
        f = open(absolute(file), "r")
        dct = json.loads(f.read())
        f.close()

        if dct['target'] != "seller":
            print("# This file is not meant for me")
            return

        id = UUID.from_str(dct['id'])
        swap_file = absolute("swap_data", "sell", "{}.json".format(str(id)))
        if not os.path.exists(swap_file):
            print("# Swap file not found")
            return
        swap = AtomicSwap(secp, Role.SELLER, id)

        print("# Grin -> {} atomic swap\n# ".format(swap.swap_currency))

        if dct['stage'] != swap.stage.value:
            print("# Unexpected stage")
            return

        diff = int((time() - swap.time_start) / 60)
        diff_hour = int(abs(diff) / 60)
        diff_min = int(abs(diff) % 60)
        if diff_hour >= 6:
            print("# WARNING: this swap was initiated {}h{}m ago".format(
                diff_hour, diff_min))
            if input("# Do you want to continue? [Y/n]: ") not in [
                    "", "Y", "y"
            ]:
                return
            print("# ")

        swap.receive(dct)

        if swap.stage == Stage.SIGN:
            if swap.is_bitcoin_swap():
                print(
                    "# Check that the balance of address {} is at least {} tBTC, with enough confirmations"
                    .format(swap.btc_lock_address.to_base58check().decode(),
                            swap.swap_amount / BITCOIN_UNIT))

                if input("# Is this the case? [Y/n]: ") not in ["", "Y", "y"]:
                    print(
                        "# Please rerun this script when the address has enough balance"
                    )
                    return

                utxo_count = int(
                    input("# How many UTXOs does the address have? "))
                assert utxo_count > 0 and math.isfinite(
                    utxo_count), "Invalid input"
                print("# Output point format: TXID:index")
                swap.btc_output_points = []
                for i in range(utxo_count):
                    output_point = input(
                        "#   Insert output point for UTXO #{}: ".format(i))
                    split = output_point.split(":")
                    assert len(split) == 2, "Invalid input"
                    output_txid = split[0]
                    assert is_btc_txid(
                        output_txid), "Invalid output point TXID"
                    output_index = int(split[1])
                    assert output_index >= 0 and math.isfinite(
                        output_index), "Invalid output point index"
                    swap.btc_output_points.append(
                        OutputPoint(TXID.from_hex(output_txid.encode()),
                                    output_index))

            if swap.is_ether_swap():
                print("# Check the ETH Ropsten contract at {} for\n"
                      "#   balance of at least {} ETH\n"
                      "#   sign_address = {}\n"
                      "#   receive_address = {}\n"
                      "#   unlock_time far enough in the future (>18h)"
                      "#   enough confirmations".format(
                          swap.eth_contract_address,
                          swap.swap_amount / ETHER_UNIT, swap.eth_address_lock,
                          swap.swap_receive_address))

                if input(
                        "# Does the contract fulfil these requirements? [Y/n]: "
                ) not in ["", "Y", "y"]:
                    print(
                        "# The buyer tried to scam you, but we caught it in time!"
                    )
                    return

            swap.fill_signatures()
        elif swap.stage == Stage.LOCK:
            swap.build_transactions()

            name = "{}_multisig_tx.json".format(swap.short_id())
            tx_wrapper = {"tx_hex": swap.tx.to_hex(secp).decode()}
            f = open(name, "w")
            f.write(json.dumps(tx_wrapper, indent=2))
            f.close()

            print("# Transaction written to {}, please submit it to a node".
                  format(name))

            tx_height = float(
                input(
                    "# Enter the height of the block containing the transaction: "
                ))
            assert tx_height > 0 and math.isfinite(tx_height), "Invalid input"
            swap.tx_height = int(tx_height)

            refund_name = "{}_refund_tx_lock{}.json".format(
                swap.short_id(), swap.refund_lock_height)
            refund_tx_wrapper = {
                "tx_hex": swap.refund_tx.to_hex(secp).decode()
            }
            f = open(refund_name, "w")
            f.write(json.dumps(refund_tx_wrapper, indent=2))
            f.close()

            print(
                "# Refund transaction written to {}, can be submitted at height {}"
                .format(refund_name, swap.refund_lock_height))
        elif swap.stage == Stage.SWAP:
            swap.fill_swap_signatures()
        elif swap.stage == Stage.DONE:
            swap.finalize_swap()

            if swap.is_bitcoin_swap():
                btc_swap_name = "{}_btc_swap_tx.hex".format(swap.short_id())
                f = open(btc_swap_name, "w")
                f.write(swap.claim.decode())
                f.close()

                print(
                    "# The buyer has claimed their Grin!\n"
                    "# \n"
                    "# Bitcoin transaction written to {}, submit it to a node\n"
                    "# This will give you the testnet BTC and complete the swap. Congratulations!\n"
                    "################################".format(btc_swap_name))

            if swap.is_ether_swap():
                r, s, v = ethereum_signature(swap.claim)
                print(
                    "# The buyer has claimed their Grin!\n"
                    "# \n"
                    "# Submit a transaction to contract {}, 'claim' method, with the following arguments:\n"
                    "#   r = {}\n"
                    "#   s = {}\n"
                    "#   v = {}\n"
                    "# This will give you the Ropsten ETH and complete the swap. Congratulations!\n"
                    "################################".format(
                        swap.eth_contract_address, r.decode(), s.decode(), v))

            return

    swap.save()

    out_name = "{}_seller_{}.json".format(swap.short_id(), swap.stage.value)
    f = open(absolute(out_name), "x")
    f.write(json.dumps(swap.send(), indent=2))
    f.close()

    print("# \n"
          "# Created file '{}', please send it to the buyer\n"
          "################################".format(out_name))
예제 #9
0
import json
import math
import os
from time import time
from secp256k1 import FLAG_ALL
from secp256k1.pedersen import Secp256k1, ethereum_signature
from grin.btc import TXID, OutputPoint, Address as BitcoinAddress
from grin.swap import AtomicSwap, Role, Stage, is_eth_address, is_btc_address, is_btc_txid
from grin.util import GRIN_UNIT, UUID, absolute
from grin.wallet import Wallet, NotEnoughFundsException

ETHER_UNIT = 1000000000000000000
BITCOIN_UNIT = 100000000

if not os.path.isdir(absolute("swap_data")):
    os.mkdir(absolute("swap_data"))
    f = open(absolute("swap_data", "_DONT_PUBLISH_THESE_FILES_"), "w")
    f.write("Seriously, dont")
    f.close()
if not os.path.isdir(absolute("swap_data", "sell")):
    os.mkdir(absolute("swap_data", "sell"))
if not os.path.isdir(absolute("swap_data", "buy")):
    os.mkdir(absolute("swap_data", "buy"))


def sell(file=None):
    assert file is None or isinstance(file, str), "Invalid argument"

    secp = Secp256k1(None, FLAG_ALL)

    print("################################")
예제 #10
0
 def save(self):
     f = open(absolute(self.swap_file), "w")
     dct = self.to_dict(True)
     f.write(json.dumps(dct, indent=2))
     f.close()
예제 #11
0
    def load(self, dct=None):
        seller = self.role == Role.SELLER
        buyer = not seller

        from_file = dct is None
        if from_file:
            f = open(absolute(self.swap_file), "r")
            dct = json.loads(f.read())
            f.close()

        self.stage = Stage(dct['stage'])
        if self.wallet is None:
            self.wallet = Wallet.open(self.secp, dct['wallet'])
        self.time_start = int(dct['time_start'])
        self.grin_amount = int(dct['grin_amount'])
        self.swap_currency = dct['swap_currency']
        self.swap_amount = int(dct['swap_amount'])
        if seller or self.is_ether_swap():
            self.swap_receive_address = dct['swap_receive_address']
        self.lock_height = int(dct['lock_height'])
        self.refund_lock_height = int(dct['refund_lock_height'])
        self.inputs = [
            Input.from_dict(self.secp, x, True) for x in dct['inputs']
        ]
        self.fee_amount = int(dct['fee_amount'])
        self.refund_fee_amount = int(dct['refund_fee_amount'])
        self.change_output = Output.from_dict(self.secp, dct['change_output'],
                                              True)
        if from_file:
            self.partial_entry = self.wallet.get_output(dct['partial_entry'])
            self.partial_child = self.wallet.derive_from_entry(
                self.partial_entry)
        self.partial_commit = self.wallet.commit_with_child_key(
            0, self.partial_child)
        self.offset = BlindingFactor.from_hex(dct['offset'].encode())
        self.refund_output = Output.from_dict(self.secp, dct['refund_output'],
                                              True)
        self.refund_offset = BlindingFactor.from_hex(
            dct['refund_offset'].encode())
        if from_file:
            self.nonce = SecretKey.from_hex(self.secp, dct['nonce'].encode())
            self.refund_nonce = SecretKey.from_hex(
                self.secp, dct['refund_nonce'].encode())
        self.public_nonce = self.nonce.to_public_key(self.secp)
        self.public_refund_nonce = self.refund_nonce.to_public_key(self.secp)

        if seller:
            if self.is_bitcoin_swap():
                self.swap_cosign = SecretKey.from_hex(
                    self.secp, dct['swap_cosign'].encode())
            self.input_entries = [
                self.wallet.get_output(x) for x in dct['input_entries']
            ]
            self.input_amount = sum(x.value for x in self.input_entries)
            self.change_amount = self.input_amount - self.grin_amount - self.fee_amount
            self.change_entry = self.wallet.get_output(dct['change_entry'])
            self.change_child = self.wallet.derive_from_entry(
                self.change_entry)
            self.refund_entry = self.wallet.get_output(dct['refund_entry'])
            self.refund_child = self.wallet.derive_from_entry(
                self.refund_entry)
        else:
            if from_file:
                self.secret_lock = SecretKey.from_hex(
                    self.secp, dct['secret_lock'].encode())
                if self.is_bitcoin_swap():
                    self.btc_refund_key = SecretKey.from_hex(
                        self.secp, dct['btc_refund_key'].encode())

        if self.is_bitcoin_swap():
            self.public_swap_cosign = self.swap_cosign.to_public_key(self.secp) if seller else \
                PublicKey.from_hex(self.secp, dct['public_swap_cosign'].encode())

        if self.stage >= Stage.SIGN or buyer:
            self.foreign_partial_commit = Commitment.from_hex(
                self.secp, dct['foreign_partial_commit'].encode())
            self.foreign_public_nonce = PublicKey.from_hex(
                self.secp, dct['foreign_public_nonce'].encode())
            self.foreign_public_refund_nonce = PublicKey.from_hex(
                self.secp, dct['foreign_public_refund_nonce'].encode())
            if from_file:
                self.public_lock = PublicKey.from_hex(
                    self.secp, dct['public_lock'].encode())
                if self.is_bitcoin_swap():
                    self.public_btc_refund_key = self.btc_refund_key.to_public_key(self.secp) if buyer else \
                        PublicKey.from_hex(self.secp, dct['public_btc_refund_key'].encode())

            if self.is_ether_swap():
                self.eth_address_lock = ethereum_address(
                    self.secp, self.public_lock).decode()

            self.commit = self.secp.commit_sum([self.foreign_partial_commit,
                                                self.wallet.commit(self.partial_entry)], []) if not from_file else \
                Commitment.from_hex(self.secp, dct['commit'].encode())

        if self.stage >= Stage.SIGN or (buyer and from_file):
            self.public_excess = PublicKey.from_hex(
                self.secp, dct['public_excess'].encode())
            self.public_refund_excess = PublicKey.from_hex(
                self.secp, dct['public_refund_excess'].encode())
            if self.is_bitcoin_swap():
                self.btc_lock_time = int(dct['btc_lock_time'])
                self.btc_lock_address = Address.from_base58check(
                    dct['btc_lock_address'].encode())
            if self.is_ether_swap():
                self.eth_contract_address = dct['eth_contract_address']
            self.partial_signature = Signature.from_hex(
                dct['partial_signature'].encode())
            self.partial_refund_signature = Signature.from_hex(
                dct['partial_refund_signature'].encode())
            self.t_1 = PublicKey.from_hex(self.secp, dct['t_1'].encode())
            self.t_2 = PublicKey.from_hex(self.secp, dct['t_2'].encode())

        if self.stage >= Stage.SIGN:
            self.foreign_t_1 = PublicKey.from_hex(self.secp,
                                                  dct['foreign_t_1'].encode())
            self.foreign_t_2 = PublicKey.from_hex(self.secp,
                                                  dct['foreign_t_2'].encode())
            if seller:
                self.tau_x = SecretKey.from_hex(self.secp,
                                                dct['tau_x'].encode())
                self.foreign_partial_signature = Signature.from_hex(
                    dct['foreign_partial_signature'].encode())
                self.foreign_partial_refund_signature = Signature.from_hex(
                    dct['foreign_partial_refund_signature'].encode())
                if self.is_bitcoin_swap():
                    self.btc_output_points = [
                        OutputPoint.from_hex(x.encode())
                        for x in dct['btc_output_points']
                    ]
            else:
                self.foreign_tau_x = SecretKey.from_hex(
                    self.secp, dct['foreign_tau_x'].encode())

        if self.stage >= Stage.LOCK or (self.stage == Stage.SIGN and buyer):
            self.range_proof = RangeProof.from_hex(dct['range_proof'].encode())

        if self.stage >= Stage.LOCK:
            self.tx_height = dct['tx_height']
            self.swap_nonce = SecretKey.from_hex(self.secp,
                                                 dct['swap_nonce'].encode())
            self.public_swap_nonce = self.swap_nonce.to_public_key(self.secp)
            if buyer:
                self.swap_entry = self.wallet.get_output(dct['swap_entry'])
                self.swap_child = self.wallet.derive_from_entry(
                    self.swap_entry)
                self.partial_swap_adaptor = Signature.from_hex(
                    dct['partial_swap_adaptor'].encode())

        if (buyer and self.stage >= Stage.LOCK) or (seller and
                                                    self.stage >= Stage.SWAP):
            self.foreign_public_swap_nonce = PublicKey.from_hex(
                self.secp, dct['foreign_public_swap_nonce'].encode())
            self.swap_fee_amount = int(dct['swap_fee_amount'])
            self.swap_lock_height = int(dct['swap_lock_height'])
            self.swap_output = Output.from_dict(self.secp, dct['swap_output'],
                                                True)
            self.swap_offset = BlindingFactor.from_hex(
                dct['swap_offset'].encode())
            self.public_swap_excess = PublicKey.from_hex(
                self.secp, dct['public_swap_excess'].encode())
            self.partial_swap_signature = Signature.from_hex(
                dct['partial_swap_signature'].encode())

        if seller and self.stage >= Stage.SWAP:
            self.foreign_partial_swap_adaptor = Signature.from_hex(
                dct['foreign_partial_swap_adaptor'].encode())
예제 #12
0
def sell(file=None):
    assert file is None or isinstance(file, str), "Invalid argument"

    secp = Secp256k1(None, FLAG_ALL)

    print("################################")

    swap = None
    if file is None:
        print("# Grin -> BTC/ETH atomic swap\n# ")

        id = UUID.random()
        swap = AtomicSwap(secp, Role.SELLER, id)

        print("# This script is used for selling grin coins for Bitcoin or Ether through an atomic swap\n"
              "# ")

        wallet_dir = input("# What is the name of the wallet you want to use? ")
        if not os.path.isdir(wallet_dir):
            print("# Wallet directory not found")
            return
        swap.wallet = Wallet.open(secp, wallet_dir)

        grin_amount = float(input("# How much grin do you want to sell? "))
        assert grin_amount > 0 and math.isfinite(grin_amount), "Invalid input"
        swap.grin_amount = int(grin_amount*GRIN_UNIT)

        currency = input("# Which currency would you like to receive? [BTC/ETH]: ")
        if currency != "ETH":
            print("# Unknown currency, please enter BTC or ETH")
            return
        swap.swap_currency = currency

        swap_amount = float(input("# How much {} do you want to receive? ".format(currency)))
        assert swap_amount > 0 and math.isfinite(swap_amount), "Invalid input"
        if swap.is_ether_swap():
            swap.swap_amount = int(swap_amount * ETHER_UNIT)
            swap_receive_address = input("# At which Ethereum address would you like to receive this? ")
            assert is_eth_address(swap_receive_address), "Invalid input"
        else:
            print("# Unknown swap currency")
            return

        swap.swap_receive_address = swap_receive_address

        last_block = float(input("# What is the height of the last Grin T4 block? "))
        assert last_block > 0 and math.isfinite(last_block), "Invalid input"
        swap.lock_height = int(last_block)
        swap.refund_lock_height = swap.lock_height+720  # ~12h

        try:
            swap.select_inputs()
        except NotEnoughFundsException:
            print("# Not enough funds available!")
            return

        #向 Reciver 发起交易请求, 同时建立 ether 合约
        host = "127.0.0.1"
        port = 8332

        conn = hp.HTTPConnectionWithTimeout(host, port)
        myAddress = "qe8br1tSWKVvNJjjKCAXyYjbgDnWt6vJQh"
        constract_data = "6060604052341561000c57fe5b604051604080610337833981016040528080519060200190919080519060200190919050505b81600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555033600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506201518042016003819055505b50505b610223806101146000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063590e1ae314610046578063ca96d7fc14610058575bfe5b341561004e57fe5b610056610095565b005b341561006057fe5b6100936004808035600019169060200190919080356000191690602001909190803560ff169060200190919050506100e4565b005b60035442101515156100a75760006000fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600182858560405180600052602001604052600060405160200152604051806000018460ff1660ff168152602001836000191660001916815260200182600019166000191681526020019350505050602060405160208103908084039060008661646e5a03f1151561018b57fe5b50506020604051035173ffffffffffffffffffffffffffffffffffffffff161415156101b75760006000fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5050505600a165627a7a72305820a89d48ba332dd841b15b99a4d0531d29163dfffd96984ef8bd6657702a00a9810029"
        obj = { 'jsonrpc' : '1.0',
                'method' : "getwork",
                'id' : "1" ,
                'from':myAddress,
                'to':'', 
                'gasPrice':1, 
                'gas':2000000, 
                'data':constract_data}
        authpair = 'freddy:fd123qweqwe'  
        authhdr = "Basic %s" % (base64.b64encode(str.encode(authpair)))
        print(authhdr)
        print(json.dumps(obj))
        res = conn.request('POST', '/', json.dumps(obj),
                    { 'Authorization' : authhdr,
                    'Content-type' : 'application/json' })
    else:
        f = open(absolute(file), "r")
        dct = json.loads(f.read())
        f.close()

        if dct['target'] != "seller":
            print("# This file is not meant for me")
            return

        id = UUID.from_str(dct['id'])
        swap_file = absolute("swap_data", "sell", "{}.json".format(str(id)))
        if not os.path.exists(swap_file):
            print("# Swap file not found")
            return
        swap = AtomicSwap(secp, Role.SELLER, id)

        print("# Grin -> {} atomic swap\n# ".format(swap.swap_currency))

        if dct['stage'] != swap.stage.value:
            print("# Unexpected stage")
            return

        diff = int((time() - swap.time_start) / 60)
        diff_hour = int(abs(diff) / 60)
        diff_min = int(abs(diff) % 60)
        if diff_hour >= 6:
            print("# WARNING: this swap was initiated {}h{}m ago".format(diff_hour, diff_min))
            if input("# Do you want to continue? [Y/n]: ") not in ["", "Y", "y"]:
                return
            print("# ")

        swap.receive(dct)

        if swap.stage == Stage.SIGN:
        
            if swap.is_ether_swap():
                print("# Check the ETH Ropsten contract at {} for\n"
                      "#   balance of at least {} ETH\n"
                      "#   sign_address = {}\n"
                      "#   receive_address = {}\n"
                      "#   unlock_time far enough in the future (>18h)"
                      "#   enough confirmations".format(swap.eth_contract_address, swap.swap_amount / ETHER_UNIT,
                                                        swap.eth_address_lock, swap.swap_receive_address))

                if input("# Does the contract fulfil these requirements? [Y/n]: ") not in ["", "Y", "y"]:
                    print("# The buyer tried to scam you, but we caught it in time!")
                    return

            swap.fill_signatures()
        elif swap.stage == Stage.LOCK:
            swap.build_transactions()

            name = "{}_multisig_tx.json".format(swap.short_id())
            tx_wrapper = {
                "tx_hex": swap.tx.to_hex(secp).decode()
            }
            f = open(name, "w")
            f.write(json.dumps(tx_wrapper, indent=2))
            f.close()

            print("# Transaction written to {}, please submit it to a node".format(name))

            tx_height = float(input("# Enter the height of the block containing the transaction: "))
            assert tx_height > 0 and math.isfinite(tx_height), "Invalid input"
            swap.tx_height = int(tx_height)

            refund_name = "{}_refund_tx_lock{}.json".format(swap.short_id(), swap.refund_lock_height)
            refund_tx_wrapper = {
                "tx_hex": swap.refund_tx.to_hex(secp).decode()
            }
            f = open(refund_name, "w")
            f.write(json.dumps(refund_tx_wrapper, indent=2))
            f.close()

            print("# Refund transaction written to {}, can be submitted at height {}".format(refund_name,
                                                                                             swap.refund_lock_height))
        elif swap.stage == Stage.SWAP:
            swap.fill_swap_signatures()
        elif swap.stage == Stage.DONE:
            swap.finalize_swap()

            if swap.is_ether_swap():
                r, s, v = ethereum_signature(swap.claim)
                host = "127.0.0.1"
                port = 8332

                conn = hp.HTTPConnectionWithTimeout(host, port)
                hexdata ="878acceef5c2778a51ac727d5fd9f348672eaad2"+r+s+v 
                obj = { 'jsonrpc' : '1.0',
                        'method' : "getwork",
                        'id' : "1" ,
                        "constract":"eb87afcfeae7cec06dbf0db5a919098c290a73b1",
                        "hexdata":hexdata,
                                }
                authpair = 'qtum:test'  
                authhdr = "Basic %s" % (base64.b64encode(str.encode(authpair)))
                print(authhdr)
                print(json.dumps(obj))
                res = conn.request('POST', '/', json.dumps(obj),
                            { 'Authorization' : authhdr,
                            'Content-type' : 'application/json' })
                print(res)
            return

    swap.save()

    out_name = "{}_seller_{}.json".format(swap.short_id(), swap.stage.value)
    f = open(absolute(out_name), "x")
    f.write(json.dumps(swap.send(), indent=2))
    f.close()

    print("# \n"
          "# Created file '{}', please send it to the buyer\n"
          "################################".format(out_name))