Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixup
Browse files Browse the repository at this point in the history
jolestar committed Sep 10, 2024

Verified

This commit was signed with the committer’s verified signature.
aaronfranke Aaron Franke
1 parent 041bd08 commit 0b8c541
Showing 9 changed files with 98 additions and 57 deletions.
14 changes: 11 additions & 3 deletions crates/rooch-rpc-client/src/rooch_client.rs
Original file line number Diff line number Diff line change
@@ -20,9 +20,9 @@ use rooch_rpc_api::jsonrpc_types::{
DryRunTransactionResponseView, InscriptionPageView, UTXOPageView,
};
use rooch_rpc_api::jsonrpc_types::{
AccessPathView, AnnotatedFunctionResultView, BalanceInfoPageView, EventOptions, EventPageView,
FieldKeyView, ObjectIDVecView, ObjectIDView, RoochAddressView, StateOptions, StatePageView,
StructTagView,
AccessPathView, AnnotatedFunctionResultView, BalanceInfoPageView, BytesView, EventOptions,
EventPageView, FieldKeyView, ObjectIDVecView, ObjectIDView, RoochAddressView, StateOptions,
StatePageView, StructTagView,
};
use rooch_rpc_api::jsonrpc_types::{ExecuteTransactionResponseView, ObjectStateView};
use rooch_rpc_api::jsonrpc_types::{
@@ -382,4 +382,12 @@ impl RoochRpcClient {
Ok(None)
}
}

pub async fn broadcast_bitcoin_tx(
&self,
raw_tx: BytesView,
maxfeerate: Option<f64>,
) -> Result<String> {
Ok(self.http.broadcast_tx(raw_tx, maxfeerate, None).await?)
}
}
7 changes: 5 additions & 2 deletions crates/rooch-rpc-client/src/wallet_context.rs
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ use bitcoin::key::Secp256k1;
use bitcoin::psbt::{GetKey, KeyRequest};
use bitcoin::secp256k1::Signing;
use bitcoin::PrivateKey;
use log::debug;
use move_core_types::account_address::AccountAddress;
use moveos_types::moveos_std::gas_schedule::GasScheduleConfig;
use moveos_types::transaction::MoveAction;
@@ -27,13 +28,14 @@ use rooch_types::error::{RoochError, RoochResult};
use rooch_types::rooch_network::{BuiltinChainID, RoochNetwork};
use rooch_types::transaction::rooch::{RoochTransaction, RoochTransactionData};
use rooch_types::{addresses, crypto};
use tracing::info;
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;
use tracing::info;

#[derive(Debug)]
pub struct WalletContext {
client: Arc<RwLock<Option<Client>>>,
pub client_config: PersistedConfig<ClientConfig>,
@@ -327,6 +329,7 @@ impl GetKey for WalletContext {
key_request: KeyRequest,
_secp: &Secp256k1<C>,
) -> Result<Option<PrivateKey>, Self::Error> {
debug!("Get key for key_request: {:?}", key_request);
let address = match key_request {
KeyRequest::Pubkey(pubkey) => {
let rooch_public_key = crypto::PublicKey::from_bitcoin_pubkey(&pubkey)?;
@@ -337,7 +340,7 @@ impl GetKey for WalletContext {
}
_ => anyhow::bail!("Unsupported key request: {:?}", key_request),
};

debug!("Get key for address: {:?}", address);
let kp = self
.keystore
.get_key_pair(&address, self.password.clone())?;
25 changes: 21 additions & 4 deletions crates/rooch/src/commands/bitcoin/broadcast_tx.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
// Copyright (c) RoochNetwork
// SPDX-License-Identifier: Apache-2.0

use crate::cli_types::CommandAction;
use crate::{
cli_types::{CommandAction, WalletContextOptions},
commands::transaction::commands::is_file_path,
};
use async_trait::async_trait;
use clap::Parser;
use rooch_types::error::RoochResult;

#[derive(Debug, Parser)]
pub struct BroadcastTx {
input_file: String,
input: String,
#[clap(flatten)]
pub(crate) context_options: WalletContextOptions,
}

#[async_trait]
impl CommandAction<String> for BroadcastTx {
async fn execute(self) -> RoochResult<String> {
// Implement broadcast-tx logic here
Ok(format!("Broadcasted transaction from {}", self.input_file))
let context = self.context_options.build()?;
let client = context.get_client().await?;

let bytes = if is_file_path(&self.input) {
std::fs::read(self.input)?
} else {
let hex = self.input.strip_prefix("0x").unwrap_or(&self.input);
hex::decode(hex)?
};

Ok(client
.rooch
.broadcast_bitcoin_tx(bytes.into(), None)
.await?)
}
}
60 changes: 27 additions & 33 deletions crates/rooch/src/commands/bitcoin/build_tx.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@

use super::transaction_builder::TransactionBuilder;
use crate::cli_types::{CommandAction, WalletContextOptions};
use anyhow::ensure;
use async_trait::async_trait;
use bitcoin::absolute::LockTime;
use bitcoin::{Amount, FeeRate, OutPoint};
@@ -56,11 +55,15 @@ impl FromStr for ParsedOutput {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split(':').collect();
ensure!(parts.len() == 2, "Invalid output format: {}", s);
let address = ParsedAddress::parse(parts[0])?;
let amount = Amount::from_str(parts[1])?;
Ok(ParsedOutput { address, amount })
let (addr_part, amount_part) = s
.split_once(':')
.ok_or_else(|| RoochError::CommandArgumentError("Invalid output format".to_string()))?;
let address = ParsedAddress::parse(addr_part)?;
let amount = u64::from_str(amount_part)?;
Ok(ParsedOutput {
address,
amount: Amount::from_sat(amount),
})
}
}

@@ -75,15 +78,14 @@ pub struct BuildTx {
#[clap(long, short = 'i')]
inputs: Vec<ParsedInput>,

/// The to address of the transaction, if not specified, the outputs will be used
#[clap(long, short = 't', conflicts_with = "outputs", group = "to", value_parser=ParsedAddress::parse)]
to: Option<ParsedAddress>,
// /// The to address of the transaction, if not specified, the outputs will be used
// #[clap(long, short = 't', conflicts_with = "outputs", group = "to", value_parser=ParsedAddress::parse)]
// to: Option<ParsedAddress>,

/// The amount of the transaction, if not specified, the amount will be calculated automatically
#[clap(long, short = 'a', conflicts_with = "outputs", group = "to")]
amount: Option<Amount>,

#[clap(long, short = 'o')]
// /// The amount of the transaction, if not specified, the amount will be calculated automatically
// #[clap(long, short = 'a', conflicts_with = "outputs", group = "to")]
// amount: Option<Amount>,
#[clap(long, short = 'o', required = true, num_args = 1..)]
outputs: Vec<ParsedOutput>,

/// The fee rate of the transaction, if not specified, the fee will be calculated automatically
@@ -113,7 +115,7 @@ pub struct BuildTx {
#[async_trait]
impl CommandAction<String> for BuildTx {
async fn execute(self) -> RoochResult<String> {
let context = self.context_options.build()?;
let context = self.context_options.build_require_password()?;
let client = context.get_client().await?;

let bitcoin_network = context.get_bitcoin_network().await?;
@@ -126,6 +128,7 @@ impl CommandAction<String> for BuildTx {
.map(|input| input.into_object_id())
.collect();
let mut tx_builder = TransactionBuilder::new(
&context,
client,
sender.to_bitcoin_address(bitcoin_network)?,
inputs,
@@ -144,24 +147,15 @@ impl CommandAction<String> for BuildTx {
tx_builder =
tx_builder.with_change_address(change_address.to_bitcoin_address(bitcoin_network)?);
}
let psbt = if let Some(to) = self.to {
let amount = self.amount.ok_or_else(|| {
RoochError::CommandArgumentError("Amount is required".to_string())
})?;
let to = context.resolve_bitcoin_address(to).await?;
tx_builder
.build_transfer(to.to_bitcoin_address(bitcoin_network)?, amount)
.await?
} else {
let mut outputs = Vec::new();
for output in self.outputs.iter() {
let address = context
.resolve_bitcoin_address(output.address.clone())
.await?;
outputs.push((address.to_bitcoin_address(bitcoin_network)?, output.amount));
}
tx_builder.build(outputs).await?
};

let mut outputs = Vec::new();
for output in self.outputs.iter() {
let address = context
.resolve_bitcoin_address(output.address.clone())
.await?;
outputs.push((address.to_bitcoin_address(bitcoin_network)?, output.amount));
}
let psbt = tx_builder.build(outputs).await?;
info!("PSBT: {}", serde_json::to_string_pretty(&psbt).unwrap());
Ok(psbt.serialize_hex())
}
9 changes: 6 additions & 3 deletions crates/rooch/src/commands/bitcoin/sign_tx.rs
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ use rooch_types::{
bitcoin::multisign_account::{self, MultisignAccountModule},
error::RoochResult,
};
use tracing::debug;

#[derive(Debug, Parser)]
pub struct SignTx {
@@ -44,6 +45,7 @@ impl CommandAction<String> for SignTx {
let psbt = bitcoin::Psbt::deserialize(&bytes)?;

let tx = sign_tx(psbt, &context, &client).await?;
debug!("Signed tx: {:?}", tx);
let mut bytes = vec![];
tx.consensus_encode(&mut bytes)?;
let signed_tx_hex = hex::encode(bytes);
@@ -75,9 +77,10 @@ async fn sign_tx(
let kp = context.get_key_pair(&rooch_addr)?;
multisign_account::sign_taproot_multisig(&mut psbt, &kp)?;
}

psbt.sign(context, &secp)
debug!("Signed psbt: {:?}", psbt);
let sign_keys = psbt
.sign(context, &secp)
.map_err(|_e| anyhow::anyhow!("Sign psbt errror"))?;

debug!("Sign keys: {:?}", sign_keys);
Ok(psbt.extract_tx()?)
}
21 changes: 16 additions & 5 deletions crates/rooch/src/commands/bitcoin/transaction_builder.rs
Original file line number Diff line number Diff line change
@@ -6,32 +6,34 @@ use std::{collections::HashSet, str::FromStr};
use super::utxo_selector::UTXOSelector;
use anyhow::{anyhow, bail, Result};
use bitcoin::{
absolute::LockTime, transaction::Version, Address, Amount, FeeRate, OutPoint, Psbt, ScriptBuf,
Sequence, Transaction, TxIn, TxOut, Witness,
absolute::LockTime, bip32::Fingerprint, transaction::Version, Address, Amount, FeeRate,
OutPoint, Psbt, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Witness,
};
use moveos_types::{module_binding::MoveFunctionCaller, moveos_std::object::ObjectID};
use rooch_rpc_api::jsonrpc_types::{btc::utxo::UTXOView, ObjectMetaView};
use rooch_rpc_client::Client;
use rooch_rpc_client::{wallet_context::WalletContext, Client};
use rooch_types::{
address::BitcoinAddress,
bitcoin::multisign_account::{self, MultisignAccountInfo},
};

#[derive(Debug)]
pub struct TransactionBuilder {
pub struct TransactionBuilder<'a> {
wallet_context: &'a WalletContext,
client: Client,
utxo_selector: UTXOSelector,
fee_rate: FeeRate,
change_address: Address,
lock_time: Option<LockTime>,
}

impl TransactionBuilder {
impl<'a> TransactionBuilder<'a> {
const ADDITIONAL_INPUT_VBYTES: usize = 58;
const ADDITIONAL_OUTPUT_VBYTES: usize = 43;
const SCHNORR_SIGNATURE_SIZE: usize = 64;

pub async fn new(
wallet_context: &'a WalletContext,
client: Client,
sender: Address,
inputs: Vec<ObjectID>,
@@ -40,6 +42,7 @@ impl TransactionBuilder {
let utxo_selector =
UTXOSelector::new(client.clone(), sender.clone(), inputs, skip_seal_check).await?;
Ok(Self {
wallet_context,
client,
utxo_selector,
fee_rate: FeeRate::from_sat_per_vb(10).unwrap(),
@@ -177,6 +180,14 @@ impl TransactionBuilder {
),
None => true,
};
let rooch_addr = BitcoinAddress::from(bitcoin_addr.clone()).to_rooch_address();
let kp = self.wallet_context.get_key_pair(&rooch_addr)?;

psbt.inputs[idx].bip32_derivation.insert(
kp.bitcoin_public_key()?.inner,
(Fingerprint::default(), Default::default()),
);

if is_witness {
psbt.inputs[idx].witness_utxo = Some(TxOut {
value: utxo.amount(),
3 changes: 3 additions & 0 deletions crates/rooch/src/commands/bitcoin/utxo_selector.rs
Original file line number Diff line number Diff line change
@@ -41,6 +41,9 @@ impl UTXOSelector {
}

async fn load_specific_utxos(&mut self) -> Result<()> {
if self.specific_utxos.is_empty() {
return Ok(());
}
let utxos_objs = self
.client
.rooch
10 changes: 6 additions & 4 deletions crates/rooch/src/lib.rs
Original file line number Diff line number Diff line change
@@ -11,10 +11,10 @@ use clap::builder::{
};
use cli_types::CommandAction;
use commands::{
abi::ABI, account::Account, dynamic_field::DynamicField, env::Env, genesis::Genesis,
init::Init, move_cli::MoveCli, object::ObjectCommand, resource::ResourceCommand, rpc::Rpc,
server::Server, session_key::SessionKey, state::StateCommand, transaction::Transaction,
upgrade::Upgrade, util::Util, version::Version,
abi::ABI, account::Account, bitcoin::Bitcoin, dynamic_field::DynamicField, env::Env,
genesis::Genesis, init::Init, move_cli::MoveCli, object::ObjectCommand,
resource::ResourceCommand, rpc::Rpc, server::Server, session_key::SessionKey,
state::StateCommand, transaction::Transaction, upgrade::Upgrade, util::Util, version::Version,
};
use once_cell::sync::Lazy;
use rooch_types::error::RoochResult;
@@ -46,6 +46,7 @@ static LONG_VERSION: Lazy<String> = Lazy::new(|| {
pub enum Command {
Version(Version),
Account(Account),
Bitcoin(Bitcoin),
Init(Init),
Move(MoveCli),
Server(Server),
@@ -72,6 +73,7 @@ pub async fn run_cli(opt: RoochCli) -> RoochResult<String> {
match opt.cmd {
Command::Version(version) => version.execute().await,
Command::Account(account) => account.execute().await,
Command::Bitcoin(bitcoin) => bitcoin.execute_serialized().await,
Command::Move(move_cli) => move_cli.execute().await,
Command::Server(server) => server.execute().await,
Command::Init(init) => init.execute_serialized().await,
6 changes: 3 additions & 3 deletions scripts/bitcoin/README.md
Original file line number Diff line number Diff line change
@@ -13,11 +13,11 @@ This directory contains scripts for setting up a local development environment f

## Development on rooch

1. Run `rooch server start --btc-rpc-url http://127.0.0.1:18443 --btc-rpc-username roochuser --btc-rpc-password roochpass`
1. Run `rooch server start -n local --btc-sync-block-interval 1 --btc-rpc-url http://127.0.0.1:18443 --btc-rpc-username roochuser --btc-rpc-password roochpass`
2. Run `rooch account list --json` to get the `bitcoin_address`
3. Run `bitcoin-cli generatetoaddress 101 <bitcoin_address>` to generate 101 blocks to the address
2. Run `rooch rpc request --method rooch_queryObjectStates --params '[{"object_type":"0x4::utxo::UTXO"}, null, "2", {"descending": true,"showDisplay":false}]'` to query the UTXO set
3. Run `rooch rpc request --method rooch_queryObjectStates --params '[{"object_type":"0x4::ord::Inscription"}, null, "2", {"descending": true,"showDisplay":false}]'` to query the Inscription set
2. Run `rooch rpc request --method btc_queryUTXOs --params '["all", null, "2", true]'` to query the UTXO set
3. Run `rooch rpc request --method btc_queryInscriptions --params '["all", null, "2", true]'` to query the Inscription set

## Usage

0 comments on commit 0b8c541

Please sign in to comment.