Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(multisig-prover): start update worker set #67

Merged
merged 21 commits into from
Sep 8, 2023

Conversation

cjcobb23
Copy link
Contributor

@cjcobb23 cjcobb23 commented Aug 25, 2023

Description

detect if worker set needs to be updated
abi encode new worker set and include in batch
depends on #64

Todos

update the worker set automatically as part of batch creation

  • Unit tests
  • Manual tests
  • Documentation
  • Connect epics/issues

Steps to Test

Expected Behaviour

Other Notes

AXE-1534

cjcobb23 and others added 2 commits August 22, 2023 16:39
* Add a method to register public keys
* Add a query to fetch public keys
* Distinguish keys and signatures by type
@cjcobb23 cjcobb23 requested a review from a team as a code owner August 25, 2023 02:23
@cjcobb23 cjcobb23 changed the base branch from main to feat/multisig-key-registration August 31, 2023 17:50
* detect if worker set needs to be updated
* ABI encode new worker set and add to batch
@cjcobb23 cjcobb23 force-pushed the feat/update-worker-set branch from 58781f4 to ce76b67 Compare August 31, 2023 19:06
.map(TryInto::try_into)
.collect::<Result<Vec<Command>, ContractError>>()?;
if new_worker_set.is_some() {
commands.push(new_worker_set.clone().unwrap().try_into()?);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't use unwrap in production code

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use if let Some(worker_set) instead

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushing it into .commands without touching the .message_ids is weird to me.

.iter()
.map(|s| {
(
evm_address((&s.pub_key).into()).expect("couldn't convert pubkey to evm address"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems to me that maybe evm_address needs to change? You're throwing away the knowledge that it's a valid pub_key, only to panic if it's not valid

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I partially agree with @cgorenflo. Any Ecdsa public key can be converted into a valid Ethereum address so it only makes sense for the evm_address method to return an error when the key type isn't right.

Comment on lines 203 to 215
let (addresses, weights) = operators.iter().fold(
(vec![], vec![]),
|(mut addresses, mut weights), operator| {
addresses.push(Token::Address(ethereum_types::Address::from_slice(
operator.0.as_slice(),
)));
weights.push(Token::Uint(ethereum_types::U256::from_big_endian(
&operator.1.to_be_bytes(),
)));

(addresses, weights)
},
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use unzip

.zip(snapshot.participants.values().map(|p| p.weight.into()))
.collect();
Ok(Operators {
weights: zipped,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call this weights_by_addresses or something similar, so it's clear that it's a tuple

Comment on lines 67 to 77
impl BatchID {
pub fn new(message_ids: &[String]) -> BatchID {
pub fn new(message_ids: &[String], new_worker_set: Option<WorkerSet>) -> BatchID {
let mut message_ids = message_ids.to_vec();
message_ids.sort();

if let Some(new_worker_set) = new_worker_set {
message_ids.push(new_worker_set.hash().to_string())
}
Keccak256::digest(message_ids.join(",")).as_slice().into()
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if message_ids are empty and worker_set is none?

contracts/multisig-prover/src/execute.rs Outdated Show resolved Hide resolved
#[cw_serde]
pub struct WorkerSet {
pub signers: Vec<Signer>,
pub threshold: Uint256,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which threshold is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signing threshold. Defines how many signatures are needed before relaying to the external gateway

pub fn new(
snapshot: Snapshot,
pub_keys: Vec<multisig::types::PublicKey>,
env: Env,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pass in the block height instead

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with @cgorenflo. This goes again with my comment saying that I don't like passing exessive information to any function.

})
.collect::<Vec<Signer>>();
.collect::<Vec<(Signer, Option<Signature>)>>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let signers_with_sigs = ...

}

#[cw_serde]
pub struct Multisig {
pub state: MultisigState,
pub quorum: Uint256,
pub signers: Vec<Signer>,
pub signers: Vec<(Signer, Option<Signature>)>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub signers: Vec<(Signer, Option<Signature>)>,
pub signers_with_sigs: Vec<(Signer, Option<Signature>)>,

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are signatures optional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the response for the GetMultisig query, and the behavior is to return all of the signers, even if they haven't signed a particular message yet. So the signature is present iff they signed.

@@ -44,34 +46,69 @@ impl TryFrom<Message> for Command {
}
}

impl TryFrom<Signer> for Operator {
impl TryFrom<WorkerSet> for Command {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think implementing From or TryFrom simply cuz something can be converted to another makes sense to me. Personally, it still has to be two things that are conceptually the same or similar. I'd rather seeing a new_transfer_operatorship method that takes a WorkerSet.

contracts/multisig-prover/src/state.rs Outdated Show resolved Hide resolved
}
}

impl TryFrom<(Signer, Option<Signature>)> for Operator {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is another example where I do NOT think TryFrom makes sense. I'd prefer having impl TryFrom<Signer> for Operator and then have a method like .signature( ... ) on Operator to set it.

})
}
}

pub fn make_worker_set(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function isn't being used?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, why is this not a method on WorkerSet? I also don't like it taking an entire Snapshot as one of the parameters cuz only one field from the snapshot is relevant here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not being used, deleted it. There is a similar method on WorkerSet actually

Comment on lines 91 to 95
impl CommandBatch {
pub fn new(
messages: Vec<Message>,
destination_chain_id: Uint256,
new_worker_set: Option<WorkerSet>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer something like following instead of having everything possible in the .new method.

let command_batch_builder = CommandBatchBuilder::default();

command_batch_builder.add_message( ... );
command_batch_builder.add_new_worker_set( ... );

let command_batch = command_batch_builder.build();

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are likely going to have more types of commands, and keep adding things to new is confusing to me.

@@ -41,6 +41,7 @@ pub fn instantiate(
service_name: msg.service_name,
chain_name: ChainName::from_str(&msg.chain_name)
.map_err(|_| ContractError::InvalidChainName)?,
worker_set_diff_threshold: msg.worker_set_diff_threshold,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the max number of workers that can change without triggering a worker set update.

Comment on lines 111 to 122
let mut pub_keys = vec![];
for worker in &workers {
let pub_key_query = multisig::msg::QueryMsg::GetPublicKey {
worker_address: worker.address.to_string(),
key_type: multisig::types::KeyType::ECDSA,
};
let pub_key: PublicKey = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
contract_addr: config.multisig.to_string(),
msg: to_binary(&pub_key_query)?,
}))?;
pub_keys.push(pub_key);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do this with .iter().map?

contracts/multisig-prover/src/execute.rs Outdated Show resolved Hide resolved
pub fn new(
snapshot: Snapshot,
pub_keys: Vec<multisig::types::PublicKey>,
env: Env,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with @cgorenflo. This goes again with my comment saying that I don't like passing exessive information to any function.

}

pub fn hash(&self) -> HexBinary {
Keccak256::digest(serde_json::to_vec(&self).expect("couldn't serialize worker set"))
Copy link
Collaborator

@fish-sammy fish-sammy Sep 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it makes it clearer that a BTreeSet makes way more sense than a Vec cuz order matters when it comes to the hash.

Base automatically changed from feat/multisig-key-registration to main September 7, 2023 23:35
# Conflicts:
#	ampd/src/handlers/multisig.rs
#	contracts/multisig-prover/src/execute.rs
#	contracts/multisig-prover/src/test/test_data.rs
#	contracts/multisig/src/contract.rs
#	contracts/multisig/src/key.rs
@cgorenflo cgorenflo merged commit 1eace6b into main Sep 8, 2023
3 checks passed
@cgorenflo cgorenflo deleted the feat/update-worker-set branch September 8, 2023 00:10
@cgorenflo cgorenflo restored the feat/update-worker-set branch September 8, 2023 00:55
@cgorenflo cgorenflo deleted the feat/update-worker-set branch September 12, 2023 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants