Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ mod tests {
use super::*;
use crate::Wormhole;
use soroban_sdk::{Bytes, BytesN, IntoVal, Symbol, testutils::Events, vec};
use wormhole_soroban_client::{CHAIN_ID_STELLAR, GOVERNANCE_CHAIN_ID, GOVERNANCE_EMITTER, MODULE_CORE};
use wormhole_soroban_client::{
CHAIN_ID_STELLAR, GOVERNANCE_CHAIN_ID, GOVERNANCE_EMITTER, MODULE_CORE,
};

fn build_payload(env: &Env, module: [u8; 32], action: u8, chain: u16, fee: u64) -> Bytes {
let mut payload = Bytes::new(env);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ mod tests {
contracttype,
testutils::{Address as TestAddress, Events, Ledger},
};
use wormhole_soroban_client::{CHAIN_ID_STELLAR, GOVERNANCE_EMITTER, MODULE_CORE, NATIVE_TOKEN_ADDRESS};
use wormhole_soroban_client::{
CHAIN_ID_STELLAR, GOVERNANCE_EMITTER, MODULE_CORE, NATIVE_TOKEN_ADDRESS,
};

#[contracttype]
#[derive(Clone)]
Expand Down
64 changes: 62 additions & 2 deletions stellar/contracts/wormhole-contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ use crate::governance::{
guardian_set::{get_current_guardian_set_index, get_guardian_set, get_guardian_set_expiry},
};
use soroban_sdk::{Address, Bytes, BytesN, Env, Vec, contract, contractimpl};
use storage::StorageKey;
use wormhole_soroban_client::{
CHAIN_ID_STELLAR, ConsistencyLevel, GOVERNANCE_CHAIN_ID, GOVERNANCE_EMITTER, GuardianSetInfo,
WormholeCoreInterface, WormholeError,
STORAGE_TTL_EXTENSION, STORAGE_TTL_THRESHOLD, WormholeCoreInterface, WormholeError,
hash_address,
};

mod governance;
Expand Down Expand Up @@ -150,12 +152,35 @@ impl WormholeCoreInterface for Wormhole {
fn get_governance_emitter(env: Env) -> BytesN<32> {
BytesN::from_array(&env, &GOVERNANCE_EMITTER)
}

fn record_address(env: Env, address: Address) -> Result<BytesN<32>, WormholeError> {
let hash = hash_address(&env, &address);
let key = StorageKey::AddressTable(hash.clone());
env.storage().persistent().set(&key, &address);
env.storage()
.persistent()
.extend_ttl(&key, STORAGE_TTL_THRESHOLD, STORAGE_TTL_EXTENSION);
Ok(hash)
}

fn get_address_from_hash(env: Env, hash: BytesN<32>) -> Result<Address, WormholeError> {
let key = StorageKey::AddressTable(hash);
let Some(address) = env.storage().persistent().get(&key) else {
return Err(WormholeError::AddressNotFound);
};

env.storage()
.persistent()
.extend_ttl(&key, STORAGE_TTL_THRESHOLD, STORAGE_TTL_EXTENSION);

Ok(address)
}
}

#[cfg(test)]
mod tests {
use super::*;
use soroban_sdk::{Bytes, BytesN, Env, vec};
use soroban_sdk::{Bytes, BytesN, Env, testutils::Address as _, vec};
use wormhole_soroban_client::{
ACTION_GUARDIAN_SET_UPGRADE, ACTION_SET_MESSAGE_FEE, ACTION_TRANSFER_FEES,
GOVERNANCE_EMITTER, MODULE_CORE,
Expand Down Expand Up @@ -205,6 +230,41 @@ mod tests {
assert_eq!(client.get_governance_chain_id(), GOVERNANCE_CHAIN_ID);
}

#[test]
fn test_record_address_round_trip() {
let env = Env::default();
let (_contract_id, client) = deploy_initialized(&env);
let address = Address::generate(&env);
let hash = hash_address(&env, &address);

assert_eq!(client.record_address(&address), hash);
assert_eq!(client.get_address_from_hash(&hash), address);
}

#[test]
fn test_record_account_address_round_trip() {
let env = Env::default();
let (_contract_id, client) = deploy_initialized(&env);
let pk_bytes = BytesN::from_array(&env, &[7u8; 32]);
let address = crate::utils::address_from_ed25519_pk_bytes(&env, &pk_bytes);
let hash = hash_address(&env, &address);

assert_eq!(client.record_address(&address), hash);
assert_eq!(client.get_address_from_hash(&hash), address);
}

#[test]
fn test_get_address_from_hash_returns_not_found_for_missing_hash() {
let env = Env::default();
let (_contract_id, client) = deploy_initialized(&env);
let hash = BytesN::<32>::from_array(&env, &[9u8; 32]);

assert_eq!(
client.try_get_address_from_hash(&hash),
Err(Ok(WormholeError::AddressNotFound))
);
}

#[test]
fn test_get_governance_emitter_returns_const() {
let env = Env::default();
Expand Down
1 change: 0 additions & 1 deletion stellar/contracts/wormhole-contract/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,5 +341,4 @@ mod tests {
assert_eq!(s2, 2);
assert_eq!(client.get_emitter_sequence(&emitter), 3);
}

}
2 changes: 2 additions & 0 deletions stellar/contracts/wormhole-contract/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ pub enum StorageKey {
MessageFee,
/// Next sequence number for each emitter address.
EmitterSequence(Address),
/// Hash of an address used in `from/to` fields.
AddressTable(BytesN<32>),
}
2 changes: 2 additions & 0 deletions stellar/contracts/wormhole-soroban-client/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub enum WormholeError {
GuardianSetNotFound = 41,
/// Cannot overwrite an existing guardian set.
GuardianSetAlreadyExists = 42,
/// Requested address hash does not exist in storage.
AddressNotFound = 43,

// ========== Fee Errors (50-59) ==========
/// Emitter has not approved sufficient fee for message posting.
Expand Down
35 changes: 35 additions & 0 deletions stellar/contracts/wormhole-soroban-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ pub use types::*;

use soroban_sdk::{Address, Bytes, BytesN, Env, String, contractclient};

/// Computes the canonical Wormhole lookup hash for a Soroban address.
///
/// The hash input is the address StrKey string bytes.
pub fn hash_address(env: &Env, address: &Address) -> BytesN<32> {
env.crypto()
.keccak256(&address.to_string().to_bytes())
.to_bytes()
}

/// Complete public interface for the Wormhole Core contract.
///
/// Defines all contract entry points for VAA verification, governance actions,
Expand Down Expand Up @@ -271,6 +280,32 @@ pub trait WormholeCoreInterface {
/// # Returns
/// 32-byte governance emitter address
fn get_governance_emitter(env: Env) -> BytesN<32>;

/// Record a Soroban address in the hash lookup table.
///
/// Returns the canonical hash used as the lookup key.
fn record_address(env: Env, address: Address) -> Result<BytesN<32>, WormholeError>;

/// Resolve a previously recorded address hash.
fn get_address_from_hash(env: Env, hash: BytesN<32>) -> Result<Address, WormholeError>;
}

#[cfg(test)]
mod tests {
use super::*;
use soroban_sdk::testutils::Address as _;

#[test]
fn test_hash_address_uses_strkey_string_bytes() {
let env = Env::default();
let address = Address::generate(&env);
let expected = env
.crypto()
.keccak256(&address.to_string().to_bytes())
.to_bytes();

assert_eq!(hash_address(&env, &address), expected);
}
}

/// Public interface for the Wormhole Executor contract.
Expand Down
16 changes: 2 additions & 14 deletions stellar/integration-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,7 @@ impl TestContext {
}

pub fn fund_identity(&self, name: &str) {
run(Command::new("stellar").args([
"keys",
"fund",
"--network",
&self.network,
name,
]));
run(Command::new("stellar").args(["keys", "fund", "--network", &self.network, name]));
}

pub fn get_identity_address(&self, name: &str) -> String {
Expand All @@ -127,13 +121,7 @@ impl TestContext {

pub fn setup_identity(&self, name: &str) -> String {
let _ = Command::new("stellar").args(["keys", "rm", name]).output();
run(Command::new("stellar").args([
"keys",
"generate",
"--network",
&self.network,
name,
]));
run(Command::new("stellar").args(["keys", "generate", "--network", &self.network, name]));
let addr = run(Command::new("stellar").args(["keys", "address", name]))
.trim()
.to_string();
Expand Down
Loading