def test_two_party_proof(): secp = Secp256k1(None, FLAG_ALL) secret_key_a = SecretKey.random(secp) public_key_a = secret_key_a.to_public_key(secp) child_key_a = ChildKey(0, Identifier.random(), Identifier.random(), secret_key_a) secret_key_b = SecretKey.random(secp) public_key_b = secret_key_b.to_public_key(secp) child_key_b = ChildKey(0, Identifier.random(), Identifier.random(), secret_key_b) amount = randint(1, 999999) commit = secp.commit_sum( [secp.commit(amount, secret_key_a), secp.commit(0, secret_key_b)], []) tpp_a = TwoPartyBulletProof(secp, child_key_a, public_key_b, amount, commit) tpp_b = TwoPartyBulletProof(secp, child_key_b, public_key_a, amount, commit) t_1_a, t_2_a = tpp_a.round_1() t_1_b, t_2_b = tpp_b.round_1() tpp_a.fill_round_1(t_1_b, t_2_b) tpp_b.fill_round_1(t_1_a, t_2_a) tau_x_a = tpp_a.round_2() tau_x_b = tpp_b.round_2() tpp_a.fill_round_2(tau_x_b) tpp_b.fill_round_2(tau_x_a) proof_a = tpp_a.finalize() assert secp.verify_bullet_proof(commit, proof_a, bytearray()) proof_b = tpp_b.finalize() assert proof_a == proof_b
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 select_inputs(self): assert self.stage is None, "Incorrect stage" assert self.role == Role.SELLER, "Incorrect role" self.stage = Stage.INIT if self.is_bitcoin_swap(): # Generate a key that will co-sign the BTC multisig self.swap_cosign = SecretKey.random(self.secp) self.public_swap_cosign = self.swap_cosign.to_public_key(self.secp) # Inputs self.input_entries = self.wallet.select_outputs( self.grin_amount + tx_fee(1, 2, MILLI_GRIN_UNIT) + 1) self.inputs = [] for entry in self.input_entries: entry.mark_locked() input = self.wallet.entry_to_input(entry) self.inputs.append(input) self.fee_amount = tx_fee(len(self.input_entries), 2, MILLI_GRIN_UNIT) self.refund_fee_amount = tx_fee(1, 1, MILLI_GRIN_UNIT) self.input_amount = sum(x.value for x in self.input_entries) self.change_amount = self.input_amount - self.grin_amount - self.fee_amount # Change output self.change_child, self.change_entry = self.wallet.create_output( self.change_amount) self.change_entry.mark_locked() self.change_output = self.wallet.entry_to_output(self.change_entry) # Partial multisig output self.partial_child, self.partial_entry = self.wallet.create_output( self.grin_amount) self.partial_entry.mark_locked() self.partial_commit = self.wallet.commit_with_child_key( 0, self.partial_child) # Offset self.offset = BlindingFactor.from_secret_key( SecretKey.random(self.secp)) # Refund output refund_amount = self.grin_amount - self.refund_fee_amount self.refund_child, self.refund_entry = self.wallet.create_output( refund_amount) self.refund_output = self.wallet.entry_to_output(self.refund_entry) # Refund offset self.refund_offset = BlindingFactor.from_secret_key( SecretKey.random(self.secp)) # Nonces self.nonce = SecretKey.random(self.secp) self.public_nonce = self.nonce.to_public_key(self.secp) self.refund_nonce = SecretKey.random(self.secp) self.public_refund_nonce = self.refund_nonce.to_public_key(self.secp) self.wallet.save()
def prepare_swap(self): assert self.role == Role.BUYER and self.stage == Stage.LOCK, "Incorrect stage" self.swap_nonce = SecretKey.random(self.secp) self.public_swap_nonce = self.swap_nonce.to_public_key(self.secp) self.swap_fee_amount = tx_fee(1, 1, MILLI_GRIN_UNIT) self.swap_lock_height = self.lock_height + 1 self.swap_child, self.swap_entry = self.wallet.create_output( self.grin_amount - self.swap_fee_amount) self.wallet.save() self.swap_offset = BlindingFactor.from_secret_key( SecretKey.random(self.secp)) self.swap_output = self.wallet.entry_to_output(self.swap_entry) self.fill_swap_signatures()
def multi_party_proof(n): assert n > 1 secp = Secp256k1(None, FLAG_ALL) mpps = [] child_keys = [] amount = randint(1, 999999) commit_sum = [secp.commit_value(amount)] common_nonce = SecretKey.random(secp) for i in range(n): child_keys.append( ChildKey(i, Identifier.random(), Identifier.random(), SecretKey.random(secp))) child_key = child_keys[i] commit_sum.append(secp.commit(0, child_key.key)) # Total commitment: sum xi*G + v*H commitment = secp.commit_sum(commit_sum, []) t_1s = [] t_2s = [] # Each party exports their T1 and T2 for i in range(n): mpps.append( MultiPartyBulletProof(secp, child_keys[i], amount, commitment, common_nonce)) t_1, t_2 = mpps[i].round_1() t_1s.append(t_1) t_2s.append(t_2) # Each party receives the other T1 and T2 values and adds it to their own for i in range(n): for j in range(n): if i != j: mpps[i].fill_round_1(t_1s[j], t_2s[j]) tau_xs = [] # Each party exports their tau_x for i in range(n): tau_xs.append(mpps[i].round_2()) # One party receives the other tau_x values and adds it to their own # In this test we simulate each party calculating the proof for i in range(n): for j in range(n): if i != j: mpps[i].fill_round_2(tau_xs[j]) proofs = [] for i in range(n): proofs.append(mpps[i].finalize()) assert secp.verify_bullet_proof(commitment, proofs[i], bytearray()) for i in range(n - 1): assert proofs[i + 1] == proofs[0]
def create_nonce(secp: Secp256k1, chain_code: ChainCode, commit: Commitment) -> SecretKey: return SecretKey.from_bytearray( secp, bytearray( blake2b(bytes(chain_code.chain_code), digest_size=32, key=bytes(commit.to_bytearray(secp))).digest()))
def sign(self, secp: Secp256k1, i: int, secret_key: SecretKey): script_sig = bytearray() signature = self.raw_signature(secp, i, secret_key) script_sig.extend(script_write_bytes(len(signature))) script_sig.extend(signature) public_key = secret_key.to_public_key(secp).to_bytearray(secp) script_sig.extend(script_write_bytes(len(public_key))) script_sig.extend(public_key) self.inputs[i].script_sig = script_sig
def post(handler: HTTPServerHandler): global secp, wallet, server try: length = handler.headers['Content-Length'] length = 0 if length is None else int(length) raw = "" if length > 0: raw = handler.rfile.read(length).decode() f = open("simple_tx_receive.json", "w") f.write(raw) f.close() slate = Slate.from_dict(secp, json.loads(raw)) print("Receive {} grin".format(slate.amount / GRIN_UNIT)) # Output # n_outputs = min(100, slate.amount) n_outputs = 1 blind_sum = BlindSum() output_child_key, output_entry = wallet.create_output(slate.amount-n_outputs+1) print("Generate output 0: {}".format(wallet.commit(output_entry))) print() blind_sum.add_child_key(output_child_key) slate.tx.add_output(secp, wallet.entry_to_output(output_entry)) if n_outputs > 1: for i in range(n_outputs-1): output_child_key_loop, output_entry_loop = wallet.create_output(1) print("Generate output {}: {}".format(i + 1, wallet.commit(output_entry_loop))) blind_sum.add_child_key(output_child_key_loop) slate.tx.add_output(secp, wallet.entry_to_output(output_entry_loop)) # Excess excess = wallet.chain.blind_sum(blind_sum).to_secret_key(secp) public_excess = excess.to_public_key(secp) print("Generated excess") # Nonce nonce = SecretKey.random(secp) public_nonce = nonce.to_public_key(secp) # Add participant data participant = ParticipantData(1, public_excess, public_nonce, None) slate.add_participant(participant) # After all participants published their nonce, calculate partial signature slate.partial_signature(secp, participant, excess, nonce) resp = json.dumps(slate.to_dict(secp)) wallet.save() handler.json_response((resp + "\r\n").encode()) print("Sent response") except Exception as e: print("Unable to parse slate: {}".format(e)) handler.error_response()
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 test_proofs(create=False): secp = Secp256k1(None, FLAG_ALL) if create: dct = [] for i in range(100): secret = SecretKey.random(secp) nonce = SecretKey.random(secp) amount = randint(1, 10000000000) proof = secp.bullet_proof(amount, secret, nonce, bytearray()) dct.append({ "secret": secret.to_hex().decode(), "nonce": nonce.to_hex().decode(), "amount": amount, "proof": proof.to_hex().decode() }) f = open("proofs_new.json", "w") f.write(json.dumps(dct, indent=2)) f.close() return f = open("tests/proofs.json", "r") dct = json.loads(f.read()) f.close() test_count = 0 test_equal_count = 0 for test in dct: test_count += 1 secret = SecretKey.from_hex(secp, test['secret'].encode()) nonce = SecretKey.from_hex(secp, test['nonce'].encode()) amount = test['amount'] proof_target = RangeProof.from_hex(test['proof'].encode()) proof = secp.bullet_proof(amount, secret, nonce, bytearray()) commit = secp.commit(amount, secret) assert secp.verify_bullet_proof( commit, proof, bytearray()), "Proof {} fails to verify".format(test_count) if proof == proof_target: test_equal_count += 1 assert test_count == test_equal_count, \ "All proofs verify, but only {} out of {} proofs are the same".format(test_equal_count, test_count)
def create_common_nonce(secp: Secp256k1, secret_key: SecretKey, public_key: PublicKey, commit: Commitment) -> SecretKey: common_key = public_key.mul(secp, secret_key) return SecretKey.from_bytearray( secp, bytearray( blake2b(bytes(common_key.to_bytearray(secp)), digest_size=32, key=bytes(commit.to_bytearray(secp))).digest()))
def build_transactions(self): assert self.role == Role.SELLER and self.stage == Stage.LOCK, "Incorrect stage" # Check output range proof output = Output(OutputFeatures.DEFAULT_OUTPUT, self.commit, self.range_proof) assert output.verify(self.secp), "Invalid bulletproof" # Build refund tx refund_input = Input(OutputFeatures.DEFAULT_OUTPUT, self.commit) public_refund_nonce_sum = PublicKey.from_combination( self.secp, [self.public_refund_nonce, self.foreign_public_refund_nonce]) refund_signature = aggsig.add_partials(self.secp, [ self.partial_refund_signature, self.foreign_partial_refund_signature ], public_refund_nonce_sum) assert aggsig.verify( self.secp, refund_signature, self.public_refund_excess, self.refund_fee_amount, self.refund_lock_height), "Unable to verify refund signature" refund_kernel = Kernel(0, self.refund_fee_amount, self.refund_lock_height, None, None) self.refund_tx = Transaction([refund_input], [self.refund_output], [refund_kernel], self.refund_offset) refund_kernel.excess = self.refund_tx.sum_commitments(self.secp) refund_kernel.excess_signature = refund_signature assert self.refund_tx.verify_kernels( self.secp), "Unable to verify refund kernel" # Build multisig tx public_nonce_sum = PublicKey.from_combination( self.secp, [self.public_nonce, self.foreign_public_nonce]) signature = aggsig.add_partials( self.secp, [self.partial_signature, self.foreign_partial_signature], public_nonce_sum) assert aggsig.verify(self.secp, signature, self.public_excess, self.fee_amount, self.lock_height), \ "Unable to verify signature" kernel = Kernel(0, self.fee_amount, self.lock_height, None, None) self.tx = Transaction(self.inputs, [self.change_output, output], [kernel], self.offset) kernel.excess = self.tx.sum_commitments(self.secp) kernel.excess_signature = signature assert self.tx.verify_kernels(self.secp), "Unable to verify kernel" self.swap_nonce = SecretKey.random(self.secp) self.public_swap_nonce = self.swap_nonce.to_public_key(self.secp)
def ckd_public(self, secp: Secp256k1, hasher: Hasher, i: ChildNumber): if i.is_hardened(): raise HardenedIndexError() hasher.init_sha512(self.chain_code.to_bytearray()) hasher.append_sha512(self.public_key.to_bytearray(secp)) hasher.append_sha512(i.to_bytearray()) hash = hasher.result_sha512() key = SecretKey.from_bytearray(secp, hash[:32]).to_public_key(secp) key.add_assign(secp, self.public_key) return ExtendedPublicKey(self.network, self.depth + 1, self.fingerprint(secp, hasher), i, key, ChainCode.from_bytearray(hash[32:]))
def ckd_secret(self, secp: Secp256k1, hasher: Hasher, i: ChildNumber): hasher.init_sha512(self.chain_code.to_bytearray()) if i.is_normal(): hasher.append_sha512(self.secret_key.to_public_key(secp).to_bytearray(secp)) else: hasher.append_sha512(bytearray([0])) hasher.append_sha512(self.secret_key.to_bytearray()) hasher.append_sha512(i.to_bytearray()) hash = hasher.result_sha512() key = SecretKey.from_bytearray(secp, hash[:32]) key.add_assign(secp, self.secret_key) return ExtendedSecretKey(self.network, self.depth + 1, self.fingerprint(secp, hasher), i, key, ChainCode.from_bytearray(hash[32:]))
def blind_sum(self, positives, negatives) -> SecretKey: keys = [] for positive in positives: assert isinstance(positive, SecretKey) keys.append(ffi.new("char []", bytes(positive.key))) for negative in negatives: assert isinstance(negative, SecretKey) keys.append(ffi.new("char []", bytes(negative.key))) sum_key = ffi.new("char []", SECRET_KEY_SIZE) ret = lib.secp256k1_pedersen_blind_sum(self.ctx, sum_key, keys, len(keys), len(positives)) assert ret, "Unable to sum blinding factors" return SecretKey.from_bytearray( self, bytearray(ffi.buffer(sum_key, SECRET_KEY_SIZE)))
def bullet_proof_multisig_2(self, value: int, blind: SecretKey, commit: Commitment, common_nonce: SecretKey, nonce: SecretKey, t_1: PublicKey, t_2: PublicKey, extra_data: bytearray) -> SecretKey: scratch = lib.secp256k1_scratch_space_create(self.ctx, 256 * MAX_WIDTH) tau_x_ptr = ffi.new("char []", 32) blind_key = ffi.new("char []", bytes(blind.key)) res = lib.secp256k1_bulletproof_rangeproof_prove( self.ctx, scratch, self.gens, ffi.NULL, ffi.NULL, tau_x_ptr, t_1.key, t_2.key, [value], ffi.NULL, [blind_key], [commit.commitment], 1, self.GENERATOR_H, 64, bytes(common_nonce.key), bytes(nonce.key), bytes(extra_data), len(extra_data), ffi.NULL) lib.secp256k1_scratch_space_destroy(scratch) assert res, "Unable to generate multisig bulletproof" return SecretKey.from_bytearray(self, bytearray(ffi.buffer(tau_x_ptr, 32)))
def bullet_proof_multisig_3(self, value: int, blind: SecretKey, commit: Commitment, common_nonce: SecretKey, nonce: SecretKey, t_1: PublicKey, t_2: PublicKey, tau_x: SecretKey, extra_data: bytearray) -> RangeProof: scratch = lib.secp256k1_scratch_space_create(self.ctx, 256 * MAX_WIDTH) proof_ptr = ffi.new("char []", MAX_PROOF_SIZE) proof_len_ptr = ffi.new("size_t *", MAX_PROOF_SIZE) tau_x_ptr = ffi.new("char []", bytes(tau_x.to_bytearray())) blind_key = ffi.new("char []", bytes(blind.key)) res = lib.secp256k1_bulletproof_rangeproof_prove( self.ctx, scratch, self.gens, proof_ptr, proof_len_ptr, tau_x_ptr, t_1.key, t_2.key, [value], ffi.NULL, [blind_key], [commit.commitment], 1, self.GENERATOR_H, 64, bytes(common_nonce.key), bytes(nonce.key), bytes(extra_data), len(extra_data), ffi.NULL) obj = RangeProof.from_bytearray( bytearray(ffi.buffer(proof_ptr, proof_len_ptr[0]))) lib.secp256k1_scratch_space_destroy(scratch) assert res, "Unable to generate multisig bulletproof" return obj
def from_bytearray(secp: Secp256k1, data: bytearray): assert len(data) == 78 return ExtendedSecretKey(data[:4], data[4], Fingerprint.from_bytearray(data[5:9]), ChildNumber.from_bytearray(data[9:13]), SecretKey.from_bytearray(secp, data[46:78]), ChainCode.from_bytearray(data[13:45]))
def receive(self, dct: dict): if self.stage is None: if self.role == Role.BUYER: dct['time_start'] = int(time()) self.wallet = Wallet.open(self.secp, dct['wallet']) # Add 'foreign_' prefix to some keys dct.update({ "foreign_partial_commit": dct['partial_commit'], "foreign_public_nonce": dct['public_nonce'], "foreign_public_refund_nonce": dct['public_refund_nonce'] }) dct.pop("partial_commit", None) dct.pop("public_nonce", None) dct.pop("public_refund_nonce", None) # Partial multisig output self.partial_child, self.partial_entry = self.wallet.create_output( dct['grin_amount']) self.partial_entry.mark_locked() self.nonce = SecretKey.random(self.secp) self.refund_nonce = SecretKey.random(self.secp) self.secret_lock = SecretKey.random(self.secp) self.public_lock = self.secret_lock.to_public_key(self.secp) if dct['swap_currency'] == "BTC": self.btc_lock_time = int(time() + 24 * 60 * 60) self.btc_refund_key = SecretKey.random(self.secp) self.public_btc_refund_key = self.btc_refund_key.to_public_key( self.secp) self.load(dct) if self.is_bitcoin_swap(): self.btc_lock_address = self.calculate_btc_lock_address() self.wallet.save() else: raise Exception("This stage doesn't expect an input file") elif self.stage == Stage.INIT: self.stage = Stage.SIGN self.foreign_t_1 = PublicKey.from_hex(self.secp, dct['t_1'].encode()) self.foreign_t_2 = PublicKey.from_hex(self.secp, dct['t_2'].encode()) if self.role == Role.SELLER: self.foreign_partial_commit = Commitment.from_hex( self.secp, dct['partial_commit'].encode()) self.foreign_public_nonce = PublicKey.from_hex( self.secp, dct['public_nonce'].encode()) self.foreign_public_refund_nonce = PublicKey.from_hex( self.secp, dct['public_refund_nonce'].encode()) self.public_lock = PublicKey.from_hex( self.secp, dct['public_lock'].encode()) if self.is_bitcoin_swap(): self.btc_lock_time = int(dct['btc_lock_time']) self.public_btc_refund_key = PublicKey.from_hex( self.secp, dct['public_btc_refund_key']) self.btc_lock_address = self.calculate_btc_lock_address() if self.is_ether_swap(): self.eth_address_lock = ethereum_address( self.secp, self.public_lock).decode() self.eth_contract_address = dct['eth_contract_address'] self.commit = self.secp.commit_sum([ self.foreign_partial_commit, self.wallet.commit(self.partial_entry) ], []) self.foreign_partial_signature = Signature.from_hex( dct['partial_signature'].encode()) self.foreign_partial_refund_signature = Signature.from_hex( dct['partial_refund_signature'].encode()) else: self.foreign_tau_x = SecretKey.from_hex( self.secp, dct['tau_x'].encode()) elif self.stage == Stage.SIGN: self.stage = Stage.LOCK if self.role == Role.SELLER: self.range_proof = RangeProof.from_hex( dct['range_proof'].encode()) else: self.tx_height = dct['tx_height'] self.foreign_public_swap_nonce = PublicKey.from_hex( self.secp, dct['public_swap_nonce'].encode()) elif self.stage == Stage.LOCK: self.stage = Stage.SWAP if self.role == Role.SELLER: self.foreign_public_swap_nonce = PublicKey.from_hex( self.secp, dct['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.foreign_partial_swap_adaptor = Signature.from_hex( dct['partial_swap_adaptor']) else: self.foreign_partial_swap_signature = Signature.from_hex( dct['partial_swap_signature']) elif self.stage == Stage.SWAP: self.stage = Stage.DONE if self.role == Role.SELLER: self.foreign_partial_swap_signature = Signature.from_hex( dct['partial_swap_signature']) else: raise Exception("Invalid stage")
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 empty(secp: Secp256k1, features: int, fee: int, lock_height: int): kernel = Kernel(features, fee, lock_height, None, None) return Transaction([], [], [kernel], BlindingFactor.from_secret_key( SecretKey.random(secp)))
def send(node_url: str): global secp, wallet send_amount = 1 height = 33333 lock_height = 33333 features = 0 dest_url = "http://127.0.0.1:17175" fluff = True n_outputs = 2 secp = Secp256k1(None, FLAG_ALL) wallet = Wallet.open(secp, "wallet_1") print("Preparing to send {} grin to {}".format(send_amount / GRIN_UNIT, dest_url)) input_entries = wallet.select_outputs(send_amount + tx_fee(1, n_outputs, MILLI_GRIN_UNIT)) fee_amount = tx_fee(len(input_entries), n_outputs, MILLI_GRIN_UNIT) input_amount = sum(x.value for x in input_entries) change_amount = input_amount - send_amount - fee_amount print("Selected {} inputs".format(len(input_entries))) blind_sum = BlindSum() slate = Slate.blank(secp, 2, send_amount, height, features, fee_amount, lock_height) # Inputs for entry in input_entries: entry.mark_locked() blind_sum.sub_child_key(wallet.derive_from_entry(entry)) slate.tx.add_input(secp, wallet.entry_to_input(entry)) # Change output change_key, change_entry = wallet.create_output(change_amount) blind_sum.add_child_key(change_key) slate.tx.add_output(secp, wallet.entry_to_output(change_entry)) # Excess blind_sum.sub_blinding_factor(slate.tx.offset) 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) # Participant participant = ParticipantData(0, public_excess, public_nonce, None) slate.add_participant(participant) print("Sending slate to receiver..") req = urlopen(dest_url, json.dumps(slate.to_dict(secp)).encode(), 600) slate = Slate.from_dict(secp, json.loads(req.read().decode())) print("Received response, finishing transaction..") participant = slate.get_participant(0) slate.partial_signature(secp, participant, excess, nonce) slate.finalize(secp) tx_wrapper = { "tx_hex": slate.tx.to_hex(secp).decode() } 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 commit_value(self, value: int) -> Commitment: blind = SecretKey() return self.commit(value, blind)
def from_secret_key(key: SecretKey): return BlindingFactor.from_bytearray(key.to_bytearray())
def new_master(secp: Secp256k1, hasher: Hasher, seed: bytearray): hasher.init_sha512(hasher.master_seed()) hasher.append_sha512(seed) hash = hasher.result_sha512() return ExtendedSecretKey(hasher.network_secret(), 0, Fingerprint.default(), ChildNumber.from_normal_index(0), SecretKey.from_bytearray(secp, hash[:32]), ChainCode.from_bytearray(hash[32:]))
def to_secret_key(self, secp: Secp256k1) -> SecretKey: return SecretKey.from_bytearray(secp, self.to_bytearray())
def test_public_key(): secp = Secp256k1(None, FLAG_ALL) # (de)serialization secret_key = SecretKey.random(secp) public_key = secret_key.to_public_key(secp) public_key_2 = PublicKey.from_bytearray(secp, public_key.to_bytearray(secp)) assert public_key == public_key_2 # (a+b)*G = a*G + b*G secret_key_a = SecretKey.random(secp) secret_key_b = SecretKey.random(secp) secret_key_a_b = secret_key_a.add(secp, secret_key_b) public_key_a = secret_key_a.to_public_key(secp) public_key_b = secret_key_b.to_public_key(secp) public_key_a_b = secret_key_a_b.to_public_key(secp) public_key_a_b_2 = PublicKey.from_combination(secp, [public_key_a, public_key_b]) public_key_a_b_3 = public_key_a.add_scalar(secp, secret_key_b) assert public_key_a_b == public_key_a_b_2 assert public_key_a_b == public_key_a_b_3 # (ab)*G = a(b*G) = b(a*G) secret_key_a = SecretKey.random(secp) secret_key_b = SecretKey.random(secp) secret_key_ab = secret_key_a.mul(secp, secret_key_b) public_key_ab = secret_key_ab.to_public_key(secp) public_key_ab_2 = secret_key_a.to_public_key(secp) public_key_ab_2.mul_assign(secp, secret_key_b) public_key_ab_3 = secret_key_b.to_public_key(secp) public_key_ab_3.mul_assign(secp, secret_key_a) assert public_key_ab == public_key_ab_2 assert public_key_ab == public_key_ab_3 # (c(a+b))*G = c(a*G) + c(b*G) secret_key_a = SecretKey.random(secp) secret_key_b = SecretKey.random(secp) secret_key_c = SecretKey.random(secp) secret_key_ca_b = secret_key_a.add(secp, secret_key_b) secret_key_ca_b.mul_assign(secp, secret_key_c) public_key_ca_b = secret_key_ca_b.to_public_key(secp) public_key_ca = secret_key_a.to_public_key(secp) public_key_ca.mul_assign(secp, secret_key_c) public_key_cb = secret_key_b.to_public_key(secp) public_key_cb.mul_assign(secp, secret_key_c) public_key_ca_cb = public_key_ca.add(secp, public_key_cb) assert public_key_ca_b == public_key_ca_cb # (a+b+c)*G = a*G + b*G + c*G secret_key_a = SecretKey.random(secp) secret_key_b = SecretKey.random(secp) secret_key_c = SecretKey.random(secp) secret_key_a_b_c = secret_key_a.add(secp, secret_key_b) secret_key_a_b_c.add_assign(secp, secret_key_c) public_key_a_b_c = secret_key_a_b_c.to_public_key(secp) public_key_a = secret_key_a.to_public_key(secp) public_key_b = secret_key_b.to_public_key(secp) public_key_c = secret_key_c.to_public_key(secp) public_key_a_b_c_2 = PublicKey.from_combination( secp, [public_key_a, public_key_b, public_key_c]) assert public_key_a_b_c == public_key_a_b_c_2
def from_secret_key(secp: Secp256k1, key: SecretKey): public = key.to_public_key(secp) return Identifier.from_public_key(secp, public)
def test_secret_key(): secp = Secp256k1(None, FLAG_ALL) # (de)serialization key_a = SecretKey.random(secp) key_b = SecretKey.from_bytearray(secp, key_a.to_bytearray()) assert key_a == key_b # Too short with raises(Exception): SecretKey.from_bytearray(secp, bytearray([0x01] * (SECRET_KEY_SIZE - 1))) # Too long with raises(Exception): SecretKey.from_bytearray(secp, bytearray([0x01] * (SECRET_KEY_SIZE + 1))) # Zero with raises(Exception): SecretKey.from_bytearray(secp, bytearray([0] * SECRET_KEY_SIZE)) # Overflow with raises(Exception): SecretKey.from_bytearray(secp, bytearray([0xFF] * SECRET_KEY_SIZE)) # Top of range SecretKey.from_bytearray( secp, bytearray([ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x40 ])) # One past top of range with raises(Exception): SecretKey.from_bytearray( secp, bytearray([ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 ])) # a=a key_a = SecretKey.from_bytearray(secp, bytearray([0xCB] * SECRET_KEY_SIZE)) key_b = SecretKey.from_bytearray(secp, bytearray([0xCB] * SECRET_KEY_SIZE)) assert key_a == key_b # a!=b key_b = SecretKey.from_bytearray(secp, bytearray([0xCC] * SECRET_KEY_SIZE)) assert key_a != key_b # a+b key_a = SecretKey.from_bytearray(secp, bytearray([0xDD] * SECRET_KEY_SIZE)) key_b = SecretKey.from_bytearray(secp, bytearray([0x02] * SECRET_KEY_SIZE)) key_a.add_assign(secp, key_b) key_c = SecretKey.from_bytearray(secp, bytearray([0xDF] * SECRET_KEY_SIZE)) assert key_a == key_c # a+b = b+a key_a = SecretKey.random(secp) key_b = SecretKey.random(secp) key_a_b = key_a.add(secp, key_b) key_b_a = key_b.add(secp, key_a) assert key_a_b == key_b_a # Key addition where sum > order (N-1+N-2=N-3) key_a = SecretKey.from_bytearray( secp, bytearray([ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x40 ])) key_b = SecretKey.from_bytearray( secp, bytearray([ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F ])) key_a.add_assign(secp, key_b) key_c = SecretKey.from_bytearray( secp, bytearray([ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E ])) assert key_a == key_c # ab = ba key_a = SecretKey.random(secp) key_b = SecretKey.random(secp) key_ab = key_a.mul(secp, key_b) key_ba = key_b.mul(secp, key_a) assert key_ab == key_ba # c(a+b) = ca + cb key_a = SecretKey.random(secp) key_b = SecretKey.random(secp) key_c = SecretKey.random(secp) key_ca = key_a.mul(secp, key_c) key_cb = key_b.mul(secp, key_c) key_ca_b = key_a.add(secp, key_b) key_ca_b.mul_assign(secp, key_c) key_ca_cb = key_ca.add(secp, key_cb) assert key_ca_b == key_ca_cb