From 4f9757e76f42e0d5619d0228f89fd551036a9ba2 Mon Sep 17 00:00:00 2001 From: Temple3x Date: Mon, 13 Jun 2022 01:21:15 +0800 Subject: [PATCH 1/5] cmd/starcoin: try to generate sign file when found multisig account in txn execution process (WIP) --- .../src/account/execute_script_cmd.rs | 11 +- .../src/account/sign_multisig_txn_cmd.rs | 23 ++-- cmd/starcoin/src/cli_state.rs | 103 +++++++++++++++--- 3 files changed, 107 insertions(+), 30 deletions(-) diff --git a/cmd/starcoin/src/account/execute_script_cmd.rs b/cmd/starcoin/src/account/execute_script_cmd.rs index 2661877f46..4a37755ac8 100644 --- a/cmd/starcoin/src/account/execute_script_cmd.rs +++ b/cmd/starcoin/src/account/execute_script_cmd.rs @@ -1,11 +1,11 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::cli_state::CliState; -use crate::view::{ExecuteResultView, TransactionOptions}; -use crate::StarcoinOpt; +use std::path::PathBuf; + use anyhow::{bail, Result}; use clap::Parser; + use scmd::{CommandAction, ExecContext}; use starcoin_move_compiler::load_bytecode_file; use starcoin_types::transaction::{ @@ -13,7 +13,10 @@ use starcoin_types::transaction::{ }; use starcoin_vm_types::transaction_argument::convert_txn_args; use starcoin_vm_types::{language_storage::TypeTag, parser::parse_type_tag}; -use std::path::PathBuf; + +use crate::cli_state::CliState; +use crate::view::{ExecuteResultView, TransactionOptions}; +use crate::StarcoinOpt; /// Execute a script #[derive(Debug, Parser)] diff --git a/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs b/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs index bc7667d59b..c184037399 100644 --- a/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs +++ b/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs @@ -1,14 +1,18 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::cli_state::CliState; -use crate::StarcoinOpt; +use std::convert::TryInto; +use std::env::current_dir; +use std::fs::File; +use std::path::PathBuf; + use anyhow::{bail, Result}; use clap::Parser; -use scmd::{CommandAction, ExecContext}; -use starcoin_account_api::AccountPublicKey; use starcoin_crypto::hash::PlainCryptoHash; use starcoin_crypto::multi_ed25519::multi_shard::MultiEd25519SignatureShard; + +use scmd::{CommandAction, ExecContext}; +use starcoin_account_api::AccountPublicKey; use starcoin_rpc_api::types::{FunctionIdView, RawUserTransactionView, TransactionStatusView}; use starcoin_rpc_client::StateRootOption; use starcoin_state_api::StateReaderExt; @@ -22,10 +26,9 @@ use starcoin_vm_types::token::stc::STC_TOKEN_CODE_STR; use starcoin_vm_types::transaction::{ScriptFunction, TransactionPayload}; use starcoin_vm_types::transaction_argument::convert_txn_args; use starcoin_vm_types::{language_storage::TypeTag, parser::parse_type_tag}; -use std::convert::TryInto; -use std::env::current_dir; -use std::fs::File; -use std::path::PathBuf; + +use crate::cli_state::CliState; +use crate::StarcoinOpt; #[derive(Debug, Parser)] #[clap(name = "sign-multisig-txn")] @@ -56,7 +59,7 @@ pub struct GenerateMultisigTxnOpt { )] type_tags: Option>, - #[clap(long = "arg", name = "transaction-arg", parse(try_from_str = parse_transaction_argument))] + #[clap(long = "arg", name = "transaction-arg", parse(try_from_str = parse_transaction_argument))] /// transaction arguments args: Option>, @@ -111,7 +114,7 @@ impl CommandAction for GenerateMultisigTxnCommand { // gen multisig txn or read from file sent by other participants. let (raw_txn, existing_signatures) = if let Some(function_id) = opt.script_function.clone().map(|t| t.0) { - let sender = ctx.opt().sender.expect("sender adress should be provided"); + let sender = ctx.opt().sender.expect("sender address should be provided"); let script_function = ScriptFunction::new( function_id.module, function_id.function, diff --git a/cmd/starcoin/src/cli_state.rs b/cmd/starcoin/src/cli_state.rs index addabe0811..5bee48414c 100644 --- a/cmd/starcoin/src/cli_state.rs +++ b/cmd/starcoin/src/cli_state.rs @@ -1,14 +1,23 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::view::{ExecuteResultView, ExecutionOutputView, TransactionOptions}; +use std::convert::TryInto; +use std::env::current_dir; +use std::fs::File; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::time::Duration; + use anyhow::{bail, format_err, Result}; -use bcs_ext::BCSCodec; use serde::de::DeserializeOwned; +use starcoin_crypto::hash::PlainCryptoHash; +use starcoin_crypto::multi_ed25519::multi_shard::MultiEd25519SignatureShard; +use starcoin_crypto::HashValue; + +use bcs_ext::BCSCodec; use starcoin_abi_decoder::{decode_txn_payload, DecodedTransactionPayload}; use starcoin_account_api::{AccountInfo, AccountProvider}; use starcoin_config::{ChainNetworkID, DataDirPath}; -use starcoin_crypto::HashValue; use starcoin_node::NodeHandle; use starcoin_rpc_api::chain::GetEventOption; use starcoin_rpc_api::types::{RawUserTransactionView, TransactionStatusView}; @@ -19,11 +28,12 @@ use starcoin_vm_types::account_address::AccountAddress; use starcoin_vm_types::account_config::association_address; use starcoin_vm_types::move_resource::MoveResource; use starcoin_vm_types::token::stc::STC_TOKEN_CODE_STR; -use starcoin_vm_types::transaction::{DryRunTransaction, RawUserTransaction, TransactionPayload}; -use std::convert::TryInto; -use std::path::{Path, PathBuf}; -use std::sync::Arc; -use std::time::Duration; +use starcoin_vm_types::transaction::authenticator::{AccountPublicKey, TransactionAuthenticator}; +use starcoin_vm_types::transaction::{ + DryRunTransaction, RawUserTransaction, SignedUserTransaction, TransactionPayload, +}; + +use crate::view::{ExecuteResultView, ExecutionOutputView, TransactionOptions}; static G_HISTORY_FILE_NAME: &str = "history"; @@ -252,8 +262,9 @@ impl CliState { blocking: bool, ) -> Result { let sender = self.get_account(raw_txn.sender())?; + let public_key = sender.public_key; let dry_output = self.client.dry_run_raw(DryRunTransaction { - public_key: sender.public_key, + public_key: public_key.clone(), raw_txn: raw_txn.clone(), })?; let mut raw_txn_view: RawUserTransactionView = raw_txn.clone().try_into()?; @@ -271,15 +282,75 @@ impl CliState { return Ok(execute_result); } let signed_txn = self.account_client.sign_txn(raw_txn, sender.address)?; - let signed_txn_hex = hex::encode(signed_txn.encode()?); - let txn_hash = self.client.submit_hex_transaction(signed_txn_hex)?; - eprintln!("txn {} submitted.", txn_hash); - let execute_output = if blocking { - self.watch_txn(txn_hash)? + + let multisig_public_key = match &public_key { + AccountPublicKey::Single(_) => { + let signed_txn_hex = hex::encode(signed_txn.encode()?); + let txn_hash = self.client.submit_hex_transaction(signed_txn_hex)?; + eprintln!("txn {} submitted.", txn_hash); + let execute_output = if blocking { + self.watch_txn(txn_hash)? + } else { + ExecutionOutputView::new(txn_hash) + }; + execute_result.execute_output = Some(execute_output); + return Ok(execute_result); + } + + AccountPublicKey::Multi(m) => m.clone(), + }; + + // It's multisig account, try to sign it. + let my_signatures = if let TransactionAuthenticator::MultiEd25519 { signature, .. } = + signed_txn.authenticator() + { + MultiEd25519SignatureShard::new(signature, *multisig_public_key.threshold()) + } else { + unreachable!() + }; + + // merge my signatures with existing signatures of other participants. + let merged_signatures = { + let signatures = vec![my_signatures]; + MultiEd25519SignatureShard::merge(signatures)? + }; + eprintln!( + "mutlisig txn(address: {}, threshold: {}): {} signatures collected", + sender.address, + merged_signatures.threshold(), + merged_signatures.signatures().len() + ); + if !merged_signatures.is_enough() { + eprintln!( + "still require {} signatures", + merged_signatures.threshold() as usize - merged_signatures.signatures().len() + ); } else { - ExecutionOutputView::new(txn_hash) + eprintln!("enough signatures collected for the multisig txn, txn can be submitted now"); + } + + // construct the signed txn with merged signatures. + let signed_txn = { + let authenticator = TransactionAuthenticator::MultiEd25519 { + public_key: multisig_public_key, + signature: merged_signatures.into(), + }; + SignedUserTransaction::new(signed_txn.into_raw_transaction(), authenticator) + }; + + // output the txn, send this to other participants to sign, or just submit it. + let output_file = { + let mut output_dir = current_dir()?; + // use hash's as output file name + let file_name = signed_txn.crypto_hash().to_hex(); + output_dir.push(file_name); + output_dir.set_extension("multisig-txn"); + output_dir }; - execute_result.execute_output = Some(execute_output); + let mut file = File::create(output_file)?; + // write txn to file + bcs_ext::serialize_into(&mut file, &signed_txn)?; + Ok(execute_result) } From b2ae68fe6d2dea5a59b034ad57d746e7828768bc Mon Sep 17 00:00:00 2001 From: Temple3x Date: Mon, 13 Jun 2022 02:44:37 +0800 Subject: [PATCH 2/5] refine code: create pub fn sign_multisig_txn_to_file for public using --- .../src/account/sign_multisig_txn_cmd.rs | 66 +++-------------- cmd/starcoin/src/cli_state.rs | 64 +++------------- cmd/starcoin/src/mutlisig_transaction.rs | 74 ++++++++++++++++++- 3 files changed, 91 insertions(+), 113 deletions(-) diff --git a/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs b/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs index c184037399..a47ea22fad 100644 --- a/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs +++ b/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs @@ -3,12 +3,10 @@ use std::convert::TryInto; use std::env::current_dir; -use std::fs::File; use std::path::PathBuf; use anyhow::{bail, Result}; use clap::Parser; -use starcoin_crypto::hash::PlainCryptoHash; use starcoin_crypto::multi_ed25519::multi_shard::MultiEd25519SignatureShard; use scmd::{CommandAction, ExecContext}; @@ -28,6 +26,7 @@ use starcoin_vm_types::transaction_argument::convert_txn_args; use starcoin_vm_types::{language_storage::TypeTag, parser::parse_type_tag}; use crate::cli_state::CliState; +use crate::mutlisig_transaction::sign_multisig_txn_to_file; use crate::StarcoinOpt; #[derive(Debug, Parser)] @@ -213,61 +212,14 @@ impl CommandAction for GenerateMultisigTxnCommand { } } } - let signer_address = raw_txn.sender(); - let partial_signed_txn = account_client.sign_txn(raw_txn, signer_address)?; - let my_signatures = if let TransactionAuthenticator::MultiEd25519 { signature, .. } = - partial_signed_txn.authenticator() - { - MultiEd25519SignatureShard::new(signature, *account_public_key.threshold()) - } else { - unreachable!() - }; - // merge my signatures with existing signatures of other participants. - let merged_signatures = { - let mut signatures = vec![]; - if let Some(s) = existing_signatures { - signatures.push(s); - } - signatures.push(my_signatures); - MultiEd25519SignatureShard::merge(signatures)? - }; - eprintln!( - "mutlisig txn(address: {}, threshold: {}): {} signatures collected", - sender, - merged_signatures.threshold(), - merged_signatures.signatures().len() - ); - if !merged_signatures.is_enough() { - eprintln!( - "still require {} signatures", - merged_signatures.threshold() as usize - merged_signatures.signatures().len() - ); - } else { - eprintln!("enough signatures collected for the multisig txn, txn can be submitted now"); - } - - // construct the signed txn with merged signatures. - let signed_txn = { - let authenticator = TransactionAuthenticator::MultiEd25519 { - public_key: account_public_key, - signature: merged_signatures.into(), - }; - SignedUserTransaction::new(partial_signed_txn.into_raw_transaction(), authenticator) - }; - - // output the txn, send this to other participants to sign, or just submit it. - let output_file = { - let mut output_dir = opt.output_dir.clone().unwrap_or(current_dir()?); - // use hash's as output file name - let file_name = signed_txn.crypto_hash().to_hex(); - output_dir.push(file_name); - output_dir.set_extension("multisig-txn"); - output_dir - }; - let mut file = File::create(output_file.clone())?; - // write txn to file - bcs_ext::serialize_into(&mut file, &signed_txn)?; - Ok(output_file) + let output_dir = opt.output_dir.clone().unwrap_or(current_dir()?); + sign_multisig_txn_to_file( + raw_txn.sender(), + account_public_key, + existing_signatures, + account_client.sign_txn(raw_txn, sender)?, + output_dir, + ) } } diff --git a/cmd/starcoin/src/cli_state.rs b/cmd/starcoin/src/cli_state.rs index 5bee48414c..d17960d142 100644 --- a/cmd/starcoin/src/cli_state.rs +++ b/cmd/starcoin/src/cli_state.rs @@ -3,15 +3,12 @@ use std::convert::TryInto; use std::env::current_dir; -use std::fs::File; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::Duration; use anyhow::{bail, format_err, Result}; use serde::de::DeserializeOwned; -use starcoin_crypto::hash::PlainCryptoHash; -use starcoin_crypto::multi_ed25519::multi_shard::MultiEd25519SignatureShard; use starcoin_crypto::HashValue; use bcs_ext::BCSCodec; @@ -28,11 +25,10 @@ use starcoin_vm_types::account_address::AccountAddress; use starcoin_vm_types::account_config::association_address; use starcoin_vm_types::move_resource::MoveResource; use starcoin_vm_types::token::stc::STC_TOKEN_CODE_STR; -use starcoin_vm_types::transaction::authenticator::{AccountPublicKey, TransactionAuthenticator}; -use starcoin_vm_types::transaction::{ - DryRunTransaction, RawUserTransaction, SignedUserTransaction, TransactionPayload, -}; +use starcoin_vm_types::transaction::authenticator::AccountPublicKey; +use starcoin_vm_types::transaction::{DryRunTransaction, RawUserTransaction, TransactionPayload}; +use crate::mutlisig_transaction::sign_multisig_txn_to_file; use crate::view::{ExecuteResultView, ExecutionOutputView, TransactionOptions}; static G_HISTORY_FILE_NAME: &str = "history"; @@ -281,6 +277,7 @@ impl CliState { eprintln!("txn dry run failed"); return Ok(execute_result); } + let signed_txn = self.account_client.sign_txn(raw_txn, sender.address)?; let multisig_public_key = match &public_key { @@ -300,56 +297,13 @@ impl CliState { AccountPublicKey::Multi(m) => m.clone(), }; - // It's multisig account, try to sign it. - let my_signatures = if let TransactionAuthenticator::MultiEd25519 { signature, .. } = - signed_txn.authenticator() - { - MultiEd25519SignatureShard::new(signature, *multisig_public_key.threshold()) - } else { - unreachable!() - }; - - // merge my signatures with existing signatures of other participants. - let merged_signatures = { - let signatures = vec![my_signatures]; - MultiEd25519SignatureShard::merge(signatures)? - }; - eprintln!( - "mutlisig txn(address: {}, threshold: {}): {} signatures collected", + let _ = sign_multisig_txn_to_file( sender.address, - merged_signatures.threshold(), - merged_signatures.signatures().len() + multisig_public_key, + None, + signed_txn, + current_dir()?, ); - if !merged_signatures.is_enough() { - eprintln!( - "still require {} signatures", - merged_signatures.threshold() as usize - merged_signatures.signatures().len() - ); - } else { - eprintln!("enough signatures collected for the multisig txn, txn can be submitted now"); - } - - // construct the signed txn with merged signatures. - let signed_txn = { - let authenticator = TransactionAuthenticator::MultiEd25519 { - public_key: multisig_public_key, - signature: merged_signatures.into(), - }; - SignedUserTransaction::new(signed_txn.into_raw_transaction(), authenticator) - }; - - // output the txn, send this to other participants to sign, or just submit it. - let output_file = { - let mut output_dir = current_dir()?; - // use hash's as output file name - let file_name = signed_txn.crypto_hash().to_hex(); - output_dir.push(file_name); - output_dir.set_extension("multisig-txn"); - output_dir - }; - let mut file = File::create(output_file)?; - // write txn to file - bcs_ext::serialize_into(&mut file, &signed_txn)?; Ok(execute_result) } diff --git a/cmd/starcoin/src/mutlisig_transaction.rs b/cmd/starcoin/src/mutlisig_transaction.rs index 84f3d22b49..76a19fec37 100644 --- a/cmd/starcoin/src/mutlisig_transaction.rs +++ b/cmd/starcoin/src/mutlisig_transaction.rs @@ -1,9 +1,18 @@ +use std::collections::HashMap; +use std::fs::File; +use std::path::PathBuf; + use anyhow::Result; use serde::{Deserialize, Serialize}; use starcoin_crypto::ed25519::{Ed25519PublicKey, Ed25519Signature}; +use starcoin_crypto::hash::PlainCryptoHash; +use starcoin_crypto::multi_ed25519::multi_shard::MultiEd25519SignatureShard; use starcoin_crypto::multi_ed25519::{MultiEd25519PublicKey, MultiEd25519Signature}; + +use starcoin_types::account_address::AccountAddress; +use starcoin_vm_types::transaction::authenticator::TransactionAuthenticator; use starcoin_vm_types::transaction::{RawUserTransaction, SignedUserTransaction}; -use std::collections::HashMap; + #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct MultisigTransaction { raw_txn: RawUserTransaction, @@ -81,3 +90,66 @@ impl MultisigTransaction { )) } } + +pub fn sign_multisig_txn_to_file( + sender: AccountAddress, + multisig_public_key: MultiEd25519PublicKey, + existing_signatures: Option, + partial_signed_txn: SignedUserTransaction, + output_dir: PathBuf, +) -> Result { + let my_signatures = if let TransactionAuthenticator::MultiEd25519 { signature, .. } = + partial_signed_txn.authenticator() + { + MultiEd25519SignatureShard::new(signature, *multisig_public_key.threshold()) + } else { + unreachable!() + }; + + // merge my signatures with existing signatures of other participants. + let merged_signatures = { + let mut signatures = vec![]; + if let Some(s) = existing_signatures { + signatures.push(s); + } + signatures.push(my_signatures); + MultiEd25519SignatureShard::merge(signatures)? + }; + eprintln!( + "mutlisig txn(address: {}, threshold: {}): {} signatures collected", + sender, + merged_signatures.threshold(), + merged_signatures.signatures().len() + ); + if !merged_signatures.is_enough() { + eprintln!( + "still require {} signatures", + merged_signatures.threshold() as usize - merged_signatures.signatures().len() + ); + } else { + eprintln!("enough signatures collected for the multisig txn, txn can be submitted now"); + } + + // construct the signed txn with merged signatures. + let signed_txn = { + let authenticator = TransactionAuthenticator::MultiEd25519 { + public_key: multisig_public_key, + signature: merged_signatures.into(), + }; + SignedUserTransaction::new(partial_signed_txn.into_raw_transaction(), authenticator) + }; + + // output the txn, send this to other participants to sign, or just submit it. + let output_file = { + let mut output_dir = output_dir; + // use hash's as output file name + let file_name = signed_txn.crypto_hash().to_hex(); + output_dir.push(file_name); + output_dir.set_extension("multisig-txn"); + output_dir + }; + let mut file = File::create(output_file.clone())?; + // write txn to file + bcs_ext::serialize_into(&mut file, &signed_txn)?; + Ok(output_file) +} From f904ffdc2dbfbbb2ccee8fe4d858df13c97cbcfc Mon Sep 17 00:00:00 2001 From: Temple3x Date: Wed, 15 Jun 2022 13:38:06 +0800 Subject: [PATCH 3/5] cmd/starcoin: add pub fn submit_txn for cli.state for common usage --- cmd/starcoin/src/account/submit_txn_cmd.rs | 31 +++++--------------- cmd/starcoin/src/account/transfer_cmd.rs | 8 ++++-- cmd/starcoin/src/cli_state.rs | 33 ++++++++++++++++++++-- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/cmd/starcoin/src/account/submit_txn_cmd.rs b/cmd/starcoin/src/account/submit_txn_cmd.rs index 8d428fcfe4..442267ca13 100644 --- a/cmd/starcoin/src/account/submit_txn_cmd.rs +++ b/cmd/starcoin/src/account/submit_txn_cmd.rs @@ -1,15 +1,15 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::cli_state::CliState; -use crate::view::{ExecutionOutputView, FilePathOrHex}; -use crate::StarcoinOpt; use anyhow::Result; use clap::Parser; + use scmd::{CommandAction, ExecContext}; -use starcoin_rpc_api::types::SignedUserTransactionView; use starcoin_vm_types::transaction::SignedUserTransaction; -use std::convert::TryInto; + +use crate::cli_state::CliState; +use crate::view::{ExecutionOutputView, FilePathOrHex}; +use crate::StarcoinOpt; #[derive(Debug, Parser)] /// Submit a SignedTransaction file or hex to transaction pool. @@ -41,27 +41,10 @@ impl CommandAction for SubmitSignedTxnCommand { ctx: &ExecContext, ) -> Result { let opt = ctx.opt(); - let client = ctx.state().client(); + let signed_txn: SignedUserTransaction = bcs_ext::from_bytes(opt.signed_txn_file_or_hex.as_bytes()?.as_slice())?; - let mut signed_txn_view: SignedUserTransactionView = signed_txn.clone().try_into()?; - signed_txn_view.raw_txn.decoded_payload = - Some(ctx.state().decode_txn_payload(signed_txn.payload())?.into()); - - eprintln!( - "Prepare to submit the transaction: \n {}", - serde_json::to_string_pretty(&signed_txn_view)? - ); - let txn_hash = signed_txn.id(); - client.submit_transaction(signed_txn)?; - - eprintln!("txn {:#x} submitted.", txn_hash); - - if opt.blocking { - ctx.state().watch_txn(txn_hash) - } else { - Ok(ExecutionOutputView::new(txn_hash)) - } + ctx.state().submit_txn(signed_txn, opt.blocking) } } diff --git a/cmd/starcoin/src/account/transfer_cmd.rs b/cmd/starcoin/src/account/transfer_cmd.rs index 3ddf3ddf5a..1fa9b738a6 100644 --- a/cmd/starcoin/src/account/transfer_cmd.rs +++ b/cmd/starcoin/src/account/transfer_cmd.rs @@ -1,17 +1,19 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::cli_state::CliState; -use crate::view::{ExecuteResultView, TransactionOptions}; -use crate::StarcoinOpt; use anyhow::Result; use clap::Parser; + use scmd::{CommandAction, ExecContext}; use starcoin_types::account_address::AccountAddress; use starcoin_vm_types::token::stc::G_STC_TOKEN_CODE; use starcoin_vm_types::token::token_code::TokenCode; use starcoin_vm_types::transaction::TransactionPayload; +use crate::cli_state::CliState; +use crate::view::{ExecuteResultView, TransactionOptions}; +use crate::StarcoinOpt; + /// Transfer token's command, this command will send a transaction to the chain. #[derive(Debug, Parser)] #[clap(name = "transfer")] diff --git a/cmd/starcoin/src/cli_state.rs b/cmd/starcoin/src/cli_state.rs index d17960d142..cabce8fa6b 100644 --- a/cmd/starcoin/src/cli_state.rs +++ b/cmd/starcoin/src/cli_state.rs @@ -17,7 +17,9 @@ use starcoin_account_api::{AccountInfo, AccountProvider}; use starcoin_config::{ChainNetworkID, DataDirPath}; use starcoin_node::NodeHandle; use starcoin_rpc_api::chain::GetEventOption; -use starcoin_rpc_api::types::{RawUserTransactionView, TransactionStatusView}; +use starcoin_rpc_api::types::{ + RawUserTransactionView, SignedUserTransactionView, TransactionStatusView, +}; use starcoin_rpc_client::{RpcClient, StateRootOption}; use starcoin_state_api::StateReaderExt; use starcoin_types::account_config::AccountResource; @@ -26,7 +28,9 @@ use starcoin_vm_types::account_config::association_address; use starcoin_vm_types::move_resource::MoveResource; use starcoin_vm_types::token::stc::STC_TOKEN_CODE_STR; use starcoin_vm_types::transaction::authenticator::AccountPublicKey; -use starcoin_vm_types::transaction::{DryRunTransaction, RawUserTransaction, TransactionPayload}; +use starcoin_vm_types::transaction::{ + DryRunTransaction, RawUserTransaction, SignedUserTransaction, TransactionPayload, +}; use crate::mutlisig_transaction::sign_multisig_txn_to_file; use crate::view::{ExecuteResultView, ExecutionOutputView, TransactionOptions}; @@ -319,4 +323,29 @@ impl CliState { pub fn into_inner(self) -> (ChainNetworkID, Arc, Option) { (self.net, self.client, self.node_handle) } + + pub fn submit_txn( + &self, + signed_txn: SignedUserTransaction, + blocking: bool, + ) -> Result { + let mut signed_txn_view: SignedUserTransactionView = signed_txn.clone().try_into()?; + signed_txn_view.raw_txn.decoded_payload = + Some(self.decode_txn_payload(signed_txn.payload())?.into()); + + eprintln!( + "Prepare to submit the transaction: \n {}", + serde_json::to_string_pretty(&signed_txn_view)? + ); + let txn_hash = signed_txn.id(); + self.client().submit_transaction(signed_txn)?; + + eprintln!("txn {:#x} submitted.", txn_hash); + + if blocking { + self.watch_txn(txn_hash) + } else { + Ok(ExecutionOutputView::new(txn_hash)) + } + } } From 5ab037a9c1984f6b6134cbfba758bccc010ab1c4 Mon Sep 17 00:00:00 2001 From: Temple3x Date: Thu, 16 Jun 2022 17:38:06 +0800 Subject: [PATCH 4/5] cmd/starcoin: add pub fn read_multisig_existing_signatures for common usage --- .../src/account/sign_multisig_txn_cmd.rs | 25 +++-------------- cmd/starcoin/src/mutlisig_transaction.rs | 28 +++++++++++++++++-- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs b/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs index a47ea22fad..bda8fb48a9 100644 --- a/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs +++ b/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs @@ -7,17 +7,14 @@ use std::path::PathBuf; use anyhow::{bail, Result}; use clap::Parser; -use starcoin_crypto::multi_ed25519::multi_shard::MultiEd25519SignatureShard; use scmd::{CommandAction, ExecContext}; use starcoin_account_api::AccountPublicKey; use starcoin_rpc_api::types::{FunctionIdView, RawUserTransactionView, TransactionStatusView}; use starcoin_rpc_client::StateRootOption; use starcoin_state_api::StateReaderExt; -use starcoin_types::transaction::authenticator::TransactionAuthenticator; use starcoin_types::transaction::{ - parse_transaction_argument, DryRunTransaction, RawUserTransaction, SignedUserTransaction, - TransactionArgument, + parse_transaction_argument, DryRunTransaction, RawUserTransaction, TransactionArgument, }; use starcoin_vm_types::account_address::AccountAddress; use starcoin_vm_types::token::stc::STC_TOKEN_CODE_STR; @@ -26,7 +23,7 @@ use starcoin_vm_types::transaction_argument::convert_txn_args; use starcoin_vm_types::{language_storage::TypeTag, parser::parse_type_tag}; use crate::cli_state::CliState; -use crate::mutlisig_transaction::sign_multisig_txn_to_file; +use crate::mutlisig_transaction::{read_multisig_existing_signatures, sign_multisig_txn_to_file}; use crate::StarcoinOpt; #[derive(Debug, Parser)] @@ -143,22 +140,8 @@ impl CommandAction for GenerateMultisigTxnCommand { ); (raw_txn, None) } else if let Some(file_input) = opt.multisig_txn_file.as_ref() { - let txn: SignedUserTransaction = - bcs_ext::from_bytes(&std::fs::read(file_input.as_path())?)?; - - let existing_signatures = match txn.authenticator() { - TransactionAuthenticator::Ed25519 { .. } => { - bail!( - "expect a multisig txn in file {}", - file_input.as_path().display() - ); - } - TransactionAuthenticator::MultiEd25519 { - public_key, - signature, - } => MultiEd25519SignatureShard::new(signature, *public_key.threshold()), - }; - (txn.raw_txn().clone(), Some(existing_signatures)) + let read_ret = read_multisig_existing_signatures(file_input.as_path()).unwrap(); + (read_ret.txn, read_ret.signatures) } else { unreachable!() }; diff --git a/cmd/starcoin/src/mutlisig_transaction.rs b/cmd/starcoin/src/mutlisig_transaction.rs index 76a19fec37..37f2d5921a 100644 --- a/cmd/starcoin/src/mutlisig_transaction.rs +++ b/cmd/starcoin/src/mutlisig_transaction.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use std::fs::File; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; -use anyhow::Result; +use anyhow::{bail, Result}; use serde::{Deserialize, Serialize}; use starcoin_crypto::ed25519::{Ed25519PublicKey, Ed25519Signature}; use starcoin_crypto::hash::PlainCryptoHash; @@ -91,6 +91,30 @@ impl MultisigTransaction { } } +pub struct RawTxnMultiSign { + pub txn: RawUserTransaction, + pub signatures: Option, +} + +pub fn read_multisig_existing_signatures(file_input: &Path) -> Result { + let txn: SignedUserTransaction = bcs_ext::from_bytes(&std::fs::read(file_input)?)?; + + let existing_signatures = match txn.authenticator() { + TransactionAuthenticator::Ed25519 { .. } => { + bail!("expect a multisig txn in file {}", file_input.display()); + } + TransactionAuthenticator::MultiEd25519 { + public_key, + signature, + } => MultiEd25519SignatureShard::new(signature, *public_key.threshold()), + }; + + Ok(RawTxnMultiSign { + txn: txn.raw_txn().clone(), + signatures: Some(existing_signatures), + }) +} + pub fn sign_multisig_txn_to_file( sender: AccountAddress, multisig_public_key: MultiEd25519PublicKey, From 08ff50e2f3ccd1be69897ee72cd31828a8e8df5f Mon Sep 17 00:00:00 2001 From: Temple3x Date: Thu, 16 Jun 2022 19:24:17 +0800 Subject: [PATCH 5/5] cmd/starcoin: submit multisig txn when signatures enough in execute txn process --- .../src/account/sign_multisig_txn_cmd.rs | 6 +- cmd/starcoin/src/cli_state.rs | 85 ++++++++++++++++++- cmd/starcoin/src/mutlisig_transaction.rs | 68 +-------------- 3 files changed, 87 insertions(+), 72 deletions(-) diff --git a/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs b/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs index bda8fb48a9..3638952354 100644 --- a/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs +++ b/cmd/starcoin/src/account/sign_multisig_txn_cmd.rs @@ -23,7 +23,7 @@ use starcoin_vm_types::transaction_argument::convert_txn_args; use starcoin_vm_types::{language_storage::TypeTag, parser::parse_type_tag}; use crate::cli_state::CliState; -use crate::mutlisig_transaction::{read_multisig_existing_signatures, sign_multisig_txn_to_file}; +use crate::mutlisig_transaction::read_multisig_existing_signatures; use crate::StarcoinOpt; #[derive(Debug, Parser)] @@ -197,12 +197,14 @@ impl CommandAction for GenerateMultisigTxnCommand { } let output_dir = opt.output_dir.clone().unwrap_or(current_dir()?); - sign_multisig_txn_to_file( + ctx.state().sign_multisig_txn_to_file_or_submit( raw_txn.sender(), account_public_key, existing_signatures, account_client.sign_txn(raw_txn, sender)?, output_dir, + false, + false, ) } } diff --git a/cmd/starcoin/src/cli_state.rs b/cmd/starcoin/src/cli_state.rs index cabce8fa6b..74ad343dbb 100644 --- a/cmd/starcoin/src/cli_state.rs +++ b/cmd/starcoin/src/cli_state.rs @@ -3,12 +3,16 @@ use std::convert::TryInto; use std::env::current_dir; +use std::fs::File; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::Duration; use anyhow::{bail, format_err, Result}; use serde::de::DeserializeOwned; +use starcoin_crypto::hash::PlainCryptoHash; +use starcoin_crypto::multi_ed25519::multi_shard::MultiEd25519SignatureShard; +use starcoin_crypto::multi_ed25519::MultiEd25519PublicKey; use starcoin_crypto::HashValue; use bcs_ext::BCSCodec; @@ -27,12 +31,11 @@ use starcoin_vm_types::account_address::AccountAddress; use starcoin_vm_types::account_config::association_address; use starcoin_vm_types::move_resource::MoveResource; use starcoin_vm_types::token::stc::STC_TOKEN_CODE_STR; -use starcoin_vm_types::transaction::authenticator::AccountPublicKey; +use starcoin_vm_types::transaction::authenticator::{AccountPublicKey, TransactionAuthenticator}; use starcoin_vm_types::transaction::{ DryRunTransaction, RawUserTransaction, SignedUserTransaction, TransactionPayload, }; -use crate::mutlisig_transaction::sign_multisig_txn_to_file; use crate::view::{ExecuteResultView, ExecutionOutputView, TransactionOptions}; static G_HISTORY_FILE_NAME: &str = "history"; @@ -301,12 +304,14 @@ impl CliState { AccountPublicKey::Multi(m) => m.clone(), }; - let _ = sign_multisig_txn_to_file( + let _ = self.sign_multisig_txn_to_file_or_submit( sender.address, multisig_public_key, None, signed_txn, current_dir()?, + true, + blocking, ); Ok(execute_result) @@ -324,6 +329,80 @@ impl CliState { (self.net, self.client, self.node_handle) } + // Sign multisig transaction, if enough signatures collected & submit is true, + // try to submit txn directly. + // Otherwise, keep signatures into file. + pub fn sign_multisig_txn_to_file_or_submit( + &self, + sender: AccountAddress, + multisig_public_key: MultiEd25519PublicKey, + existing_signatures: Option, + partial_signed_txn: SignedUserTransaction, + output_dir: PathBuf, + submit: bool, + blocking: bool, + ) -> Result { + let my_signatures = if let TransactionAuthenticator::MultiEd25519 { signature, .. } = + partial_signed_txn.authenticator() + { + MultiEd25519SignatureShard::new(signature, *multisig_public_key.threshold()) + } else { + unreachable!() + }; + + // merge my signatures with existing signatures of other participants. + let merged_signatures = { + let mut signatures = vec![]; + if let Some(s) = existing_signatures { + signatures.push(s); + } + signatures.push(my_signatures); + MultiEd25519SignatureShard::merge(signatures)? + }; + eprintln!( + "mutlisig txn(address: {}, threshold: {}): {} signatures collected", + sender, + merged_signatures.threshold(), + merged_signatures.signatures().len() + ); + if !merged_signatures.is_enough() { + eprintln!( + "still require {} signatures", + merged_signatures.threshold() as usize - merged_signatures.signatures().len() + ); + } else { + eprintln!("enough signatures collected for the multisig txn, txn can be submitted now"); + } + + // construct the signed txn with merged signatures. + let signed_txn = { + let authenticator = TransactionAuthenticator::MultiEd25519 { + public_key: multisig_public_key, + signature: merged_signatures.into(), + }; + SignedUserTransaction::new(partial_signed_txn.into_raw_transaction(), authenticator) + }; + + if submit { + let _ = self.submit_txn(signed_txn, blocking); + return Ok(PathBuf::new()); + } + + // output the txn, send this to other participants to sign, or just submit it. + let output_file = { + let mut output_dir = output_dir; + // use hash's as output file name + let file_name = signed_txn.crypto_hash().to_hex(); + output_dir.push(file_name); + output_dir.set_extension("multisig-txn"); + output_dir + }; + let mut file = File::create(output_file.clone())?; + // write txn to file + bcs_ext::serialize_into(&mut file, &signed_txn)?; + Ok(output_file) + } + pub fn submit_txn( &self, signed_txn: SignedUserTransaction, diff --git a/cmd/starcoin/src/mutlisig_transaction.rs b/cmd/starcoin/src/mutlisig_transaction.rs index 37f2d5921a..dd21fa82ef 100644 --- a/cmd/starcoin/src/mutlisig_transaction.rs +++ b/cmd/starcoin/src/mutlisig_transaction.rs @@ -1,15 +1,12 @@ use std::collections::HashMap; -use std::fs::File; -use std::path::{Path, PathBuf}; +use std::path::Path; use anyhow::{bail, Result}; use serde::{Deserialize, Serialize}; use starcoin_crypto::ed25519::{Ed25519PublicKey, Ed25519Signature}; -use starcoin_crypto::hash::PlainCryptoHash; use starcoin_crypto::multi_ed25519::multi_shard::MultiEd25519SignatureShard; use starcoin_crypto::multi_ed25519::{MultiEd25519PublicKey, MultiEd25519Signature}; -use starcoin_types::account_address::AccountAddress; use starcoin_vm_types::transaction::authenticator::TransactionAuthenticator; use starcoin_vm_types::transaction::{RawUserTransaction, SignedUserTransaction}; @@ -114,66 +111,3 @@ pub fn read_multisig_existing_signatures(file_input: &Path) -> Result, - partial_signed_txn: SignedUserTransaction, - output_dir: PathBuf, -) -> Result { - let my_signatures = if let TransactionAuthenticator::MultiEd25519 { signature, .. } = - partial_signed_txn.authenticator() - { - MultiEd25519SignatureShard::new(signature, *multisig_public_key.threshold()) - } else { - unreachable!() - }; - - // merge my signatures with existing signatures of other participants. - let merged_signatures = { - let mut signatures = vec![]; - if let Some(s) = existing_signatures { - signatures.push(s); - } - signatures.push(my_signatures); - MultiEd25519SignatureShard::merge(signatures)? - }; - eprintln!( - "mutlisig txn(address: {}, threshold: {}): {} signatures collected", - sender, - merged_signatures.threshold(), - merged_signatures.signatures().len() - ); - if !merged_signatures.is_enough() { - eprintln!( - "still require {} signatures", - merged_signatures.threshold() as usize - merged_signatures.signatures().len() - ); - } else { - eprintln!("enough signatures collected for the multisig txn, txn can be submitted now"); - } - - // construct the signed txn with merged signatures. - let signed_txn = { - let authenticator = TransactionAuthenticator::MultiEd25519 { - public_key: multisig_public_key, - signature: merged_signatures.into(), - }; - SignedUserTransaction::new(partial_signed_txn.into_raw_transaction(), authenticator) - }; - - // output the txn, send this to other participants to sign, or just submit it. - let output_file = { - let mut output_dir = output_dir; - // use hash's as output file name - let file_name = signed_txn.crypto_hash().to_hex(); - output_dir.push(file_name); - output_dir.set_extension("multisig-txn"); - output_dir - }; - let mut file = File::create(output_file.clone())?; - // write txn to file - bcs_ext::serialize_into(&mut file, &signed_txn)?; - Ok(output_file) -}