From 1739bf3f416a108e1497fda0a1fff2f464fcc6b8 Mon Sep 17 00:00:00 2001 From: jonghwan Date: Tue, 14 May 2024 16:08:52 +0800 Subject: [PATCH] Problem: missing verification api for wc 2.0 (fix #474) feat: enable personal signing in web3_v2.rs example feat: Add personal signature verification method feat: Update wallet_connect.cc to use Walletconnect2Client feat: Update wallet_connect.cc to use Walletconnect2Client tidy up reformat change warning error level chore: update cargo test command in CI workflow chore: add RUSTFLAGS to enable dead code linting in CI workflow chore: enable dead code linting in CI workflow chore: update cargo test command in CI workflow chore: update clippy command in CI workflow feat: Update wallet_connect.cc to use Walletconnect2Client feat: Update wallet_connect.cc to improve personal message signing and verification feat: Fix c++ walletconnect example to use wc 2.0 and add verification for sign_personal feat: Remove unnecessary include in wallet_connect.cc --- .github/workflows/ci.yml | 6 ++ CHANGELOG.md | 4 + demo/examples/src/wallet_connect.cc | 101 ++++++++++------------- extra-cpp-bindings/src/lib.rs | 7 ++ extra-cpp-bindings/src/walletconnect2.rs | 19 +++++ wallet-connect/examples/web3_v2.rs | 18 +++- 6 files changed, 94 insertions(+), 61 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b39b65d..96c1c950 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,8 @@ jobs: check: name: cargo check runs-on: ubuntu-latest + env: + RUSTFLAGS: -A dead_code steps: - uses: actions/checkout@v3 with: @@ -35,6 +37,8 @@ jobs: test: name: cargo test runs-on: ubuntu-latest + env: + RUSTFLAGS: -A dead_code steps: - uses: actions/checkout@v3 with: @@ -73,6 +77,8 @@ jobs: clippy: name: cargo clippy runs-on: ubuntu-latest + env: + RUSTFLAGS: -A dead_code steps: - uses: actions/checkout@v3 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4920cd8a..99086beb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog ## [Unreleased] +## [v0.0.27-alpha] - 2004-5-22 +- fix c++ walletconnect example to use wc 2.0 +- add verification for sign_personal + ## [v0.0.26-alpha] - 2024-4-24 - fix null `data` field in wc 2.0 diff --git a/demo/examples/src/wallet_connect.cc b/demo/examples/src/wallet_connect.cc index 386061dc..9e680214 100644 --- a/demo/examples/src/wallet_connect.cc +++ b/demo/examples/src/wallet_connect.cc @@ -34,35 +34,36 @@ rust::String address_to_hex_string(::std::array<::std::uint8_t, 20> bytes) { } // if session already exists, restore session -rust::Box make_new_client(std::string filename) { +rust::Box make_new_client(std::string filename) { std::ifstream file(filename.c_str()); if (file.is_open()) { std::string sessioninfostring((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - rust::Box client = - walletconnect_restore_client(sessioninfostring); + rust::Box client = + walletconnect2_restore_client(sessioninfostring); return client; } else { - rust::Box client = walletconnect_new_client( - "Defi WalletConnect example.", "http://localhost:8080/", - rust::Vec(), "Defi WalletConnect Web3 Example", - 338); // ChainId of Cronos Testnet + std::string projectid = std::getenv("NEXT_PUBLIC_PROJECT_ID") + ? std::getenv("NEXT_PUBLIC_PROJECT_ID") + : ""; + // assert projectid not "" + assert(projectid != ""); + + rust::Box client = walletconnect2_client_new( + "wss://relay.walletconnect.org", projectid, + "{\"eip155\":{\"methods\":[\"eth_sendTransaction\",\"eth_" + "signTransaction\",\"eth_sign\",\"personal_sign\",\"eth_" + "signTypedData\"],\"chains\":[\"eip155:338\"],\"events\":[" + "\"chainChanged\",\"accountsChanged\"]}}", + "{\"description\":\"Defi WalletConnect v2 " + "example.\",\"url\":\"http://localhost:8080/" + "\",\"icons\":[],\"name\":\"Defi WalletConnect Web3 Example\"}"); std::cout << "qrcode= " << client->get_connection_string() << std::endl; return client; } } - -class UserWalletConnectCallback : public WalletConnectCallback { - public: - UserWalletConnectCallback() {} - virtual ~UserWalletConnectCallback() {} - void onConnected(const WalletConnectSessionInfo &sessioninfo) const; - void onDisconnected(const WalletConnectSessionInfo &sessioninfo) const; - void onConnecting(const WalletConnectSessionInfo &sessioninfo) const; - void onUpdated(const WalletConnectSessionInfo &sessioninfo) const; -}; void print_session(const WalletConnectSessionInfo &sessioninfo) { std::cout << "connected: " << sessioninfo.connected << std::endl; std::cout << "chain_id: " << sessioninfo.chain_id << std::endl; @@ -78,50 +79,21 @@ void print_session(const WalletConnectSessionInfo &sessioninfo) { std::cout << "handshake_topic: " << sessioninfo.handshake_topic << std::endl; } -void UserWalletConnectCallback::onConnected( - const WalletConnectSessionInfo &sessioninfo) const { - std::cout << "user c++ onConnected" << std::endl; - print_session(sessioninfo); -} -void UserWalletConnectCallback::onDisconnected( - const WalletConnectSessionInfo &sessioninfo) const { - std::cout << "user c++ onDisconnected" << std::endl; - print_session(sessioninfo); - exit(0); -} -void UserWalletConnectCallback::onConnecting( - const WalletConnectSessionInfo &sessioninfo) const { - std::cout << "user c++ onConnecting" << std::endl; - print_session(sessioninfo); - // !!! Important !!! - // Comment out this line for actual test - exit(0); -} -void UserWalletConnectCallback::onUpdated( - const WalletConnectSessionInfo &sessioninfo) const { - std::cout << "user c++ onUpdated" << std::endl; - print_session(sessioninfo); -} int main(int argc, char *argv[]) { std::string filename = "sessioninfo.json"; try { - rust::Box client = make_new_client(filename); - WalletConnectCallback *usercallbackraw = - new UserWalletConnectCallback(); - std::unique_ptr usercallback(usercallbackraw); - client->setup_callback_blocking(std::move(usercallback)); + rust::Box client = make_new_client(filename); // Print the QR code on terminal rust::String uri = client->print_uri(); // program is blocked here for waiting connecting - WalletConnectEnsureSessionResult result = - client->ensure_session_blocking(); + WalletConnect2EnsureSessionResult result = + client->ensure_session_blocking(60000); // once connected, program continues - std::cout << "connected chain_id: " << result.chain_id << std::endl; - assert(result.addresses.size() > 0); + assert(result.eip155.accounts.size() > 0); // get the connected session info as string and save it into a file rust::String sessioninfo = client->save_client(); @@ -138,11 +110,21 @@ int main(int argc, char *argv[]) { // sign personal message if (test_personal) { /* message signing */ - rust::Vec sig1 = client->sign_personal_blocking( - "hello", result.addresses[0].address); + ::std::uint64_t testchainid = result.eip155.accounts[0].chain_id; + ::std::array<::std::uint8_t, 20> testaddress = + result.eip155.accounts[0].address.address; + std::cout << "chainid=" << testchainid << std::endl; + std::cout << "address=" + << address_to_hex_string(testaddress).c_str() + << std::endl; + rust::Vec sig1 = + client->sign_personal_blocking("hello", testaddress); std::cout << "signature=" << bytes_to_hex_string(sig1).c_str() << std::endl; std::cout << "signature length=" << sig1.size() << std::endl; + bool verifyresult = + client->verify_personal_blocking("hello", sig1, testaddress); + std::cout << "verify result=" << verifyresult << std::endl; } // send transaction @@ -166,12 +148,13 @@ int main(int argc, char *argv[]) { // info.to = "0x...."; info.to = rust::String( std::string("0x") + - address_to_hex_string(result.addresses[0].address).c_str()); + address_to_hex_string(result.eip155.accounts[0].address.address) + .c_str()); info.value = "1000000000000000000"; // 1 TCRO - info.common.chainid = result.chain_id; + info.common.chainid = result.eip155.accounts[0].chain_id; rust::Vec tx_hash = client->send_eip155_transaction_blocking( - info, result.addresses[0].address); + info, result.eip155.accounts[0].address.address); std::cout << "transaction_hash=" << bytes_to_hex_string(tx_hash).c_str() << std::endl; @@ -205,7 +188,8 @@ int main(int argc, char *argv[]) { assert(erc20.decimals() == 18); rust::String from_address = rust::String( std::string("0x") + - address_to_hex_string(result.addresses[0].address).c_str()); + address_to_hex_string(result.eip155.accounts[0].address.address) + .c_str()); U256 erc20_balance = erc20.balance_of(from_address); std::cout << "erc20 balance=" << erc20_balance.to_string() << std::endl; @@ -222,13 +206,14 @@ int main(int argc, char *argv[]) { } })"; - common.chainid = result.chain_id; + common.chainid = result.eip155.accounts[0].chain_id; common.web3api_url = "https://evm-dev-t3.cronos.org"; // TODO unnessary for // walletconnect rust::Vec tx_hash = client->send_contract_transaction( - contract_action, common, result.addresses[0].address); + contract_action, common, + result.eip155.accounts[0].address.address); std::cout << "transaction_hash=" << bytes_to_hex_string(tx_hash).c_str() << std::endl; diff --git a/extra-cpp-bindings/src/lib.rs b/extra-cpp-bindings/src/lib.rs index fe464792..25c7dde2 100644 --- a/extra-cpp-bindings/src/lib.rs +++ b/extra-cpp-bindings/src/lib.rs @@ -347,6 +347,13 @@ mod ffi { message: String, address: [u8; 20], ) -> Result>; + /// verify message + pub fn verify_personal_blocking( + self: &mut Walletconnect2Client, + message: String, + signature_bytes: Vec, + user_address: [u8; 20], + ) -> Result; pub fn ping_blocking(self: &mut Walletconnect2Client, waitmillis: u64) -> Result; /// build cronos(eth) eip155 transaction diff --git a/extra-cpp-bindings/src/walletconnect2.rs b/extra-cpp-bindings/src/walletconnect2.rs index a19df8cd..c551e500 100644 --- a/extra-cpp-bindings/src/walletconnect2.rs +++ b/extra-cpp-bindings/src/walletconnect2.rs @@ -141,6 +141,25 @@ impl Walletconnect2Client { } } + // signature: 65 bytes (r:32, s:32,v:1) + pub fn verify_personal_blocking( + &mut self, + message: String, + signature_bytes: Vec, + user_address: [u8; 20], + ) -> Result { + let address = ethers::types::Address::from_slice(&user_address); + let signature = Signature::try_from(signature_bytes.as_slice()) + .map_err(|e| anyhow!("Invalid signature: {}", e))?; + + Ok(signature.verify(message, address).is_ok()) + } + + // signature + // r: 32 bytes + // s: 32 bytees + // v: 1 byte + // total 65 bytes pub fn sign_personal_blocking( &mut self, message: String, diff --git a/wallet-connect/examples/web3_v2.rs b/wallet-connect/examples/web3_v2.rs index b5665e9c..59dd3b68 100644 --- a/wallet-connect/examples/web3_v2.rs +++ b/wallet-connect/examples/web3_v2.rs @@ -264,9 +264,9 @@ async fn main() -> Result<(), Box> { }; let test_ping = false; - let test_personal_signing = false; + let test_personal_signing = true; let test_sign_tx = false; - let test_send_tx = true; + let test_send_tx = false; let test_send_typedtx = false; let test_event_listening = false; @@ -288,9 +288,21 @@ async fn main() -> Result<(), Box> { } if test_personal_signing { // 0xaddress + let message = "Hello Crypto"; let address1 = namespaces.get_ethereum_addresses()[0].address.clone(); - let sig1 = client.personal_sign("Hello Crypto", &address1).await?; + let sig1 = client.personal_sign(message, &address1).await?; println!("sig1: {:?}", sig1); + + // Verify the signature + let signer = sig1.verify(message, address1); + match signer { + Ok(_) => { + println!("Signature verified"); + } + Err(err) => { + println!("Error verifying signature: {:?}", err); + } + } } if test_sign_tx {