wormhole/sui/wormhole/sources/guardian_pubkey.move

83 lines
2.9 KiB
Plaintext

/// Guardian keys are EVM-style 20 byte addresses
/// That is, they are computed by taking the last 20 bytes of the keccak256
/// hash of their 64 byte secp256k1 public key.
module wormhole::guardian_pubkey {
use sui::ecdsa_k1::{Self as ecdsa};
use std::vector;
use wormhole::keccak256::keccak256;
/// An error occurred while deserializing, for example due to wrong input size.
const E_DESERIALIZE: u64 = 1;
struct Address has store, drop, copy {
bytes: vector<u8>
}
/// Deserializes a raw byte sequence into an address.
/// Aborts if the input is not 20 bytes long.
public fun from_bytes(bytes: vector<u8>): Address {
assert!(std::vector::length(&bytes) == 20, E_DESERIALIZE);
Address { bytes }
}
/// Computes the address from a 64 byte public key.
public fun from_pubkey(pubkey: vector<u8>): Address {
assert!(std::vector::length(&pubkey) == 64, E_DESERIALIZE);
let hash = keccak256(pubkey);
let address = vector::empty<u8>();
let i = 0;
while (i < 20) {
vector::push_back(&mut address, vector::pop_back(&mut hash));
i = i + 1;
};
vector::reverse(&mut address);
Address { bytes: address }
}
/// Recovers the address from a signature and message.
/// This is known as 'ecrecover' in EVM.
public fun from_signature(
message: vector<u8>,
recovery_id: u8,
sig: vector<u8>,
): Address {
// sui's ecrecover function takes a 65 byte array (signature + recovery byte)
vector::push_back(&mut sig, recovery_id);
let pubkey = ecdsa::ecrecover(&sig, &message);
let pubkey = ecdsa::decompress_pubkey(&pubkey);
// decompress_pubkey returns 65 bytes, the first byte is not relevant to
// us, so we remove it
vector::remove(&mut pubkey, 0);
from_pubkey(pubkey)
}
}
#[test_only]
module wormhole::guardian_pubkey_test {
use wormhole::guardian_pubkey;
#[test]
public fun from_pubkey_test() {
// devnet guardian public key
let pubkey = x"d4a4629979f0c9fa0f0bb54edf33f87c8c5a1f42c0350a30d68f7e967023e34e495a8ebf5101036d0fd66e3b0a8c7c61b65fceeaf487ab3cd1b5b7b50beb7970";
let expected_address = guardian_pubkey::from_bytes(x"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe");
let address = guardian_pubkey::from_pubkey(pubkey);
assert!(address == expected_address, 0);
}
#[test]
public fun from_signature() {
let sig = x"38535089d6eec412a00066f84084212316ee3451145a75591dbd4a1c2a2bff442223f81e58821bfa4e8ffb80a881daf7a37500b04dfa5719fff25ed4cec8dda3";
let msg = x"43f3693ccdcb4400e1d1c5c8cec200153bd4b3d167e5b9fe5400508cf8717880";
let addr = guardian_pubkey::from_signature(msg, 0x01, sig);
let expected_addr = guardian_pubkey::from_bytes(x"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe");
assert!(addr == expected_addr, 0);
}
}