def test_partial_sig(): secp = Secp256k1(None, FLAG_ALL) excess = SecretKey.random(secp) public_excess = excess.to_public_key(secp) nonce = SecretKey.random(secp) public_key_sum = SecretKey.random(secp).to_public_key(secp) public_nonce_sum = SecretKey.random(secp).to_public_key(secp) fee = randint(1, 999999) lock_height = randint(1, 999999) sig = aggsig.calculate_partial(secp, excess, nonce, public_key_sum, public_nonce_sum, fee, lock_height) assert aggsig.verify_partial(secp, sig, public_excess, public_key_sum, public_nonce_sum, fee, lock_height) rnd_sig = Signature(bytearray(urandom(64))) assert not aggsig.verify_partial(secp, rnd_sig, public_excess, public_key_sum, public_nonce_sum, fee, lock_height) public_rnd = SecretKey.random(secp).to_public_key(secp) assert not aggsig.verify_partial(secp, sig, public_rnd, public_key_sum, public_nonce_sum, fee, lock_height) assert not aggsig.verify_partial(secp, sig, public_excess, public_rnd, public_nonce_sum, fee, lock_height) assert not aggsig.verify_partial(secp, sig, public_excess, public_key_sum, public_rnd, fee, lock_height) assert not aggsig.verify_partial(secp, sig, public_excess, public_key_sum, public_nonce_sum, 0, lock_height) assert not aggsig.verify_partial(secp, sig, public_excess, public_key_sum, public_nonce_sum, fee, 0)
def test_sig(): # Test Grin-like signature scheme secp = Secp256k1(None, FLAG_ALL) nonce_a = SecretKey.random(secp) public_nonce_a = nonce_a.to_public_key(secp) nonce_b = SecretKey.random(secp) public_nonce_b = nonce_b.to_public_key(secp) public_nonce_sum = PublicKey.from_combination( secp, [public_nonce_a, public_nonce_b]) excess_a = SecretKey.random(secp) public_excess_a = excess_a.to_public_key(secp) excess_b = SecretKey.random(secp) public_excess_b = excess_b.to_public_key(secp) public_excess_sum = PublicKey.from_combination( secp, [public_excess_a, public_excess_b]) fee = randint(1, 999999) lock_height = randint(1, 999999) # Partial signature for A sig_a = aggsig.calculate_partial(secp, excess_a, nonce_a, public_excess_sum, public_nonce_sum, fee, lock_height) assert aggsig.verify_partial(secp, sig_a, public_excess_a, public_excess_sum, public_nonce_sum, fee, lock_height) # Partial signature for B sig_b = aggsig.calculate_partial(secp, excess_b, nonce_b, public_excess_sum, public_nonce_sum, fee, lock_height) assert aggsig.verify_partial(secp, sig_b, public_excess_b, public_excess_sum, public_nonce_sum, fee, lock_height) # Total signature sig = aggsig.add_partials(secp, [sig_a, sig_b], public_nonce_sum) assert aggsig.verify(secp, sig, public_excess_sum, fee, lock_height) rnd_sig = Signature(bytearray(urandom(64))) assert not aggsig.verify(secp, rnd_sig, public_excess_sum, fee, lock_height) public_rnd = SecretKey.random(secp).to_public_key(secp) assert not aggsig.verify(secp, sig, public_rnd, fee, lock_height) assert not aggsig.verify(secp, sig, public_excess_sum, 0, lock_height) assert not aggsig.verify(secp, sig, public_excess_sum, fee, 0)
def fill_swap_signatures(self): buyer = self.role == Role.BUYER assert (buyer and self.stage == Stage.LOCK) or ( not buyer and self.stage == Stage.SWAP), "Incorrect stage" # Public (total) swap excess pos = [ self.swap_output.commit, self.secp.commit_value(self.swap_fee_amount) ] neg = [self.commit, self.secp.commit(0, self.swap_offset)] self.public_swap_excess = self.secp.commit_sum(pos, neg).to_public_key( self.secp) # Partial swap excess swap_blind_sum = BlindSum() swap_blind_sum.sub_child_key(self.partial_child) if buyer: swap_blind_sum.add_child_key(self.swap_child) swap_blind_sum.sub_blinding_factor(self.swap_offset) swap_excess = self.wallet.chain.blind_sum( swap_blind_sum).to_secret_key(self.secp) # Nonce sum public_swap_nonce_sum = PublicKey.from_combination( self.secp, [self.public_swap_nonce, self.foreign_public_swap_nonce]) if not buyer: # Verify that partial signature is valid with swap secret pos = [self.swap_output.commit] neg = [ self.foreign_partial_commit, self.secp.commit(0, self.swap_offset), self.secp.commit_value(self.grin_amount - self.swap_fee_amount) ] foreign_public_partial_swap_excess = self.secp.commit_sum( pos, neg).to_public_key(self.secp) assert aggsig.verify_partial_adaptor( self.secp, self.foreign_partial_swap_adaptor, foreign_public_partial_swap_excess, self.public_lock, self.public_swap_excess, public_swap_nonce_sum, self.swap_fee_amount, self.swap_lock_height), "Partial swap signature not valid" # Partial swap signature self.partial_swap_signature = aggsig.calculate_partial( self.secp, swap_excess, self.swap_nonce, self.public_swap_excess, public_swap_nonce_sum, self.swap_fee_amount, self.swap_lock_height) if buyer: self.partial_swap_adaptor = aggsig.calculate_partial_adaptor( self.secp, swap_excess, self.swap_nonce, self.secret_lock, self.public_swap_excess, public_swap_nonce_sum, self.swap_fee_amount, self.swap_lock_height)
def post(handler: HTTPServerHandler): global secp, wallet, server, proof_builder if secp is None: secp = Secp256k1(None, FLAG_ALL) wallet = Wallet.open(secp, "wallet_b") try: length = handler.headers['Content-Length'] length = 0 if length is None else int(length) if length == 0: raise Exception("Invalid length") dct = json.loads(handler.rfile.read(length).decode()) if proof_builder is not None: print("Creating bulletproof components") t_1_sender = PublicKey.from_hex(secp, dct['t_1'].encode()) t_2_sender = PublicKey.from_hex(secp, dct['t_2'].encode()) proof_builder.fill_step_1(t_1_sender, t_2_sender) tau_x_sender = SecretKey.from_hex(secp, dct['tau_x'].encode()) proof_builder.fill_step_2(tau_x_sender) proof = proof_builder.finalize() wallet.save() dct2 = {"proof": hexlify(bytes(proof.proof)).decode()} handler.json_response((json.dumps(dct2) + "\r\n").encode()) print("Sent response") return send_amount = dct['amount'] fee_amount = dct['fee'] refund_fee_amount = dct['refund_fee'] lock_height = dct['lock_height'] refund_lock_height = dct['refund_lock_height'] print("Receive {} grin in multisig".format(send_amount / GRIN_UNIT)) public_partial_commit_sender = Commitment.from_hex( secp, dct['public_partial_commit'].encode()) public_partial_sender = public_partial_commit_sender.to_public_key( secp) public_nonce_sender = PublicKey.from_hex(secp, dct['public_nonce'].encode()) refund_public_nonce_sender = PublicKey.from_hex( secp, dct['refund_public_nonce'].encode()) # Multisig output partial_child, partial_entry = wallet.create_output(send_amount) partial_entry.mark_locked() public_partial_commit = wallet.commit_with_child_key(0, partial_child) # Commitment commit = secp.commit_sum( [public_partial_commit_sender, wallet.commit(partial_entry)], []) print("Total commit: {}".format(commit)) # Nonce nonce = SecretKey.random(secp) public_nonce = nonce.to_public_key(secp) public_nonce_sum = PublicKey.from_combination( secp, [public_nonce_sender, public_nonce]) # Refund excess refund_blind_sum = BlindSum() refund_blind_sum.sub_child_key(partial_child) refund_excess = wallet.chain.blind_sum(refund_blind_sum).to_secret_key( secp) refund_public_excess = refund_excess.to_public_key(secp) # Refund nonce refund_nonce = SecretKey.random(secp) refund_public_nonce = refund_nonce.to_public_key(secp) refund_public_nonce_sum = PublicKey.from_combination( secp, [refund_public_nonce_sender, refund_public_nonce]) # Start building the bulletproof for the multisig output proof_builder = MultiPartyBulletProof(secp, partial_child, public_partial_sender, send_amount, commit) t_1, t_2 = proof_builder.step_1() # Partial signature partial_signature = aggsig.calculate_partial(secp, partial_child.key, nonce, public_nonce_sum, fee_amount, lock_height) # Refund partial signature refund_partial_signature = aggsig.calculate_partial( secp, refund_excess, refund_nonce, refund_public_nonce_sum, refund_fee_amount, refund_lock_height) dct2 = { "public_partial_commit": public_partial_commit.to_hex(secp).decode(), "refund_public_excess": refund_public_excess.to_hex(secp).decode(), "public_nonce": public_nonce.to_hex(secp).decode(), "refund_public_nonce": refund_public_nonce.to_hex(secp).decode(), "partial_signature": partial_signature.to_hex().decode(), "refund_partial_signature": refund_partial_signature.to_hex().decode(), "t_1": t_1.to_hex(secp).decode(), "t_2": t_2.to_hex(secp).decode() } handler.json_response((json.dumps(dct2) + "\r\n").encode()) print("Sent response") except Exception as e: print("Unable to parse input: {}".format(e)) handler.error_response()
def send(node_url: str): global secp, wallet, proof_builder now = int(time()) send_amount = GRIN_UNIT lock_height = 1 refund_lock_height = lock_height + 1440 # ~24 hours dest_url = "http://127.0.0.1:18185" fluff = True secp = Secp256k1(None, FLAG_ALL) wallet = Wallet.open(secp, "wallet_a") print("Preparing to create multisig with {}".format(dest_url)) input_entries = wallet.select_outputs(send_amount + tx_fee(1, 2, MILLI_GRIN_UNIT)) fee_amount = tx_fee(len(input_entries), 2, MILLI_GRIN_UNIT) input_amount = sum(x.value for x in input_entries) change_amount = input_amount - send_amount - fee_amount refund_fee_amount = tx_fee(1, 1, MILLI_GRIN_UNIT) print("Selected {} inputs".format(len(input_entries))) tx = Transaction.empty(secp, 0, fee_amount, lock_height) refund_tx = Transaction.empty(secp, 0, refund_fee_amount, refund_lock_height) blind_sum = BlindSum() # Inputs inputs = [] for entry in input_entries: entry.mark_locked() blind_sum.sub_child_key(wallet.derive_from_entry(entry)) input = wallet.entry_to_input(entry) tx.add_input(secp, input) inputs.append(input) # Change output change_child, change_entry = wallet.create_output(change_amount) blind_sum.add_child_key(change_child) change_output = wallet.entry_to_output(change_entry) tx.add_output(secp, change_output) # Multisig output partial_child, partial_entry = wallet.create_output(send_amount) partial_entry.mark_locked() blind_sum.add_child_key(partial_child) public_partial_commit = wallet.commit_with_child_key(0, partial_child) # Refund output refund_amount = send_amount - refund_fee_amount refund_child, refund_entry = wallet.create_output(refund_amount) refund_output = wallet.entry_to_output(refund_entry) refund_tx.add_output(secp, refund_output) # Offset blind_sum.sub_blinding_factor(tx.offset) # Excess excess = wallet.chain.blind_sum(blind_sum).to_secret_key(secp) public_excess = excess.to_public_key(secp) # Nonce nonce = SecretKey.random(secp) public_nonce = nonce.to_public_key(secp) # Refund nonce refund_nonce = SecretKey.random(secp) refund_public_nonce = refund_nonce.to_public_key(secp) dct = { "amount": send_amount, "fee": fee_amount, "refund_fee": refund_fee_amount, "lock_height": lock_height, "refund_lock_height": refund_lock_height, "public_partial_commit": public_partial_commit.to_hex(secp).decode(), "public_nonce": public_nonce.to_hex(secp).decode(), "refund_public_nonce": refund_public_nonce.to_hex(secp).decode() } f = open("logs/{}_multisig_1.json".format(now), "w") f.write(json.dumps(dct, indent=2)) f.close() print("Sending to receiver..") req = urlopen(dest_url, json.dumps(dct).encode(), 60) dct2 = json.loads(req.read().decode()) f = open("logs/{}_multisig_2.json".format(now), "w") f.write(json.dumps(dct2, indent=2)) f.close() print("Received response, processing..") public_partial_commit_recv = Commitment.from_hex( secp, dct2['public_partial_commit'].encode()) public_partial_recv = public_partial_commit_recv.to_public_key(secp) public_nonce_recv = PublicKey.from_hex(secp, dct2['public_nonce'].encode()) public_excess_recv = public_partial_commit_recv.to_public_key(secp) partial_signature_recv = Signature.from_hex( dct2['partial_signature'].encode()) refund_public_nonce_recv = PublicKey.from_hex( secp, dct2['refund_public_nonce'].encode()) refund_public_excess_recv = PublicKey.from_hex( secp, dct2['refund_public_excess'].encode()) refund_partial_signature_recv = Signature.from_hex( dct2['refund_partial_signature'].encode()) # Commitment commit = secp.commit_sum( [public_partial_commit_recv, wallet.commit(partial_entry)], []) print("Total commit: {}".format(commit)) # Nonce sums public_nonce_sum = PublicKey.from_combination( secp, [public_nonce_recv, public_nonce]) refund_public_nonce_sum = PublicKey.from_combination( secp, [refund_public_nonce_recv, refund_public_nonce]) # Step 2 of bulletproof proof_builder = MultiPartyBulletProof(secp, partial_child, public_partial_recv, send_amount, commit) t_1_recv = PublicKey.from_hex(secp, dct2['t_1'].encode()) t_2_recv = PublicKey.from_hex(secp, dct2['t_2'].encode()) t_1, t_2 = proof_builder.step_1() proof_builder.fill_step_1(t_1_recv, t_2_recv) tau_x = proof_builder.step_2() dct3 = { "t_1": t_1.to_hex(secp).decode(), "t_2": t_2.to_hex(secp).decode(), "tau_x": tau_x.to_hex().decode() } f = open("logs/{}_multisig_3.json".format(now), "w") f.write(json.dumps(dct3, indent=2)) f.close() print("Sending bulletproof component..") req2 = urlopen(dest_url, json.dumps(dct3).encode(), 60) dct4 = json.loads(req2.read().decode()) print("Received response") f = open("logs/{}_multisig_4.json".format(now), "w") f.write(json.dumps(dct4, indent=2)) f.close() # Bulletproof proof = RangeProof.from_bytearray( bytearray(unhexlify(dct4['proof'].encode()))) output = Output(OutputFeatures.DEFAULT_OUTPUT, commit, proof) assert output.verify(secp), "Invalid bulletproof" tx.add_output(secp, output) print("Created bulletproof") # First we finalize the refund tx, and check its validity refund_input = Input(OutputFeatures.DEFAULT_OUTPUT, commit) refund_tx.add_input(secp, refund_input) # Refund excess refund_blind_sum = BlindSum() refund_blind_sum.sub_child_key(partial_child) refund_blind_sum.add_child_key(refund_child) refund_blind_sum.sub_blinding_factor(refund_tx.offset) refund_excess = wallet.chain.blind_sum(refund_blind_sum).to_secret_key( secp) refund_public_excess = refund_excess.to_public_key(secp) # Refund partial signature refund_partial_signature = aggsig.calculate_partial( secp, refund_excess, refund_nonce, refund_public_nonce_sum, refund_fee_amount, refund_lock_height) # Refund final signature refund_public_excess_sum = PublicKey.from_combination( secp, [refund_public_excess_recv, refund_public_excess]) refund_signature = aggsig.add_partials( secp, [refund_partial_signature_recv, refund_partial_signature], refund_public_nonce_sum) assert aggsig.verify(secp, refund_signature, refund_public_excess_sum, refund_fee_amount, refund_lock_height), \ "Unable to verify refund signature" refund_kernel = refund_tx.kernels[0] refund_kernel.excess = refund_tx.sum_commitments(secp) refund_kernel.excess_signature = refund_signature assert refund_tx.verify_kernels(secp), "Unable to verify refund kernel" print("Refund tx is valid") f = open("logs/{}_refund.json".format(now), "w") f.write(json.dumps(refund_tx.to_dict(secp), indent=2)) f.close() refund_tx_wrapper = {"tx_hex": refund_tx.to_hex(secp).decode()} f = open("logs/{}_refund_hex.json".format(now), "w") f.write(json.dumps(refund_tx_wrapper, indent=2)) f.close() print("Finalizing multisig tx..") # Partial signature partial_signature = aggsig.calculate_partial(secp, excess, nonce, public_nonce_sum, fee_amount, lock_height) # Final signature public_excess_sum = PublicKey.from_combination( secp, [public_excess_recv, public_excess]) signature = aggsig.add_partials( secp, [partial_signature_recv, partial_signature], public_nonce_sum) assert aggsig.verify(secp, signature, public_excess_sum, fee_amount, lock_height), "Unable to verify signature" kernel = tx.kernels[0] kernel.excess = tx.sum_commitments(secp) kernel.excess_signature = signature assert tx.verify_kernels(secp), "Unable to verify kernel" f = open("logs/{}_tx.json".format(now), "w") f.write(json.dumps(tx.to_dict(secp), indent=2)) f.close() tx_wrapper = {"tx_hex": tx.to_hex(secp).decode()} f = open("logs/{}_tx_hex.json".format(now), "w") f.write(json.dumps(tx_wrapper, indent=2)) f.close() print("Submitting to node..") urlopen("{}/v1/pool/push".format(node_url) + ("?fluff" if fluff else ""), json.dumps(tx_wrapper).encode(), 600) wallet.save() print("Transaction complete!")
def partial_signature(self, secp: Secp256k1, participant: ParticipantData, secret_key: SecretKey, secret_nonce: SecretKey): participant.partial_signature = aggsig.calculate_partial( secp, secret_key, secret_nonce, self.public_blind_excess_sum(secp), self.public_nonce_sum(secp), self.fee, self.lock_height)
def fill_signatures(self): seller = self.role == Role.SELLER assert (not seller and self.stage == Stage.INIT) or ( seller and self.stage == Stage.SIGN), "Incorrect stage" # Public (total) excess pos = [ self.commit, self.change_output.commit, self.secp.commit_value(self.fee_amount) ] neg = [x.commit for x in self.inputs] neg.append(self.secp.commit(0, self.offset)) self.public_excess = self.secp.commit_sum(pos, neg).to_public_key(self.secp) # Partial excess blind_sum = BlindSum() blind_sum.add_child_key(self.partial_child) if seller: blind_sum.add_child_key(self.change_child) for entry in self.input_entries: blind_sum.sub_child_key(self.wallet.derive_from_entry(entry)) blind_sum.sub_blinding_factor(self.offset) excess = self.wallet.chain.blind_sum(blind_sum).to_secret_key( self.secp) # Partial signature public_nonce_sum = PublicKey.from_combination( self.secp, [self.public_nonce, self.foreign_public_nonce]) self.partial_signature = aggsig.calculate_partial( self.secp, excess, self.nonce, self.public_excess, public_nonce_sum, self.fee_amount, self.lock_height) # First step of multi party bullet proof proof_builder = TwoPartyBulletProof( self.secp, self.partial_child.key, self.foreign_partial_commit.to_public_key(self.secp), self.grin_amount, self.commit) self.t_1, self.t_2 = proof_builder.round_1() if seller: proof_builder.fill_round_1(self.foreign_t_1, self.foreign_t_2) self.tau_x = proof_builder.round_2() # Public (total) refund excess pos = [ self.refund_output.commit, self.secp.commit_value(self.refund_fee_amount) ] neg = [self.commit, self.secp.commit(0, self.refund_offset)] self.public_refund_excess = self.secp.commit_sum( pos, neg).to_public_key(self.secp) # Partial refund excess refund_blind_sum = BlindSum() refund_blind_sum.sub_child_key(self.partial_child) if seller: refund_blind_sum.add_child_key(self.refund_child) refund_blind_sum.sub_blinding_factor(self.refund_offset) refund_excess = self.wallet.chain.blind_sum( refund_blind_sum).to_secret_key(self.secp) # Partial refund signature public_refund_nonce_sum = PublicKey.from_combination( self.secp, [self.public_refund_nonce, self.foreign_public_refund_nonce]) self.partial_refund_signature = aggsig.calculate_partial( self.secp, refund_excess, self.refund_nonce, self.public_refund_excess, public_refund_nonce_sum, self.refund_fee_amount, self.refund_lock_height)