diff --git a/ci/ci.sh b/ci/ci.sh index da122261..c58a03f8 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -44,6 +44,7 @@ if [ "$STD" ]; then ./test_udp_socket.sh ./test_tls_client.sh ./test_tls_server.sh + ./test_eth_wallet.sh fi popd diff --git a/projects/web3/eth_wallet/host/src/cli.rs b/projects/web3/eth_wallet/host/src/cli.rs index 8d905462..68815186 100644 --- a/projects/web3/eth_wallet/host/src/cli.rs +++ b/projects/web3/eth_wallet/host/src/cli.rs @@ -87,6 +87,9 @@ pub enum Command { /// Sign a transaction. #[structopt(name = "sign-transaction")] SignTransaction(SignTransactionOpt), + /// Run tests + #[structopt(name = "test")] + Test, } #[derive(Debug, StructOpt)] diff --git a/projects/web3/eth_wallet/host/src/main.rs b/projects/web3/eth_wallet/host/src/main.rs index 44089955..6c4d71af 100644 --- a/projects/web3/eth_wallet/host/src/main.rs +++ b/projects/web3/eth_wallet/host/src/main.rs @@ -16,6 +16,7 @@ // under the License. mod cli; +mod tests; use optee_teec::{Context, Operation, ParamType, Uuid}; use optee_teec::{ParamNone, ParamTmpRef, ParamValue}; @@ -56,54 +57,92 @@ fn invoke_command(command: proto::Command, input: &[u8]) -> optee_teec::Result Result { + let serialized_output = invoke_command(proto::Command::CreateWallet, &[])?; + let output: proto::CreateWalletOutput = bincode::deserialize(&serialized_output)?; + Ok(output.wallet_id) +} + +pub fn remove_wallet(wallet_id: uuid::Uuid) -> Result<()> { + let input = proto::RemoveWalletInput { wallet_id }; + let _output = invoke_command(proto::Command::RemoveWallet, &bincode::serialize(&input)?)?; + Ok(()) +} + +pub fn derive_address(wallet_id: uuid::Uuid, hd_path: &str) -> Result<[u8; 20]> { + let input = proto::DeriveAddressInput { + wallet_id, + hd_path: hd_path.to_string(), + }; + let serialized_output = + invoke_command(proto::Command::DeriveAddress, &bincode::serialize(&input)?)?; + let output: proto::DeriveAddressOutput = bincode::deserialize(&serialized_output)?; + Ok(output.address) +} + +pub fn sign_transaction( + wallet_id: uuid::Uuid, + hd_path: &str, + chain_id: u64, + nonce: u128, + to: [u8; 20], + value: u128, + gas_price: u128, + gas: u128, +) -> Result> { + let transaction = proto::EthTransaction { + chain_id, + nonce, + to: Some(to), + value, + gas_price, + gas, + data: vec![], + }; + let input = proto::SignTransactionInput { + wallet_id, + hd_path: hd_path.to_string(), + transaction, + }; + let serialized_output = invoke_command( + proto::Command::SignTransaction, + &bincode::serialize(&input)?, + )?; + let output: proto::SignTransactionOutput = bincode::deserialize(&serialized_output)?; + Ok(output.signature) +} + fn main() -> Result<()> { let args = cli::Opt::from_args(); match args.command { cli::Command::CreateWallet(_opt) => { - let serialized_output = invoke_command(proto::Command::CreateWallet, &[])?; - let output: proto::CreateWalletOutput = bincode::deserialize(&serialized_output)?; - println!("Wallet ID: {}", output.wallet_id); + let wallet_id = create_wallet()?; + println!("Wallet ID: {}", wallet_id); } cli::Command::RemoveWallet(opt) => { - let input = proto::RemoveWalletInput { - wallet_id: opt.wallet_id, - }; - let _output = - invoke_command(proto::Command::RemoveWallet, &bincode::serialize(&input)?)?; + remove_wallet(opt.wallet_id)?; println!("Wallet removed"); } cli::Command::DeriveAddress(opt) => { - let input = proto::DeriveAddressInput { - wallet_id: opt.wallet_id, - hd_path: opt.hd_path, - }; - let serialized_output = - invoke_command(proto::Command::DeriveAddress, &bincode::serialize(&input)?)?; - let output: proto::DeriveAddressOutput = bincode::deserialize(&serialized_output)?; - println!("Address: 0x{}", hex::encode(&output.address)); - println!("Public key: {}", hex::encode(&output.public_key)); + let address = derive_address(opt.wallet_id, &opt.hd_path)?; + println!("Address: 0x{}", hex::encode(&address)); } cli::Command::SignTransaction(opt) => { - let transaction = proto::EthTransaction { - chain_id: opt.chain_id, - nonce: opt.nonce, - to: Some(opt.to), - value: opt.value, - gas_price: opt.gas_price, - gas: opt.gas, - data: vec![], - }; - let input = proto::SignTransactionInput { - wallet_id: opt.wallet_id, - hd_path: opt.hd_path, - transaction, - }; - let serialized_output = invoke_command( - proto::Command::SignTransaction, - &bincode::serialize(&input)?, + let signature = sign_transaction( + opt.wallet_id, + &opt.hd_path, + opt.chain_id, + opt.nonce, + opt.to, + opt.value, + opt.gas_price, + opt.gas, )?; - let output: proto::SignTransactionOutput = bincode::deserialize(&serialized_output)?; - println!("Signature: {}", hex::encode(&output.signature)); + println!("Signature: {}", hex::encode(&signature)); + } + cli::Command::Test => { + tests::tests::test_workflow(); + println!("Tests passed"); } _ => { bail!("Unsupported command"); diff --git a/projects/web3/eth_wallet/host/src/tests.rs b/projects/web3/eth_wallet/host/src/tests.rs new file mode 100644 index 00000000..3ed37959 --- /dev/null +++ b/projects/web3/eth_wallet/host/src/tests.rs @@ -0,0 +1,20 @@ +pub mod tests { + use crate::*; + + pub fn test_workflow() { + // Simulate the workflow of creating a wallet, deriving an address, and signing a transaction + let wallet_id = create_wallet().unwrap(); + let address = derive_address(wallet_id, "m/44'/60'/0'/0/0").unwrap(); + let result = sign_transaction( + wallet_id, + "m/44'/60'/0'/0/0", + 5, + 0, + address, + 100, + 1000000000, + 21000, + ); + assert!(result.is_ok()); + } +} diff --git a/tests/test_eth_wallet.sh b/tests/test_eth_wallet.sh new file mode 100755 index 00000000..3e414336 --- /dev/null +++ b/tests/test_eth_wallet.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set -xe + +# Include base script +source setup.sh + +# Copy TA and host binary +cp ../projects/web3/eth_wallet/ta/target/$TARGET_TA/release/*.ta shared +cp ../projects/web3/eth_wallet/host/target/$TARGET_HOST/release/eth_wallet-rs shared + +# Run script specific commands in QEMU +run_in_qemu "cp *.ta /lib/optee_armtz/\n" +run_in_qemu "./eth_wallet-rs test\n" +run_in_qemu "^C" + +# Script specific checks +{ + grep -q "Tests passed" screenlog.0 +} || { + cat -v screenlog.0 + false +} + +rm screenlog.0 \ No newline at end of file