-
Notifications
You must be signed in to change notification settings - Fork 25
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
Conversation
* Add a method to register public keys * Add a query to fetch public keys * Distinguish keys and signatures by type
* detect if worker set needs to be updated * ABI encode new worker set and add to batch
58781f4
to
ce76b67
Compare
.map(TryInto::try_into) | ||
.collect::<Result<Vec<Command>, ContractError>>()?; | ||
if new_worker_set.is_some() { | ||
commands.push(new_worker_set.clone().unwrap().try_into()?); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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"), |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
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) | ||
}, | ||
); |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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
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() | ||
} | ||
} |
There was a problem hiding this comment.
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?
#[cw_serde] | ||
pub struct WorkerSet { | ||
pub signers: Vec<Signer>, | ||
pub threshold: Uint256, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which threshold is this?
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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>)>>(); |
There was a problem hiding this comment.
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>)>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub signers: Vec<(Signer, Option<Signature>)>, | |
pub signers_with_sigs: Vec<(Signer, Option<Signature>)>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are signatures optional?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
.
} | ||
} | ||
|
||
impl TryFrom<(Signer, Option<Signature>)> for Operator { |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
impl CommandBatch { | ||
pub fn new( | ||
messages: Vec<Message>, | ||
destination_chain_id: Uint256, | ||
new_worker_set: Option<WorkerSet>, |
There was a problem hiding this comment.
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();
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this?
There was a problem hiding this comment.
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.
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); | ||
} |
There was a problem hiding this comment.
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
?
pub fn new( | ||
snapshot: Snapshot, | ||
pub_keys: Vec<multisig::types::PublicKey>, | ||
env: Env, |
There was a problem hiding this comment.
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")) |
There was a problem hiding this comment.
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.
# 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
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
Steps to Test
Expected Behaviour
Other Notes
AXE-1534