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()
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
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))
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))
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("################################")
def save(self): f = open(absolute(self.swap_file), "w") dct = self.to_dict(True) f.write(json.dumps(dct, indent=2)) f.close()
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())
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))