this works, but I need to verify the signature onchain, and this is where it fails.
this is my contract:
// SPDX-License-Identifier: Apache-2.0
contract;
use std::{
context::*,
asset::*,
b512::B512,
ecr::ec_recover_address,
};
use std::hash::*;
use helpers::{
utils::*,
context::Account
};
abi Signature {
fn verify_signature(
account: Account,
balance: u64,
signature: B512,
) -> (b256, b256, bool);
}
struct Message {
account: Account,
balance: u64,
}
impl Hash for Message {
fn hash(self, ref mut state: Hasher) {
self.account.hash(state);
self.balance.hash(state);
}
}
impl Signature for Contract {
fn verify_signature(
account: Account,
balance: u64,
signature: B512,
) -> (b256, b256, bool) {
let msg_hash = sha256(Message { account, balance });
let recovered_address = ec_recover_address(signature, msg_hash).unwrap().bits();
(msg_hash, recovered_address, account.value == recovered_address)
}
}
in Typescript (cc @nerses, this is how you “hash” structs)
const myStruct = {
account: {
// "deployer" address bits: 0xe373620c9fdae7e928ee42001314bf8ab9638cd82a61f4e19a4e27133a419f7b
value: deployer.address.toB256(),
is_contract: false
},
balance: 100_000_000,
}
const structCoder = new StructCoder("MessageStruct", {
account: new StructCoder("Account", {
value: new BigNumberCoder("u256"),
is_contract: new BooleanCoder(),
}),
balance: new BigNumberCoder("u64"),
})
const encodedStruct: Uint8Array = structCoder.encode(myStruct)
// 0xa4057e6a07b141149a0e94c2254f06c5bc31dc4ef36689d8e895df3ccf91f109
const message = hexlify(sha256(encodedStruct))
const signatureContract = await deploy("Signature", deployer)
// ---------- this works --------------
const deployerSigner = new Signer("priv key")
const signedMessage = await deployer.signMessage(message)
const hashedMessage = hashMessage(message)
const localRecoveredSigner = Signer.recoverAddress(hashedMessage, signedMessage).toB256() // 0xe373620c9fdae7e928ee42001314bf8ab9638cd82a61f4e19a4e27133a419f7b
// ---------- this FAILS to yield the right result --------------
const recoveredSigner = await signatureContract.functions
.verify_signature(myStruct.account, myStruct.balance, signedMessage)
.get()
// 0xe373620c9fdae7e928ee42001314bf8ab9638cd82a61f4e19a4e27133a419f7b
const deployerAddress = myStruct.account.value
// 0xf64013bdbbf68430d24115ac8be5744ae8d6b7f032bff54bc6141a0681e58a8b
const contractRecoveredSigner = recoveredSigner.value[1]
expect(message === recoveredSigner.value[0]).to.be.true
expect(recoveredSigner.value[2]).to.be.true // this doesn't work
Given that “deployMessage” hashes the message before signing, I’ve even gone as far as re-sha256ing the message onchain like so:
let msg_hash = sha256(Message { account, balance });
let recovered_address = ec_recover_address(signature, sha256(msg_hash)).unwrap().bits();
but this doesn’t help either