2020-08-03 15:28:20 +00:00
|
|
|
from common import unittest, await_result, H_
|
|
|
|
|
2021-03-30 08:57:07 +00:00
|
|
|
import storage.cache
|
2020-08-03 15:28:20 +00:00
|
|
|
from trezor import wire
|
2021-01-13 14:14:06 +00:00
|
|
|
from trezor.crypto.curve import secp256k1
|
|
|
|
from trezor.crypto.hashlib import sha256
|
2021-03-23 12:35:27 +00:00
|
|
|
from trezor.messages import AuthorizeCoinJoin
|
|
|
|
from trezor.messages import TxInput
|
|
|
|
from trezor.messages import TxOutput
|
|
|
|
from trezor.messages import SignTx
|
2021-01-13 14:14:06 +00:00
|
|
|
from trezor.messages import TxAckPaymentRequest
|
2021-03-23 12:35:27 +00:00
|
|
|
from trezor.enums import InputScriptType, OutputScriptType
|
2021-01-13 14:14:06 +00:00
|
|
|
from trezor.utils import HashWriter
|
2020-08-03 15:28:20 +00:00
|
|
|
|
|
|
|
from apps.common import coins
|
|
|
|
from apps.bitcoin.authorization import CoinJoinAuthorization
|
|
|
|
from apps.bitcoin.sign_tx.approvers import CoinJoinApprover
|
2020-09-30 16:50:25 +00:00
|
|
|
from apps.bitcoin.sign_tx.bitcoin import Bitcoin
|
|
|
|
from apps.bitcoin.sign_tx.tx_info import TxInfo
|
2021-01-13 14:14:06 +00:00
|
|
|
from apps.bitcoin import writers
|
2020-08-03 15:28:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TestApprover(unittest.TestCase):
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.coin = coins.by_name('Bitcoin')
|
|
|
|
self.fee_per_anonymity_percent = 0.003
|
2021-01-13 14:14:06 +00:00
|
|
|
self.coordinator_name = "www.example.com"
|
2020-08-03 15:28:20 +00:00
|
|
|
|
|
|
|
self.msg_auth = AuthorizeCoinJoin(
|
2021-01-13 14:14:06 +00:00
|
|
|
coordinator=self.coordinator_name,
|
2020-08-03 15:28:20 +00:00
|
|
|
max_total_fee=40000,
|
2021-03-30 08:57:07 +00:00
|
|
|
fee_per_anonymity=int(self.fee_per_anonymity_percent * 10**9),
|
2020-08-03 15:28:20 +00:00
|
|
|
address_n=[H_(84), H_(0), H_(0)],
|
|
|
|
coin_name=self.coin.coin_name,
|
|
|
|
script_type=InputScriptType.SPENDWITNESS,
|
|
|
|
)
|
2021-03-30 08:57:07 +00:00
|
|
|
storage.cache.start_session()
|
2020-08-03 15:28:20 +00:00
|
|
|
|
|
|
|
def test_coinjoin_lots_of_inputs(self):
|
|
|
|
denomination = 10000000
|
|
|
|
|
|
|
|
# Other's inputs.
|
|
|
|
inputs = [
|
2020-09-23 11:08:26 +00:00
|
|
|
TxInput(
|
2020-09-16 12:51:19 +00:00
|
|
|
prev_hash=b"",
|
|
|
|
prev_index=0,
|
2020-08-03 15:28:20 +00:00
|
|
|
amount=denomination + 1000000 * (i + 1),
|
2021-10-29 19:28:37 +00:00
|
|
|
script_pubkey=bytes(22),
|
2020-08-03 15:28:20 +00:00
|
|
|
script_type=InputScriptType.EXTERNAL,
|
2020-07-09 15:58:09 +00:00
|
|
|
sequence=0xffffffff,
|
2020-08-03 15:28:20 +00:00
|
|
|
) for i in range(99)
|
|
|
|
]
|
|
|
|
|
|
|
|
# Our input.
|
|
|
|
inputs.insert(
|
|
|
|
30,
|
2020-09-23 11:08:26 +00:00
|
|
|
TxInput(
|
2020-09-16 12:51:19 +00:00
|
|
|
prev_hash=b"",
|
|
|
|
prev_index=0,
|
2020-08-03 15:28:20 +00:00
|
|
|
address_n=[H_(84), H_(0), H_(0), 0, 1],
|
|
|
|
amount=denomination + 1000000,
|
|
|
|
script_type=InputScriptType.SPENDWITNESS,
|
2020-07-09 15:58:09 +00:00
|
|
|
sequence=0xffffffff,
|
2020-08-03 15:28:20 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Other's CoinJoined outputs.
|
|
|
|
outputs = [
|
2020-09-23 11:08:26 +00:00
|
|
|
TxOutput(
|
2021-01-13 14:14:06 +00:00
|
|
|
address="",
|
2020-08-03 15:28:20 +00:00
|
|
|
amount=denomination,
|
|
|
|
script_type=OutputScriptType.PAYTOWITNESS,
|
2021-01-13 14:14:06 +00:00
|
|
|
payment_req_index=0,
|
2020-08-03 15:28:20 +00:00
|
|
|
) for i in range(99)
|
|
|
|
]
|
|
|
|
|
|
|
|
# Our CoinJoined output.
|
|
|
|
outputs.insert(
|
|
|
|
40,
|
2020-09-23 11:08:26 +00:00
|
|
|
TxOutput(
|
2021-01-13 14:14:06 +00:00
|
|
|
address="",
|
2020-08-03 15:28:20 +00:00
|
|
|
address_n=[H_(84), H_(0), H_(0), 0, 2],
|
|
|
|
amount=denomination,
|
|
|
|
script_type=OutputScriptType.PAYTOWITNESS,
|
2021-01-13 14:14:06 +00:00
|
|
|
payment_req_index=0,
|
2020-08-03 15:28:20 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2021-03-30 08:57:07 +00:00
|
|
|
coordinator_fee = int(self.fee_per_anonymity_percent / 100 * len(outputs) * denomination)
|
2020-08-03 15:28:20 +00:00
|
|
|
fees = coordinator_fee + 10000
|
|
|
|
total_coordinator_fee = coordinator_fee * len(outputs)
|
|
|
|
|
|
|
|
# Other's change-outputs.
|
|
|
|
outputs.extend(
|
2020-09-23 11:08:26 +00:00
|
|
|
TxOutput(
|
2021-01-13 14:14:06 +00:00
|
|
|
address="",
|
2020-08-03 15:28:20 +00:00
|
|
|
amount=1000000 * (i + 1) - fees,
|
|
|
|
script_type=OutputScriptType.PAYTOWITNESS,
|
2021-01-13 14:14:06 +00:00
|
|
|
payment_req_index=0,
|
2020-08-03 15:28:20 +00:00
|
|
|
) for i in range(99)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Our change-output.
|
|
|
|
outputs.append(
|
2020-09-23 11:08:26 +00:00
|
|
|
TxOutput(
|
2021-01-13 14:14:06 +00:00
|
|
|
address="",
|
2020-08-03 15:28:20 +00:00
|
|
|
address_n=[H_(84), H_(0), H_(0), 1, 1],
|
|
|
|
amount=1000000 - fees,
|
|
|
|
script_type=OutputScriptType.PAYTOWITNESS,
|
2021-01-13 14:14:06 +00:00
|
|
|
payment_req_index=0,
|
2020-08-03 15:28:20 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Coordinator's output.
|
|
|
|
outputs.append(
|
2020-09-23 11:08:26 +00:00
|
|
|
TxOutput(
|
2021-01-13 14:14:06 +00:00
|
|
|
address="",
|
2020-08-03 15:28:20 +00:00
|
|
|
amount=total_coordinator_fee,
|
|
|
|
script_type=OutputScriptType.PAYTOWITNESS,
|
2021-01-13 14:14:06 +00:00
|
|
|
payment_req_index=0,
|
2020-08-03 15:28:20 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2021-03-30 08:57:07 +00:00
|
|
|
authorization = CoinJoinAuthorization(self.msg_auth)
|
2020-08-03 15:28:20 +00:00
|
|
|
tx = SignTx(outputs_count=len(outputs), inputs_count=len(inputs), coin_name=self.coin.coin_name, lock_time=0)
|
|
|
|
approver = CoinJoinApprover(tx, self.coin, authorization)
|
2020-09-30 16:50:25 +00:00
|
|
|
signer = Bitcoin(tx, None, self.coin, approver)
|
2020-08-03 15:28:20 +00:00
|
|
|
|
2021-01-13 14:14:06 +00:00
|
|
|
# Compute payment request signature.
|
|
|
|
# Private key of m/0h for "all all ... all" seed.
|
|
|
|
private_key = b'?S\ti\x8b\xc5o{,\xab\x03\x194\xea\xa8[_:\xeb\xdf\xce\xef\xe50\xf17D\x98`\xb9dj'
|
|
|
|
h_pr = HashWriter(sha256())
|
|
|
|
writers.write_bytes_fixed(h_pr, b"SL\x00\x24", 4)
|
|
|
|
writers.write_bytes_prefixed(h_pr, b"") # Empty nonce.
|
|
|
|
writers.write_bytes_prefixed(h_pr, self.coordinator_name.encode())
|
|
|
|
writers.write_bitcoin_varint(h_pr, 0) # No memos.
|
|
|
|
writers.write_uint32(h_pr, self.coin.slip44)
|
|
|
|
h_outputs = HashWriter(sha256())
|
|
|
|
for txo in outputs:
|
|
|
|
writers.write_uint64(h_outputs, txo.amount)
|
|
|
|
writers.write_bytes_prefixed(h_outputs, txo.address.encode())
|
|
|
|
writers.write_bytes_fixed(h_pr, h_outputs.get_digest(), 32)
|
|
|
|
signature = secp256k1.sign(private_key, h_pr.get_digest())
|
|
|
|
|
|
|
|
tx_ack_payment_req = TxAckPaymentRequest(
|
|
|
|
recipient_name=self.coordinator_name,
|
|
|
|
signature=signature,
|
|
|
|
)
|
|
|
|
|
2020-08-03 15:28:20 +00:00
|
|
|
for txi in inputs:
|
|
|
|
if txi.script_type == InputScriptType.EXTERNAL:
|
|
|
|
approver.add_external_input(txi)
|
|
|
|
else:
|
2020-08-22 15:28:29 +00:00
|
|
|
await_result(approver.add_internal_input(txi))
|
2020-08-03 15:28:20 +00:00
|
|
|
|
2021-01-13 14:14:06 +00:00
|
|
|
await_result(approver.add_payment_request(tx_ack_payment_req, None))
|
2020-08-03 15:28:20 +00:00
|
|
|
for txo in outputs:
|
|
|
|
if txo.address_n:
|
|
|
|
approver.add_change_output(txo, script_pubkey=bytes(22))
|
|
|
|
else:
|
|
|
|
await_result(approver.add_external_output(txo, script_pubkey=bytes(22)))
|
|
|
|
|
2020-09-30 16:45:10 +00:00
|
|
|
await_result(approver.approve_tx(TxInfo(signer, tx), []))
|
2020-08-03 15:28:20 +00:00
|
|
|
|
|
|
|
def test_coinjoin_input_account_depth_mismatch(self):
|
2021-03-30 08:57:07 +00:00
|
|
|
authorization = CoinJoinAuthorization(self.msg_auth)
|
2020-08-03 15:28:20 +00:00
|
|
|
tx = SignTx(outputs_count=201, inputs_count=100, coin_name=self.coin.coin_name, lock_time=0)
|
|
|
|
approver = CoinJoinApprover(tx, self.coin, authorization)
|
|
|
|
|
2020-09-23 11:08:26 +00:00
|
|
|
txi = TxInput(
|
2020-09-16 12:51:19 +00:00
|
|
|
prev_hash=b"",
|
|
|
|
prev_index=0,
|
2020-08-03 15:28:20 +00:00
|
|
|
address_n=[H_(49), H_(0), H_(0), 0],
|
|
|
|
amount=10000000,
|
|
|
|
script_type=InputScriptType.SPENDWITNESS
|
|
|
|
)
|
|
|
|
|
|
|
|
with self.assertRaises(wire.ProcessError):
|
2020-08-22 15:28:29 +00:00
|
|
|
await_result(approver.add_internal_input(txi))
|
2020-08-03 15:28:20 +00:00
|
|
|
|
|
|
|
def test_coinjoin_input_account_path_mismatch(self):
|
2021-03-30 08:57:07 +00:00
|
|
|
authorization = CoinJoinAuthorization(self.msg_auth)
|
2020-08-03 15:28:20 +00:00
|
|
|
tx = SignTx(outputs_count=201, inputs_count=100, coin_name=self.coin.coin_name, lock_time=0)
|
|
|
|
approver = CoinJoinApprover(tx, self.coin, authorization)
|
|
|
|
|
2020-09-23 11:08:26 +00:00
|
|
|
txi = TxInput(
|
2020-09-16 12:51:19 +00:00
|
|
|
prev_hash=b"",
|
|
|
|
prev_index=0,
|
2020-08-03 15:28:20 +00:00
|
|
|
address_n=[H_(49), H_(0), H_(0), 0, 2],
|
|
|
|
amount=10000000,
|
|
|
|
script_type=InputScriptType.SPENDWITNESS
|
|
|
|
)
|
|
|
|
|
|
|
|
with self.assertRaises(wire.ProcessError):
|
2020-08-22 15:28:29 +00:00
|
|
|
await_result(approver.add_internal_input(txi))
|
2020-08-03 15:28:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|