Skip to content

Commit

Permalink
[sui native bridge] - add update committee node url to validator CLI (#…
Browse files Browse the repository at this point in the history
…18154)

## Description 

as titled 

## Test plan 

How did you test the new or updated feature?

---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [ ] Protocol: 
- [ ] Nodes (Validators and Full nodes): 
- [ ] Indexer: 
- [ ] JSON-RPC: 
- [ ] GraphQL: 
- [ ] CLI: 
- [ ] Rust SDK:
  • Loading branch information
patrickkuo authored Jun 13, 2024
1 parent 07328eb commit 4592c09
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 24 deletions.
33 changes: 32 additions & 1 deletion crates/sui-bridge/src/sui_transaction_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ pub fn build_committee_register_transaction(
bridge_authority_pub_key_bytes: Vec<u8>,
bridge_url: &str,
ref_gas_price: u64,
gas_budget: u64,
) -> BridgeResult<TransactionData> {
let mut builder = ProgrammableTransactionBuilder::new();
let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
Expand All @@ -577,7 +578,37 @@ pub fn build_committee_register_transaction(
validator_address,
vec![*gas_object_ref],
builder.finish(),
1000000000,
gas_budget,
ref_gas_price,
);
Ok(data)
}

pub fn build_committee_update_url_transaction(
validator_address: SuiAddress,
gas_object_ref: &ObjectRef,
bridge_object_arg: ObjectArg,
bridge_url: &str,
ref_gas_price: u64,
gas_budget: u64,
) -> BridgeResult<TransactionData> {
let mut builder = ProgrammableTransactionBuilder::new();
let bridge = builder.obj(bridge_object_arg).unwrap();
let url = builder
.input(CallArg::Pure(bcs::to_bytes(bridge_url.as_bytes()).unwrap()))
.unwrap();
builder.programmable_move_call(
BRIDGE_PACKAGE_ID,
BRIDGE_MODULE_NAME.into(),
Identifier::from_str("update_node_url").unwrap(),
vec![],
vec![bridge, url],
);
let data = TransactionData::new_programmable(
validator_address,
vec![*gas_object_ref],
builder.finish(),
gas_budget,
ref_gas_price,
);
Ok(data)
Expand Down
1 change: 1 addition & 0 deletions crates/sui/src/sui_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ impl SuiCommand {
kp.public().as_bytes().to_vec(),
&format!("http://127.0.0.1:{port}"),
rgp,
1000000000,
)
.unwrap();
let signed_tx = context.sign_transaction(&tx);
Expand Down
157 changes: 134 additions & 23 deletions crates/sui/src/validator_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ use fastcrypto::{
use serde::Serialize;
use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
use sui_bridge::sui_client::SuiClient as SuiBridgeClient;
use sui_bridge::sui_transaction_builder::build_committee_register_transaction;
use sui_bridge::sui_transaction_builder::{
build_committee_register_transaction, build_committee_update_url_transaction,
};
use sui_json_rpc_types::{
SuiObjectDataOptions, SuiTransactionBlockResponse, SuiTransactionBlockResponseOptions,
};
Expand Down Expand Up @@ -181,6 +183,25 @@ pub enum SuiValidatorCommand {
/// Must present if `print_unsigned_transaction_only` is true.
#[clap(long)]
validator_address: Option<SuiAddress>,
/// Gas budget for this transaction.
#[clap(name = "gas-budget", long)]
gas_budget: Option<u64>,
},
/// Update sui native bridge committee node url
UpdateBridgeCommitteeNodeUrl {
/// New node url to be registered in the on chain bridge object.
#[clap(long)]
bridge_authority_url: String,
/// If true, only print the unsigned transaction and do not execute it.
/// This is useful for offline signing.
#[clap(name = "print-only", long, default_value = "false")]
print_unsigned_transaction_only: bool,
/// Must be present if `print_unsigned_transaction_only` is true.
#[clap(long)]
validator_address: Option<SuiAddress>,
/// Gas budget for this transaction.
#[clap(name = "gas-budget", long)]
gas_budget: Option<u64>,
},
}

Expand All @@ -204,13 +225,17 @@ pub enum SuiValidatorCommandResponse {
execution_response: Option<SuiTransactionBlockResponse>,
serialized_unsigned_transaction: Option<String>,
},
UpdateBridgeCommitteeURL {
execution_response: Option<SuiTransactionBlockResponse>,
serialized_unsigned_transaction: Option<String>,
},
}

fn make_key_files(
file_name: PathBuf,
is_protocol_key: bool,
key: Option<SuiKeyPair>,
) -> anyhow::Result<()> {
) -> Result<()> {
if file_name.exists() {
println!("Use existing {:?} key file.", file_name);
return Ok(());
Expand Down Expand Up @@ -482,27 +507,18 @@ impl SuiValidatorCommand {
bridge_authority_url,
print_unsigned_transaction_only,
validator_address,
gas_budget,
} => {
// Read bridge keypair
let ecdsa_keypair = match read_key(&bridge_authority_key_path, true)? {
SuiKeyPair::Secp256k1(key) => key,
_ => unreachable!("we required secp256k1 key in `read_key`"),
};
let address = if !print_unsigned_transaction_only {
let address = context.active_address()?;
if let Some(validator_address) = validator_address {
if validator_address != address {
bail!(
"`--validator-address` must be the same as the current active address: {}",
address
);
}
}
address
} else {
validator_address
.ok_or_else(|| anyhow!("--validator-address must be provided when `print_unsigned_transaction_only` is true"))?
};
let address = check_address(
context.active_address()?,
validator_address,
print_unsigned_transaction_only,
)?;
// Make sure the address is a validator
let sui_client = context.get_client().await?;
let active_validators = sui_client
Expand All @@ -523,19 +539,20 @@ impl SuiValidatorCommand {
.get_mutable_bridge_object_arg_must_succeed()
.await;

let gas = context
.get_one_gas_object_owned_by_address(address)
.await?
.unwrap_or_else(|| panic!("Cannot find gas object from address : {address}"));
let gas_budget = gas_budget.unwrap_or(DEFAULT_GAS_BUDGET);
let (_, gas) = context
.gas_for_owner_budget(address, gas_budget, Default::default())
.await?;

let gas_price = context.get_reference_gas_price().await?;
let tx_data = build_committee_register_transaction(
address,
&gas,
&gas.object_ref(),
bridge,
ecdsa_keypair.public().as_bytes().to_vec(),
&bridge_authority_url,
gas_price,
gas_budget,
)
.map_err(|e| anyhow!("{e:?}"))?;
if print_unsigned_transaction_only {
Expand All @@ -557,11 +574,101 @@ impl SuiValidatorCommand {
}
}
}
SuiValidatorCommand::UpdateBridgeCommitteeNodeUrl {
bridge_authority_url,
print_unsigned_transaction_only,
validator_address,
gas_budget,
} => {
// Make sure the address is member of the committee
let address = check_address(
context.active_address()?,
validator_address,
print_unsigned_transaction_only,
)?;
let sui_rpc_url = &context.config.get_active_env().unwrap().rpc;
let bridge_client = SuiBridgeClient::new(sui_rpc_url).await?;
let committee_members = bridge_client
.get_bridge_summary()
.await
.map_err(|e| anyhow!("{e:?}"))?
.committee
.members;
if !committee_members
.into_iter()
.any(|(_, m)| m.sui_address == address)
{
bail!("Address {} is not in the committee", address);
}
println!(
"Updating bridge committee node URL for Sui validator: {address}, url: {}",
bridge_authority_url
);

let bridge = bridge_client
.get_mutable_bridge_object_arg_must_succeed()
.await;

let gas_budget = gas_budget.unwrap_or(DEFAULT_GAS_BUDGET);
let (_, gas) = context
.gas_for_owner_budget(address, gas_budget, Default::default())
.await?;

let gas_price = context.get_reference_gas_price().await?;
let tx_data = build_committee_update_url_transaction(
address,
&gas.object_ref(),
bridge,
&bridge_authority_url,
gas_price,
gas_budget,
)
.map_err(|e| anyhow!("{e:?}"))?;
if print_unsigned_transaction_only {
let serialized_data = Base64::encode(bcs::to_bytes(&tx_data)?);
SuiValidatorCommandResponse::UpdateBridgeCommitteeURL {
execution_response: None,
serialized_unsigned_transaction: Some(serialized_data),
}
} else {
let tx = context.sign_transaction(&tx_data);
let response = context.execute_transaction_must_succeed(tx).await;
println!(
"Update Bridge validator node URL successful. Transaction digest: {}",
response.digest
);
SuiValidatorCommandResponse::UpdateBridgeCommitteeURL {
execution_response: Some(response),
serialized_unsigned_transaction: None,
}
}
}
});
ret
}
}

fn check_address(
active_address: SuiAddress,
validator_address: Option<SuiAddress>,
print_unsigned_transaction_only: bool,
) -> Result<SuiAddress, anyhow::Error> {
if !print_unsigned_transaction_only {
if let Some(validator_address) = validator_address {
if validator_address != active_address {
bail!(
"`--validator-address` must be the same as the current active address: {}",
active_address
);
}
}
Ok(active_address)
} else {
validator_address
.ok_or_else(|| anyhow!("--validator-address must be provided when `print_unsigned_transaction_only` is true"))
}
}

async fn get_cap_object_ref(
context: &mut WalletContext,
operation_cap_id: Option<ObjectID>,
Expand Down Expand Up @@ -795,13 +902,17 @@ impl Display for SuiValidatorCommandResponse {
SuiValidatorCommandResponse::RegisterBridgeCommittee {
execution_response,
serialized_unsigned_transaction,
}
| SuiValidatorCommandResponse::UpdateBridgeCommitteeURL {
execution_response,
serialized_unsigned_transaction,
} => {
if let Some(response) = execution_response {
write!(writer, "{}", write_transaction_response(response)?)?;
} else {
write!(
writer,
"Serializecd transaction for signing: {:?}",
"Serialized transaction for signing: {:?}",
serialized_unsigned_transaction
)?;
}
Expand Down
1 change: 1 addition & 0 deletions crates/test-cluster/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1251,6 +1251,7 @@ impl TestClusterBuilder {
kp.public().as_bytes().to_vec(),
&server_url,
ref_gas_price,
1000000000,
)
.unwrap();

Expand Down

0 comments on commit 4592c09

Please sign in to comment.