Skip to content

Commit

Permalink
Merge pull request #97 from amazingdatamachine/sander/acc-dont-panic
Browse files Browse the repository at this point in the history
Accumulator: Don't panic
  • Loading branch information
sanderpick authored Jun 12, 2024
2 parents d6dcc55 + 2ba4802 commit a2ef88f
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 39 deletions.
4 changes: 1 addition & 3 deletions fendermint/actors/accumulator/src/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ pub struct Actor;
impl Actor {
fn constructor(rt: &impl Runtime, params: ConstructorParams) -> Result<(), ActorError> {
rt.validate_immediate_caller_is(std::iter::once(&INIT_ACTOR_ADDR))?;

let state = State::new(rt.store(), params.creator, params.write_access).map_err(|e| {
e.downcast_default(
ExitCode::USR_ILLEGAL_STATE,
Expand All @@ -34,7 +33,6 @@ impl Actor {

fn push(rt: &impl Runtime, params: PushParams) -> Result<PushReturn, ActorError> {
Self::ensure_write_allowed(rt)?;

rt.transaction(|st: &mut State, rt| {
st.push(rt.store(), params.0).map_err(|e| {
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to push object")
Expand All @@ -45,7 +43,7 @@ impl Actor {
fn get_leaf_at(rt: &impl Runtime, index: u64) -> Result<Option<Vec<u8>>, ActorError> {
rt.validate_immediate_caller_accept_any()?;
let st: State = rt.state()?;
st.get_obj(rt.store(), index)
st.get_leaf_at(rt.store(), index)
.map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to get leaf"))
}

Expand Down
71 changes: 50 additions & 21 deletions fendermint/actors/accumulator/src/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,14 @@ fn bag_peaks<BS: Blockstore>(peaks: &Amt<Cid, &BS>) -> anyhow::Result<Cid> {
}

/// Given the size of the MMR and an index into the MMR, returns a tuple where the first element
/// represents the path through the subtree that the leaf node lives in, and the second element
/// represents the index of the peak containing the subtree that the leaf node lives in.
fn path_for_eigen_root(leaf_index: u64, leaf_count: u64) -> (u64, u64) {
/// represents the path through the subtree that the leaf node lives in.
/// The second element represents the index of the peak containing the subtree that the leaf node
/// lives in.
fn path_for_eigen_root(leaf_index: u64, leaf_count: u64) -> anyhow::Result<(u64, u64)> {
// Ensure `leaf_index` is within bounds.
assert!(
leaf_index < leaf_count,
"`leaf_index` must less than `leaf_count`"
);
if leaf_index >= leaf_count {
return Err(anyhow::anyhow!("`leaf_index` must less than `leaf_count`"));
}
// XOR turns matching bits into zeros and differing bits into ones, so to determine when
// the two "paths" converge, we simply look for the most significant 1 bit...
let diff = leaf_index ^ leaf_count;
Expand All @@ -150,16 +150,16 @@ fn path_for_eigen_root(leaf_index: u64, leaf_count: u64) -> (u64, u64) {
let local_offset = leaf_index & bitmask;
// The local_index is the local_offset plus the merge_height for the local eigentree
let local_path = local_offset + merge_height;
(local_path, eigen_index as u64)
Ok((local_path, eigen_index as u64))
}

fn get_at<BS: Blockstore, S: DeserializeOwned + Serialize>(
store: &BS,
leaf_index: u64,
leaf_count: u64,
peaks: &Amt<Cid, &BS>,
) -> anyhow::Result<Option<S>> {
let (path, eigen_index) = path_for_eigen_root(leaf_index, leaf_count);
) -> anyhow::Result<S> {
let (path, eigen_index) = path_for_eigen_root(leaf_index, leaf_count)?;
let cid = match peaks.get(eigen_index)? {
Some(cid) => cid,
None => {
Expand All @@ -172,7 +172,7 @@ fn get_at<BS: Blockstore, S: DeserializeOwned + Serialize>(
// Special case where eigentree has height of one
if path == 1 {
return match store.get_cbor::<S>(cid)? {
Some(value) => Ok(Some(value)),
Some(value) => Ok(value),
None => Err(anyhow::anyhow!("failed to get leaf for cid {}", cid)),
};
}
Expand Down Expand Up @@ -207,7 +207,11 @@ fn get_at<BS: Blockstore, S: DeserializeOwned + Serialize>(

let bit = (path & 1) as usize;
let cid = &pair[bit];
store.get_cbor::<S>(cid)
let leaf = match store.get_cbor::<S>(cid)? {
Some(root) => root,
None => return Err(anyhow::anyhow!("failed to get leaf for cid {}", cid)),
};
Ok(leaf)
}

/// The state represents an MMR with peaks stored in an AMT
Expand Down Expand Up @@ -299,13 +303,17 @@ impl State {
Ok(peaks)
}

pub fn get_obj<BS: Blockstore, S: DeserializeOwned + Serialize>(
pub fn get_leaf_at<BS: Blockstore, S: DeserializeOwned + Serialize>(
&self,
store: &BS,
index: u64,
) -> anyhow::Result<Option<S>> {
let amt = Amt::<Cid, &BS>::load(&self.peaks, store)?;
get_at::<BS, S>(store, index, self.leaf_count, &amt)
let leaf = match get_at::<BS, S>(store, index, self.leaf_count, &amt) {
Ok(leaf) => Some(leaf),
Err(_) => None,
};
Ok(leaf)
}
}

Expand Down Expand Up @@ -418,23 +426,41 @@ mod tests {
state.push(&store, vec![0]).unwrap();
assert_eq!(state.peak_count(), 1);
assert_eq!(state.leaf_count(), 1);
let item0 = state.get_obj::<_, Vec<i32>>(&store, 0u64).unwrap().unwrap();
let item0 = state
.get_leaf_at::<_, Vec<i32>>(&store, 0u64)
.unwrap()
.unwrap();
assert_eq!(item0, vec![0]);

state.push(&store, vec![1]).unwrap();
assert_eq!(state.peak_count(), 1);
assert_eq!(state.leaf_count(), 2);
let item0 = state.get_obj::<_, Vec<i32>>(&store, 0u64).unwrap().unwrap();
let item1 = state.get_obj::<_, Vec<i32>>(&store, 1u64).unwrap().unwrap();
let item0 = state
.get_leaf_at::<_, Vec<i32>>(&store, 0u64)
.unwrap()
.unwrap();
let item1 = state
.get_leaf_at::<_, Vec<i32>>(&store, 1u64)
.unwrap()
.unwrap();
assert_eq!(item0, vec![0]);
assert_eq!(item1, vec![1]);

state.push(&store, vec![2]).unwrap();
assert_eq!(state.peak_count(), 2);
assert_eq!(state.leaf_count(), 3);
let item0 = state.get_obj::<_, Vec<i32>>(&store, 0u64).unwrap().unwrap();
let item1 = state.get_obj::<_, Vec<i32>>(&store, 1u64).unwrap().unwrap();
let item2 = state.get_obj::<_, Vec<i32>>(&store, 2u64).unwrap().unwrap();
let item0 = state
.get_leaf_at::<_, Vec<i32>>(&store, 0u64)
.unwrap()
.unwrap();
let item1 = state
.get_leaf_at::<_, Vec<i32>>(&store, 1u64)
.unwrap()
.unwrap();
let item2 = state
.get_leaf_at::<_, Vec<i32>>(&store, 2u64)
.unwrap()
.unwrap();
assert_eq!(item0, vec![0]);
assert_eq!(item1, vec![1]);
assert_eq!(item2, vec![2]);
Expand All @@ -451,7 +477,10 @@ mod tests {
// As more items are added to the accumulator, ensure each item remains gettable at
// each phase of the growth of the inner tree structures.
for j in 0..i {
let item = state.get_obj::<_, Vec<u64>>(&store, j).unwrap().unwrap();
let item = state
.get_leaf_at::<_, Vec<u64>>(&store, j)
.unwrap()
.unwrap();
assert_eq!(item, vec![j]);
}
}
Expand Down
2 changes: 1 addition & 1 deletion infra/fendermint/scripts/subnet.toml
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ script.pre = "mkdir -p ${BASE_DIR}/${NODE_NAME}/${KEYS_SUBDIR}; cp ${PRIVATE_KEY

[tasks.subnet-fetch-genesis]
extend = "fendermint-tool"
env = { "CMD" = "genesis --genesis-file /data/genesis.json ipc from-parent --subnet-id ${SUBNET_ID} -p ${PARENT_ENDPOINT} --parent-gateway ${PARENT_GATEWAY} --parent-registry ${PARENT_REGISTRY} --base-fee ${BASE_FEE} --power-scale ${POWER_SCALE}" }
env = { "CMD" = "genesis --genesis-file /data/genesis.json ipc from-parent --subnet-id ${SUBNET_ID} -p ${PARENT_ENDPOINT} --parent-auth-token ${PARENT_HTTP_AUTH_TOKEN} --parent-gateway ${PARENT_GATEWAY} --parent-registry ${PARENT_REGISTRY} --base-fee ${BASE_FEE} --power-scale ${POWER_SCALE}" }

[tasks.subnet-genesis-set-eam-permissions]
extend = "fendermint-tool"
Expand Down
14 changes: 0 additions & 14 deletions scripts/deploy_subnet_under_calibration_net/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -286,20 +286,6 @@ do
child-validator
done

# Step 8.4: Fund proxy wallet in the subnet
echo "$DASHES Fund proxy wallets in the subnet"
for i in {0..2}
do
proxy_key=$(cat ${IPC_CONFIG_FOLDER}/evm_keystore_proxy.json | jq .[$i].private_key | tr -d '"' | tr -d '\n')
proxy_address=$(cat ${IPC_CONFIG_FOLDER}/evm_keystore_proxy.json | jq .[$i].address | tr -d '"' | tr -d '\n')
ipc-cli wallet import --wallet-type evm --private-key ${proxy_key}
ipc-cli cross-msg fund --from ${proxy_address} --subnet ${subnet_id} 1
out=${subnet_folder}/validator-${i}/validator-${i}/keys/proxy_key.sk
ipc-cli wallet export --wallet-type evm --address ${proxy_address} --fendermint | tr -d '\n' > ${out}
chmod 600 ${subnet_folder}/validator-${i}/validator-${i}/keys/proxy_key.sk
ipc-cli wallet remove --wallet-type evm --address ${proxy_address}
done

# Step 9: Test
# Step 9.1: Test ETH API endpoint
echo "$DASHES Test ETH API endpoints of validator nodes"
Expand Down

0 comments on commit a2ef88f

Please sign in to comment.