Skip to content

Commit

Permalink
feat: use configurables for proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
DefiCake committed Jun 21, 2024
1 parent 11b1c22 commit b9f145b
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 108 deletions.
39 changes: 31 additions & 8 deletions packages/fungible-token/bridge-fungible-token/proxy/src/proxy.sw
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,20 @@ abi Proxy {

#[storage(read, write)]
fn _proxy_change_owner(new_owner: Identity);

#[storage(read, write)]
fn _proxy_revoke_ownership();
}

configurable {
INITIAL_OWNER: State = State::Uninitialized,
INITIAL_TARGET: ContractId = ContractId::from(ZERO_B256)
}

#[namespace(SRC14)]
storage {
// target is at sha256("storage_SRC14_0")
target: ContractId = ContractId::zero(),
target: Option<ContractId> = None,
// owner is at sha256("storage_SRC14_1")
owner: State = State::Uninitialized,
}
Expand All @@ -39,23 +47,27 @@ impl SRC14 for Contract {
fn set_proxy_target(new_target: ContractId) {
only_owner();
require(new_target.bits() != ZERO_B256, ProxyErrors::IdentityZero);
storage.target.write(new_target);
storage.target.write(Some(new_target));
}
}

#[fallback]
#[storage(read)]
fn fallback() {
// pass through any other method call to the target
run_external(storage.target.read())
run_external(storage.target.read().unwrap_or(INITIAL_TARGET))
}

#[storage(read)]
fn only_owner() {

let owner = match storage.owner.read() {
State::Uninitialized => INITIAL_OWNER,
state => state,
};

require(
storage
.owner
.read() == State::Initialized(msg_sender().unwrap()),
owner == State::Initialized(msg_sender().unwrap()),
AccessError::NotOwner,
);
}
Expand All @@ -64,12 +76,17 @@ impl Proxy for Contract {

#[storage(read)]
fn _proxy_owner() -> State {
storage.owner.read()
let owner = storage.owner.read();

match owner {
State::Uninitialized => INITIAL_OWNER,
_ => owner,
}
}

#[storage(read)]
fn _proxy_target() -> ContractId {
storage.target.read()
storage.target.read().unwrap_or(INITIAL_TARGET)
}

#[storage(read, write)]
Expand All @@ -78,4 +95,10 @@ impl Proxy for Contract {
require(new_owner.bits() != ZERO_B256, ProxyErrors::IdentityZero);
storage.owner.write(State::Initialized(new_owner));
}

#[storage(read,write)]
fn _proxy_revoke_ownership() {
only_owner();
storage.owner.write(State::Revoked);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ mod tests {
State::Initialized(fuels::types::Identity::Address(address))
if address == wallet.address().clone().into()
),
"Ownership was not initialized or owner is not the expected address"
"Ownership was not initialized or owner is not the expected address. Value: {:?}",
owner
);

let target = proxy
Expand Down Expand Up @@ -110,12 +111,60 @@ mod tests {
State::Initialized(fuels::types::Identity::Address(address))
if address == new_owner.address().clone().into()
),
"Ownership was not initialized or owner is not the expected address"
"Ownership was not initialized or owner is not the expected address. Value: {:?}",
owner
);

Ok(())
}

#[tokio::test]
async fn proxy_revoke_ownership() -> anyhow::Result<()> {
let mut wallet = create_wallet();

let configurables: Option<BridgeFungibleTokenContractConfigurables> = None;

let (proxy_id, _implementation_contract_id) =
get_contract_ids(&wallet, configurables.clone());

let wallet_funds = (DEFAULT_COIN_AMOUNT, AssetId::default());

let (_, bridge, _) = setup_environment(
&mut wallet,
vec![wallet_funds],
vec![],
None,
None,
configurables,
)
.await;

let proxy = BridgeProxy::new(bridge.contract_id().clone(), wallet.clone());

let _tx_id = proxy
.methods()
._proxy_revoke_ownership()
.with_contract_ids(&[proxy_id.into()])
.call()
.await?
.tx_id
.unwrap();

let owner = proxy
.methods()
._proxy_owner()
.with_contract_ids(&[proxy_id.into()])
.simulate()
.await?
.value;

assert_eq!(owner, State::Revoked);

Ok(())
}



#[tokio::test]
async fn proxy_change_owner_cannot_be_zero() -> anyhow::Result<()> {
let mut wallet = create_wallet();
Expand Down Expand Up @@ -322,6 +371,4 @@ mod tests {

Ok(())
}


}
73 changes: 14 additions & 59 deletions packages/fungible-token/bridge-fungible-token/tests/utils/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ use crate::utils::{
};
use ethers::abi::Token;
use fuel_core_types::{
fuel_crypto::{Hasher, SecretKey},
fuel_crypto::SecretKey,
fuel_tx::{Bytes32, Output, TxId, TxPointer, UtxoId},
fuel_types::{canonical::Deserialize, Nonce, Word},
fuel_types::{Nonce, Word},
};
use fuels::core::{codec::ABIEncoder, traits::Tokenizable};


use fuels::{
accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount},
prelude::{
abigen, setup_custom_assets_coins, setup_test_provider, Address, AssetConfig, AssetId,
Bech32ContractId, Contract, ContractId, LoadConfiguration, Provider, TxPolicies,
},
programs::contract::StorageConfiguration,

test_helpers::{setup_single_message, DEFAULT_COIN_AMOUNT},
tx::StorageSlot,

types::{coin::Coin, input::Input, message::Message, tx_status::TxStatus, Bits256, U256},
};
use sha2::Digest;
Expand Down Expand Up @@ -232,13 +232,13 @@ pub(crate) async fn setup_environment(
.await
.unwrap();

let storage_configuration = get_proxy_storage_config(
(*implementation_contract_id.clone().hash).into(),
State::Initialized(wallet.address().clone().into()),
);
let proxy_configurables = BridgeProxyConfigurables::default()
.with_INITIAL_OWNER(State::Initialized(wallet.address().into())).unwrap()
.with_INITIAL_TARGET(implementation_contract_id.clone().into()).unwrap();

let proxy_config =
LoadConfiguration::default().with_storage_configuration(storage_configuration);
LoadConfiguration::default().with_configurables(proxy_configurables);

let proxy_contract_id = Contract::load_from(BRIDGE_PROXY_BINARY, proxy_config)
.unwrap()
.deploy(&wallet.clone(), TxPolicies::default())
Expand Down Expand Up @@ -573,60 +573,15 @@ pub(crate) fn get_contract_ids(
.unwrap()
.contract_id();

let storage_configuration = get_proxy_storage_config(
Bytes32::from_bytes(implementation_contract_id.as_slice()).unwrap(),
State::Initialized(proxy_owner.address().clone().into()),
);
let proxy_configurables = BridgeProxyConfigurables::default()
.with_INITIAL_OWNER(State::Initialized(proxy_owner.address().clone().into())).unwrap()
.with_INITIAL_TARGET(implementation_contract_id.clone()).unwrap();

let proxy_config =
LoadConfiguration::default().with_storage_configuration(storage_configuration);
LoadConfiguration::default().with_configurables(proxy_configurables);
let proxy_contract_id = Contract::load_from(BRIDGE_PROXY_BINARY, proxy_config)
.unwrap()
.contract_id();

(proxy_contract_id, implementation_contract_id)
}

pub(crate) fn get_proxy_storage_config(
target_contract_id: Bytes32,
owner: State,
) -> StorageConfiguration {
let target_key_hash = Hasher::hash("storage_SRC14_0");
let slot_override_target = StorageSlot::new(target_key_hash, target_contract_id);

let (owner_key_hash_1, owner_key_hash_2) = {
let owner_key_hash_1: Bytes32 = Hasher::hash("storage_SRC14_1");
let mut key_hash_slice = owner_key_hash_1.clone().as_slice().to_vec();

let last_index = key_hash_slice.len() - 1;
key_hash_slice[last_index] = key_hash_slice[last_index].wrapping_add(1);

let owner_key_hash_2 = Bytes32::from_bytes(&key_hash_slice).unwrap();

(owner_key_hash_1, owner_key_hash_2)
};

let (owner_value_1, owner_value_2) = {
let mut encoded_value = ABIEncoder::default().encode(&[owner.into_token()]).unwrap();

if encoded_value.len() < 64 {
encoded_value.resize(64, 0);
}

let owner_value_1: Bytes32 =
Bytes32::from_bytes(&mut encoded_value[0..32]).expect("Could not decode owner_value_1");
let owner_value_2: Bytes32 = Bytes32::from_bytes(&mut encoded_value[32..64])
.expect("Could not decode owner_value_2");

(owner_value_1, owner_value_2)
};

let slot_override_owner_1 = StorageSlot::new(owner_key_hash_1, owner_value_1);
let slot_override_owner_2 = StorageSlot::new(owner_key_hash_2, owner_value_2);

StorageConfiguration::default().add_slot_overrides([
slot_override_target,
slot_override_owner_1,
slot_override_owner_2,
])
}
31 changes: 31 additions & 0 deletions packages/integration-tests/tests/bridge_proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,35 @@ describe('Proxy', async function () {
expect(value.bits).to.be.equal(contractId);
});
});

describe('_proxy_revoke_ownership()', () => {
const contractId =
'0x7296ff960b5eb86b5f79aa587d7ebe1bae147c7cac046a16d062fbd7f3a753ec';
const contractIdentityInput = { bits: contractId.toString() };

it('revokes ownership', async () => {
fuel_proxy.account = env.fuel.deployer;
const tx = await fuel_proxy.functions._proxy_revoke_ownership().call();
const result = await tx.transactionResponse.waitForResult();
expect(result.status).to.equal('success');

const { value } = await fuel_proxy.functions._proxy_owner().dryRun();
expect(value).to.have.property('Revoked');
});

it('disallows proxy upgrades', async () => {
fuel_proxy.account = env.fuel.deployer;
const tx = fuel_proxy.functions
.set_proxy_target(contractIdentityInput)
.call();
const [txResult] = await Promise.allSettled([tx]);

if (txResult.status === 'fulfilled') {
throw new Error('Transaction did not revert');
}
const { message } = txResult.reason as FuelError;

expect(message).contains('NotOwner');
});
});
});
Loading

0 comments on commit b9f145b

Please sign in to comment.