From cd987753607e7f2ce902b17ebb639c22c68476a3 Mon Sep 17 00:00:00 2001 From: Tomas Vrba Date: Tue, 31 Oct 2023 13:21:31 +0100 Subject: [PATCH] eip1559: add unit tests --- .../src/hww/api/ethereum/sign.rs | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/src/rust/bitbox02-rust/src/hww/api/ethereum/sign.rs b/src/rust/bitbox02-rust/src/hww/api/ethereum/sign.rs index 0c137c0ac0..713778d968 100644 --- a/src/rust/bitbox02-rust/src/hww/api/ethereum/sign.rs +++ b/src/rust/bitbox02-rust/src/hww/api/ethereum/sign.rs @@ -502,6 +502,24 @@ mod tests { .to_vec() })) ); + assert_eq!( + block_on(process(&Transaction::Eip1559(&pb::EthSignEip1559Request { + keypath: KEYPATH.to_vec(), + nonce: b"\x1f\xdc".to_vec(), + max_priority_fee_per_gas: b"".to_vec(), + max_fee_per_gas: b"\x01\x65\xa0\xbc\x00".to_vec(), + gas_limit: b"\x52\x08".to_vec(), + recipient: b"\x04\xf2\x64\xcf\x34\x44\x03\x13\xb4\xa0\x19\x2a\x35\x28\x14\xfb\xe9\x27\xb8\x85".to_vec(), + value: b"\x07\x5c\xf1\x25\x9e\x9c\x40\x00".to_vec(), + data: b"".to_vec(), + host_nonce_commitment: None, + chain_id: 0, + }))), + Ok(Response::Sign(pb::EthSignResponse { + signature: b"\x4c\xad\xef\xa3\x26\x2b\xd6\x29\x17\x56\x1d\xd8\x1b\x4d\x58\xa2\xb3\x33\x04\x1d\x72\x06\x15\x27\xfa\x67\xcb\x0e\x26\x76\x02\xc8\x51\xca\x81\xb2\xb3\x37\x5e\x84\x12\x98\x9e\x14\x87\xb6\x1e\x6b\xdf\x0c\x3c\x19\x25\xea\x60\xa5\xc3\x52\x5c\x62\x98\x85\x26\x09\x00" + .to_vec() + })) + ); } /// Test a transaction with an unusually high fee. @@ -555,6 +573,57 @@ mod tests { assert_eq!(unsafe { UI_COUNTER }, 1); } + /// Test an EIP-1559 transaction with an unusually high fee. + #[test] + fn test_high_fee_warning_eip1559() { + const KEYPATH: &[u32] = &[44 + HARDENED, 60 + HARDENED, 0 + HARDENED, 0, 0]; + + static mut UI_COUNTER: u32 = 0; + mock(Data { + ui_transaction_address_create: Some(Box::new(|_amount, _address| true)), + ui_transaction_fee_create: Some(Box::new(|total, fee, longtouch| { + assert_eq!(total, "0.59427728 ETH"); + assert_eq!(fee, "0.06371328 ETH"); + assert!(!longtouch); + true + })), + ui_confirm_create: Some(Box::new(move |params| { + match unsafe { + UI_COUNTER += 1; + UI_COUNTER + } { + 1 => { + assert_eq!(params.title, "High fee"); + assert_eq!(params.body, "The fee is 12.0%\nthe send amount.\nProceed?"); + assert!(params.longtouch); + true + } + _ => panic!("too many user confirmations"), + } + })), + ..Default::default() + }); + mock_unlocked(); + assert!(block_on(process(&Transaction::Eip1559(&pb::EthSignEip1559Request { + keypath: KEYPATH.to_vec(), + nonce: b"\x1f\xdc".to_vec(), + // fee=max_fee_per_gas*gas_limit=63713280000000000 + max_priority_fee_per_gas: b"".to_vec(), + max_fee_per_gas: b"\x01\x65\xa0\xbc\x00\x00".to_vec(), + gas_limit: b"\xa2\x08".to_vec(), + recipient: + b"\x04\xf2\x64\xcf\x34\x44\x03\x13\xb4\xa0\x19\x2a\x35\x28\x14\xfb\xe9\x27\xb8\x85" + .to_vec(), + // 530564000000000000 + value: b"\x07\x5c\xf1\x25\x9e\x9c\x40\x00".to_vec(), + data: b"".to_vec(), + host_nonce_commitment: None, + chain_id: 0, + }))) + .is_ok()); + assert_eq!(unsafe { UI_COUNTER }, 1); + } + /// Standard ETH transaction on an unusual keypath (Goerli on mainnet keypath) #[test] pub fn test_process_warn_unusual_keypath() { @@ -663,6 +732,61 @@ mod tests { ); } + /// EIP-1559 ETH transaction with an unknown data field. + #[test] + pub fn test_process_eip1559_transaction_with_data() { + const KEYPATH: &[u32] = &[44 + HARDENED, 60 + HARDENED, 0 + HARDENED, 0, 0]; + static mut CONFIRM_COUNTER: u32 = 0; + mock(Data { + ui_confirm_create: Some(Box::new(|params| { + match unsafe { CONFIRM_COUNTER } { + 0 | 1 => assert_eq!(params.title, "Unknown\ncontract"), + 2 => { + assert_eq!(params.title, "Transaction\ndata"); + assert_eq!(params.body, "666f6f20626172"); // "foo bar" in hex. + assert!(params.scrollable); + assert_eq!(params.display_size, 7); // length of "foo bar" + assert!(params.accept_is_nextarrow); + } + _ => panic!("too many user confirmations"), + } + unsafe { CONFIRM_COUNTER += 1 } + true + })), + ui_transaction_address_create: Some(Box::new(|amount, address| { + assert_eq!(amount, "0.530564 ETH"); + assert_eq!(address, "0x04F264Cf34440313B4A0192A352814FBe927b885"); + true + })), + ui_transaction_fee_create: Some(Box::new(|total, fee, longtouch| { + assert_eq!(total, "0.53069 ETH"); + assert_eq!(fee, "0.000126 ETH"); + assert!(longtouch); + true + })), + ..Default::default() + }); + mock_unlocked(); + assert_eq!( + block_on(process(&Transaction::Eip1559(&pb::EthSignEip1559Request { + keypath: KEYPATH.to_vec(), + nonce: b"\x1f\xdc".to_vec(), + max_priority_fee_per_gas: b"\x3b\x9a\xca\x00".to_vec(), + max_fee_per_gas: b"\x01\x65\xa0\xbc\x00".to_vec(), + gas_limit: b"\x52\x08".to_vec(), + recipient: b"\x04\xf2\x64\xcf\x34\x44\x03\x13\xb4\xa0\x19\x2a\x35\x28\x14\xfb\xe9\x27\xb8\x85".to_vec(), + value: b"\x07\x5c\xf1\x25\x9e\x9c\x40\x00".to_vec(), + data: b"foo bar".to_vec(), + host_nonce_commitment: None, + chain_id: 0, + }))), + Ok(Response::Sign(pb::EthSignResponse { + signature: b"\x15\x60\xc0\x01\x00\x00\x37\x76\xa0\x4b\x56\x6f\xd2\x54\x1a\x75\x82\xca\x4b\x0d\x8b\x23\x57\x5e\xfc\xad\xfb\x5f\x94\x27\xe3\x4c\x3c\x20\x89\xdd\x6d\xa3\xf0\x9e\x0c\x27\x9d\xb6\x82\x26\xf2\x62\xd7\xa7\xe0\x48\x97\xd8\x27\x07\x2f\x38\xde\xd5\xa5\x6d\xaf\x6e\x00" + .to_vec() + })) + ); + } + /// ERC20 transaction: recipient is an ERC20 contract address, and /// the data field contains an ERC20 transfer method invocation. #[test] @@ -702,6 +826,24 @@ mod tests { .to_vec() })) ); + assert_eq!( + block_on(process(&Transaction::Eip1559(&pb::EthSignEip1559Request { + keypath: KEYPATH.to_vec(), + nonce: b"\x23\x67".to_vec(), + max_priority_fee_per_gas: b"\x3b\x9a\xca\x00".to_vec(), + max_fee_per_gas: b"\x02\x7a\xca\x1a\x80".to_vec(), + gas_limit: b"\x01\xd0\x48".to_vec(), + recipient: b"\xda\xc1\x7f\x95\x8d\x2e\xe5\x23\xa2\x20\x62\x06\x99\x45\x97\xc1\x3d\x83\x1e\xc7".to_vec(), + value: b"".to_vec(), + data: b"\xa9\x05\x9c\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe6\xce\x0a\x09\x2a\x99\x70\x0c\xd4\xcc\xcc\xbb\x1f\xed\xc3\x9c\xf5\x3e\x63\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x65\xc0\x40".to_vec(), + host_nonce_commitment: None, + chain_id: 1, + }))), + Ok(Response::Sign(pb::EthSignResponse { + signature: b"\x31\x62\x48\x78\x80\xab\xde\xa1\xf3\x52\xd9\xa4\xe3\xd5\x60\x66\xf1\x22\xf0\x4f\xf1\x12\x11\x7c\x8c\xa3\xcd\x22\x0f\x16\x66\x30\x2d\xac\xd5\xe5\xe8\xda\x4c\xd3\x97\x04\xe3\x34\x43\xa9\xa7\xf3\x26\x02\xd3\x32\xbb\x52\x56\x7c\x2e\x34\xaa\xfe\x9e\xd4\x8f\xeb\x01" + .to_vec() + })) + ); } /// An ERC20 transaction which is not in our list of supported ERC20 tokens. @@ -742,6 +884,24 @@ mod tests { .to_vec() })) ); + assert_eq!( + block_on(process(&Transaction::Eip1559(&pb::EthSignEip1559Request { + keypath: KEYPATH.to_vec(), + nonce: b"\xb9".to_vec(), + max_priority_fee_per_gas: b"".to_vec(), + max_fee_per_gas: b"\x3b\x9a\xca\x00".to_vec(), + gas_limit: b"\x01\x09\x85".to_vec(), + recipient: b"\x9c\x23\xd6\x7a\xea\x7b\x95\xd8\x09\x42\xe3\x83\x6b\xcd\xf7\xe7\x08\xa7\x47\xc1".to_vec(), + value: b"".to_vec(), + data: b"\xa9\x05\x9c\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\x7b\x3d\x96\x9e\xac\xb7\x75\xa9\xf7\x9c\xab\xc6\x2e\xc4\xbb\x1d\x1c\xd6\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x98\xa6\x3c\xbe\xb8\x59\xd0\x27\xb0".to_vec(), + host_nonce_commitment: None, + chain_id: 0, + }))), + Ok(Response::Sign(pb::EthSignResponse { + signature: b"\x5c\x28\xfd\x7b\xf1\x21\x05\x63\xb0\x68\x05\x9d\x7d\x38\x97\x82\x4b\xae\x15\xba\xa6\x69\x10\x70\xb0\x6f\x89\x5b\xb5\x97\x58\xd1\x25\xf9\xb8\x10\x3f\x38\x64\x08\x7f\x32\x27\x0e\x84\x6a\x4e\x89\xe5\xbc\xbb\x45\x47\x99\x21\x69\x64\x9f\xbb\x04\xf3\x81\x7b\xf5\x00" + .to_vec() + })) + ); } #[test]