def send_poll(self, method, callback): nonce = self.get_msg_nonce() msg_to_sign = prepare_ecdsa_msg(nonce, method) sig = btc.ecdsa_sign(msg_to_sign, self.keyset["key_session"][0]) noncesig = {"nonce": nonce, "sig": sig} return self.jsonrpcclient.send_poll( method, callback, noncesig, self.coinswap_parameters.session_id)
def on_JM_MAKE_TX(self, nick_list, txhex): show_receipt("JMMAKETX", nick_list, txhex) d = self.callRemote(JMSigReceived, nick="dummynick", sig="xxxsig") self.defaultCallbacks(d) #add dummy calls to check message sign and message verify d2 = self.callRemote(JMRequestMsgSig, nick="dummynickforsign", cmd="command1", msg="msgforsign", msg_to_be_signed="fullmsgforsign", hostid="hostid1") self.defaultCallbacks(d2) #To test, this must include a valid ecdsa sig fullmsg = "fullmsgforverify" priv = "aa"*32 + "01" pub = bitcoin.privkey_to_pubkey(priv) sig = bitcoin.ecdsa_sign(fullmsg, priv) d3 = self.callRemote(JMRequestMsgSigVerify, msg="msgforverify", fullmsg=fullmsg, sig=sig, pubkey=pub, nick="dummynickforverify", hashlen=4, max_encoded=5, hostid="hostid2") self.defaultCallbacks(d3) d4 = self.callRemote(JMSigReceived, nick="dummynick", sig="dummysig") self.defaultCallbacks(d4) return {'accepted': True}
def sign_message(privkey, message): """ args: privkey: bytes message: bytes returns: base64-encoded signature """ return btc.ecdsa_sign(message, privkey, True, False)
def on_JM_REQUEST_MSGSIG(self, nick, cmd, msg, msg_to_be_signed, hostid): sig = btc.ecdsa_sign(str(msg_to_be_signed), self.nick_priv) msg_to_return = str(msg) + " " + self.nick_pubkey + " " + sig d = self.callRemote(commands.JMMsgSignature, nick=nick, cmd=cmd, msg_to_return=msg_to_return, hostid=hostid) self.defaultCallbacks(d) return {'accepted': True}
def test_JMRequestMsgSigVerify(self): fullmsg = 'fullmsgforverify' priv = b"\xaa"*32 + b"\x01" pub = bintohex(bitcoin.privkey_to_pubkey(priv)) sig = bitcoin.ecdsa_sign(fullmsg, priv) yield self.init_client() yield self.callClient( JMRequestMsgSigVerify, msg='msgforverify', fullmsg=fullmsg, sig=sig, pubkey=pub, nick='dummynickforverify', hashlen=4, max_encoded=5, hostid='hostid2')
def get_fidelity_bond_template(self): if not isinstance(self.wallet_service.wallet, FidelityBondMixin): jlog.info( "Not a fidelity bond wallet, not announcing fidelity bond") return None blocks = jm_single().bc_interface.get_current_block_height() mediantime = jm_single().bc_interface.get_best_block_median_time() BLOCK_COUNT_SAFETY = 2 #use this safety number to reduce chances of the proof expiring #before the taker gets a chance to verify it RETARGET_INTERVAL = 2016 CERT_MAX_VALIDITY_TIME = 1 cert_expiry = ((blocks + BLOCK_COUNT_SAFETY) // RETARGET_INTERVAL) + CERT_MAX_VALIDITY_TIME utxos = self.wallet_service.wallet.get_utxos_by_mixdepth( include_disabled=True, includeheight=True)[FidelityBondMixin.FIDELITY_BOND_MIXDEPTH] timelocked_utxos = [ (outpoint, info) for outpoint, info in utxos.items() if FidelityBondMixin.is_timelocked_path(info["path"]) ] if len(timelocked_utxos) == 0: jlog.info( "No timelocked coins in wallet, not announcing fidelity bond") return timelocked_utxos_with_confirmation_time = [ (outpoint, info, jm_single().bc_interface.get_block_time( jm_single().bc_interface.get_block_hash(info["height"]))) for (outpoint, info) in timelocked_utxos ] interest_rate = get_interest_rate() max_valued_bond = max( timelocked_utxos_with_confirmation_time, key=lambda x: FidelityBondMixin. calculate_timelocked_fidelity_bond_value(x[1]["value"], x[2], x[1][ "path"][-1], mediantime, interest_rate)) (utxo_priv, locktime), engine = self.wallet_service.wallet._get_key_from_path( max_valued_bond[1]["path"]) utxo_pub = engine.privkey_to_pubkey(utxo_priv) cert_priv = os.urandom(32) + b"\x01" cert_pub = btc.privkey_to_pubkey(cert_priv) cert_msg = b"fidelity-bond-cert|" + cert_pub + b"|" + str( cert_expiry).encode("ascii") cert_sig = base64.b64decode(btc.ecdsa_sign(cert_msg, utxo_priv)) utxo = (max_valued_bond[0][0], max_valued_bond[0][1]) fidelity_bond = FidelityBond(utxo, utxo_pub, locktime, cert_expiry, cert_priv, cert_pub, cert_sig) jlog.info("Announcing fidelity bond coin {}".format(fmt_utxo(utxo))) return fidelity_bond
def send(self, *args): """The state machine state maps to a specific call in the JSON RPC API. The return value is passed to the callback, which is the statemachine .tick() function, which passes that return value to the next state transition function. All method calls must be prefaced by the sessionid for distinction of client, other than the handshake which inits the session. """ mn = self.jsonrpcclient.method_names[self.sm.state] nonce = self.get_msg_nonce() msg_to_sign = prepare_ecdsa_msg(nonce, mn, *args) sig = btc.ecdsa_sign(msg_to_sign, self.keyset["key_session"][0]) noncesig = {"nonce": nonce, "sig": sig} params = [self.coinswap_parameters.session_id, noncesig, mn ] + list(args) if mn != "handshake": return self.jsonrpcclient.send("coinswap", *params) else: return self.jsonrpcclient.send("handshake", *params)
def on_auth_received(self, nick, offer, commitment, cr, amount, kphex): """Receives data on proposed transaction offer from daemon, verifies commitment, returns necessary data to send ioauth message (utxos etc) """ # special case due to cjfee passed as string: it can accidentally parse # as hex: if not isinstance(offer["cjfee"], str): offer["cjfee"] = bintohex(offer["cjfee"]) #check the validity of the proof of discrete log equivalence tries = jm_single().config.getint("POLICY", "taker_utxo_retries") def reject(msg): jlog.info("Counterparty commitment not accepted, reason: " + msg) return (False, ) # deserialize the commitment revelation try: cr_dict = PoDLE.deserialize_revelation(cr) except PoDLEError as e: reason = repr(e) return reject(reason) if not verify_podle(cr_dict['P'], cr_dict['P2'], cr_dict['sig'], cr_dict['e'], commitment, index_range=range(tries)): reason = "verify_podle failed" return reject(reason) #finally, check that the proffered utxo is real, old enough, large enough, #and corresponds to the pubkey res = jm_single().bc_interface.query_utxo_set([cr_dict['utxo']], includeconf=True) if len(res) != 1 or not res[0]: reason = "authorizing utxo is not valid" return reject(reason) age = jm_single().config.getint("POLICY", "taker_utxo_age") if res[0]['confirms'] < age: reason = "commitment utxo not old enough: " + str( res[0]['confirms']) return reject(reason) reqd_amt = int( amount * jm_single().config.getint("POLICY", "taker_utxo_amtpercent") / 100.0) if res[0]['value'] < reqd_amt: reason = "commitment utxo too small: " + str(res[0]['value']) return reject(reason) try: if not self.wallet_service.pubkey_has_script( cr_dict['P'], res[0]['script']): raise EngineError() except EngineError: reason = "Invalid podle pubkey: " + str(cr_dict['P']) return reject(reason) # authorisation of taker passed # Find utxos for the transaction now: utxos, cj_addr, change_addr = self.oid_to_order(offer, amount) if not utxos: #could not find funds return (False, ) # for index update persistence: self.wallet_service.save_wallet() # Construct data for auth request back to taker. # Need to choose an input utxo pubkey to sign with # (no longer using the coinjoin pubkey from 0.2.0) # Just choose the first utxo in self.utxos and retrieve key from wallet. auth_address = utxos[list(utxos.keys())[0]]['address'] auth_key = self.wallet_service.get_key_from_addr(auth_address) auth_pub = btc.privkey_to_pubkey(auth_key) # kphex was auto-converted by @hexbin but we actually need to sign the # hex version to comply with pre-existing JM protocol: btc_sig = btc.ecdsa_sign(bintohex(kphex), auth_key) return (True, utxos, auth_pub, cj_addr, change_addr, btc_sig)
def create_proof_msg(self, cert_priv): nick_sig = ecdsa_sign(self.nick_msg, cert_priv) # FIXME: remove stupid base64 nick_sig = base64.b64decode(nick_sig) return self._serialize_proof_msg(nick_sig)