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

Create ContractSignerProvider #188

Merged
merged 1 commit into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 146 additions & 66 deletions bitcoin-rpc-provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use std::sync::{Arc, Mutex};
use std::time::Duration;

use bitcoin::consensus::encode::Error as EncodeError;
use bitcoin::hashes::hex::ToHex;
use bitcoin::hashes::serde;
use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::secp256k1::rand::thread_rng;
use bitcoin::secp256k1::{PublicKey, SecretKey};
Expand All @@ -15,10 +17,12 @@ use bitcoin::{
Txid,
};
use bitcoin::{Address, OutPoint, TxOut};
use bitcoincore_rpc::jsonrpc::serde_json;
use bitcoincore_rpc::jsonrpc::serde_json::Value;
use bitcoincore_rpc::{json, Auth, Client, RpcApi};
use bitcoincore_rpc_json::AddressType;
use dlc_manager::error::Error as ManagerError;
use dlc_manager::{Blockchain, Signer, Utxo, Wallet};
use dlc_manager::{Blockchain, ContractSignerProvider, SimpleSigner, Utxo, Wallet};
use json::EstimateMode;
use lightning::chain::chaininterface::{ConfirmationTarget, FeeEstimator};
use log::error;
Expand Down Expand Up @@ -102,7 +106,7 @@ impl BitcoinCoreProvider {

pub fn new_from_rpc_client(rpc_client: Client) -> Self {
let client = Arc::new(Mutex::new(rpc_client));
let mut fees: HashMap<ConfirmationTarget, AtomicU32> = HashMap::new();
let mut fees: HashMap<ConfirmationTarget, AtomicU32> = HashMap::with_capacity(7);
fees.insert(ConfirmationTarget::OnChainSweep, AtomicU32::new(5000));
fees.insert(
ConfirmationTarget::MaxAllowedNonAnchorChannelRemoteFee,
Expand Down Expand Up @@ -168,7 +172,57 @@ fn enc_err_to_manager_err(_e: EncodeError) -> ManagerError {
Error::BitcoinError.into()
}

impl Signer for BitcoinCoreProvider {
impl ContractSignerProvider for BitcoinCoreProvider {
type Signer = SimpleSigner;

fn derive_signer_key_id(&self, _is_offer_party: bool, temp_id: [u8; 32]) -> [u8; 32] {
temp_id // fixme not safe
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like this method should take more parameters, like maybe the collateral values, the event id, or whatever could be use to generate a likely unique key id. Then IIUC this should be called only once, so if using the method I mention below, you could check that the label does not exist (and maybe panic if it does?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A lot of the parameters aren't really always available. The main problems comes from renew_offer where you have to do it from only a single party's params

}

fn derive_contract_signer(&self, keys_id: [u8; 32]) -> Result<Self::Signer, ManagerError> {
let label_map = self
.client
.lock()
.unwrap()
.call::<HashMap<Address, Value>>(
"getaddressesbylabel",
&[Value::String(keys_id.to_hex())],
)
.map_err(rpc_err_to_manager_err)?;

if let Some(address) = label_map.keys().next() {
// we should only have one address per keys_id
// if not something has gone wrong
assert_eq!(label_map.len(), 1);

let pk = self
.client
.lock()
.unwrap()
.dump_private_key(address)
.map_err(rpc_err_to_manager_err)?;
Ok(SimpleSigner::new(pk.inner))
} else {
let sk = SecretKey::new(&mut thread_rng());
let network = self.get_network()?;
self.client
.lock()
.unwrap()
.import_private_key(
&PrivateKey {
compressed: true,
network,
inner: sk,
},
Some(&keys_id.to_hex()),
Some(false),
)
.map_err(rpc_err_to_manager_err)?;

Ok(SimpleSigner::new(sk))
}
}

fn get_secret_key_for_pubkey(&self, pubkey: &PublicKey) -> Result<SecretKey, ManagerError> {
let b_pubkey = bitcoin::PublicKey {
compressed: true,
Expand All @@ -186,56 +240,24 @@ impl Signer for BitcoinCoreProvider {
Ok(pk.inner)
}

fn sign_psbt_input(
&self,
psbt: &mut PartiallySignedTransaction,
input_index: usize,
) -> Result<(), ManagerError> {
let outpoint = &psbt.unsigned_tx.input[input_index].previous_output;
let tx_out = if let Some(input) = psbt.inputs.get(input_index) {
if let Some(wit_utxo) = &input.witness_utxo {
Ok(wit_utxo.clone())
} else if let Some(in_tx) = &input.non_witness_utxo {
Ok(in_tx.output[outpoint.vout as usize].clone())
} else {
Err(ManagerError::InvalidParameters(
"No TxOut for PSBT input".to_string(),
))
}
} else {
Err(ManagerError::InvalidParameters(
"No TxOut for PSBT input".to_string(),
))
}?;

let redeem_script = psbt
.inputs
.get(input_index)
.and_then(|i| i.redeem_script.clone());

let input = json::SignRawTransactionInput {
txid: outpoint.txid,
vout: outpoint.vout,
script_pub_key: tx_out.script_pubkey.clone(),
redeem_script,
amount: Some(Amount::from_sat(tx_out.value)),
};

let sign_result = self
.client
fn get_new_secret_key(&self) -> Result<SecretKey, ManagerError> {
let sk = SecretKey::new(&mut thread_rng());
let network = self.get_network()?;
self.client
.lock()
.unwrap()
.sign_raw_transaction_with_wallet(&psbt.unsigned_tx, Some(&[input]), None)
.import_private_key(
&PrivateKey {
compressed: true,
network,
inner: sk,
},
None,
Some(false),
)
.map_err(rpc_err_to_manager_err)?;
let signed_tx = Transaction::consensus_decode(&mut sign_result.hex.as_slice())
.map_err(enc_err_to_manager_err)?;

psbt.inputs[input_index].final_script_sig =
Some(signed_tx.input[input_index].script_sig.clone());
psbt.inputs[input_index].final_script_witness =
Some(signed_tx.input[input_index].witness.clone());

Ok(())
Ok(sk)
}
}

Expand All @@ -249,27 +271,14 @@ impl Wallet for BitcoinCoreProvider {
}

fn get_new_change_address(&self) -> Result<Address, ManagerError> {
self.get_new_address()
}

fn get_new_secret_key(&self) -> Result<SecretKey, ManagerError> {
let sk = SecretKey::new(&mut thread_rng());
let network = self.get_network()?;
self.client
.lock()
.unwrap()
.import_private_key(
&PrivateKey {
compressed: true,
network,
inner: sk,
},
None,
Some(false),
.call(
"getrawchangeaddress",
&[Value::Null, opt_into_json(Some(AddressType::Bech32))?],
)
.map_err(rpc_err_to_manager_err)?;

Ok(sk)
.map_err(rpc_err_to_manager_err)
}

fn get_utxos_for_amount(
Expand Down Expand Up @@ -321,6 +330,58 @@ impl Wallet for BitcoinCoreProvider {
.import_address(address, None, Some(false))
.map_err(rpc_err_to_manager_err)
}

fn sign_psbt_input(
&self,
psbt: &mut PartiallySignedTransaction,
input_index: usize,
) -> Result<(), ManagerError> {
let outpoint = &psbt.unsigned_tx.input[input_index].previous_output;
let tx_out = if let Some(input) = psbt.inputs.get(input_index) {
if let Some(wit_utxo) = &input.witness_utxo {
Ok(wit_utxo.clone())
} else if let Some(in_tx) = &input.non_witness_utxo {
Ok(in_tx.output[outpoint.vout as usize].clone())
} else {
Err(ManagerError::InvalidParameters(
"No TxOut for PSBT input".to_string(),
))
}
} else {
Err(ManagerError::InvalidParameters(
"No TxOut for PSBT input".to_string(),
))
}?;

let redeem_script = psbt
.inputs
.get(input_index)
.and_then(|i| i.redeem_script.clone());

let input = json::SignRawTransactionInput {
txid: outpoint.txid,
vout: outpoint.vout,
script_pub_key: tx_out.script_pubkey.clone(),
redeem_script,
amount: Some(Amount::from_sat(tx_out.value)),
};

let sign_result = self
.client
.lock()
.unwrap()
.sign_raw_transaction_with_wallet(&psbt.unsigned_tx, Some(&[input]), None)
.map_err(rpc_err_to_manager_err)?;
let signed_tx = Transaction::consensus_decode(&mut sign_result.hex.as_slice())
.map_err(enc_err_to_manager_err)?;

psbt.inputs[input_index].final_script_sig =
Some(signed_tx.input[input_index].script_sig.clone());
psbt.inputs[input_index].final_script_witness =
Some(signed_tx.input[input_index].witness.clone());

Ok(())
}
}

impl Blockchain for BitcoinCoreProvider {
Expand Down Expand Up @@ -479,3 +540,22 @@ fn poll_for_fee_estimates(
std::thread::sleep(Duration::from_secs(60));
});
}

/// Shorthand for converting a variable into a serde_json::Value.
fn into_json<T>(val: T) -> bitcoincore_rpc::Result<Value>
where
T: serde::ser::Serialize,
{
Ok(serde_json::to_value(val)?)
}

/// Shorthand for converting an Option into an Option<serde_json::Value>.
fn opt_into_json<T>(opt: Option<T>) -> Result<Value, ManagerError>
where
T: serde::ser::Serialize,
{
match opt {
Some(val) => Ok(into_json(val).map_err(rpc_err_to_manager_err)?),
None => Ok(Value::Null),
}
}
4 changes: 3 additions & 1 deletion dlc-manager/src/channel/offered_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use secp256k1_zkp::PublicKey;

use crate::{
contract::offered_contract::OfferedContract, conversion_utils::get_tx_input_infos,
error::Error, ChannelId, ContractId,
error::Error, ChannelId, ContractId, KeysId,
};

use super::party_points::PartyBasePoints;
Expand Down Expand Up @@ -83,6 +83,7 @@ impl OfferedChannel {
pub fn from_offer_channel(
offer_channel: &OfferChannel,
counter_party: PublicKey,
keys_id: KeysId,
) -> Result<(OfferedChannel, OfferedContract), Error> {
let channel = OfferedChannel {
offered_contract_id: offer_channel.temporary_contract_id,
Expand Down Expand Up @@ -128,6 +129,7 @@ impl OfferedChannel {
.map(|x| x.into())
.collect(),
total_collateral: offer_channel.contract_info.get_total_collateral(),
keys_id,
};

Ok((channel, contract))
Expand Down
22 changes: 11 additions & 11 deletions dlc-manager/src/channel/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,18 @@ impl_dlc_writeable!(SignedChannel, {

impl_dlc_writeable_enum!(
SignedChannelState,;
(0, Established, {(signed_contract_id, writeable), (own_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (counter_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (buffer_transaction, writeable), (is_offer, writeable)}),
(1, SettledOffered, {(counter_payout, writeable), (next_per_update_point, writeable), (timeout, writeable)}),
(2, SettledReceived, {(own_payout, writeable), (counter_next_per_update_point, writeable)}),
(3, SettledAccepted, {(counter_next_per_update_point, writeable), (own_next_per_update_point, writeable), (settle_tx, writeable), (own_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable)}),
(4, SettledConfirmed, {(settle_tx, writeable), (counter_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (own_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (counter_next_per_update_point, writeable), (own_next_per_update_point, writeable), (timeout, writeable), (own_payout, writeable) }),
(5, Settled, {(settle_tx, writeable), (counter_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (own_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature})}),
(6, RenewOffered, {(offered_contract_id, writeable), (counter_payout, writeable), (is_offer, writeable), (offer_next_per_update_point, writeable), (timeout, writeable)}),
(7, RenewAccepted, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (accept_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable)}),
(8, RenewConfirmed, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (offer_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (accept_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable)}),
(9, Closing, {(buffer_transaction, writeable), (signed_cet, writeable), (contract_id, writeable), (attestations, vec)}),
(0, Established, {(signed_contract_id, writeable), (own_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (counter_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (buffer_transaction, writeable), (is_offer, writeable), (keys_id, writeable)}),
(1, SettledOffered, {(counter_payout, writeable), (next_per_update_point, writeable), (timeout, writeable), (keys_id, writeable)}),
(2, SettledReceived, {(own_payout, writeable), (counter_next_per_update_point, writeable), (keys_id, writeable)}),
(3, SettledAccepted, {(counter_next_per_update_point, writeable), (own_next_per_update_point, writeable), (settle_tx, writeable), (own_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (keys_id, writeable)}),
(4, SettledConfirmed, {(settle_tx, writeable), (counter_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (own_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (counter_next_per_update_point, writeable), (own_next_per_update_point, writeable), (timeout, writeable), (own_payout, writeable), (keys_id, writeable) }),
(5, Settled, {(settle_tx, writeable), (counter_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (own_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (keys_id, writeable)}),
(6, RenewOffered, {(offered_contract_id, writeable), (counter_payout, writeable), (is_offer, writeable), (offer_next_per_update_point, writeable), (timeout, writeable), (keys_id, writeable)}),
(7, RenewAccepted, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (accept_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (keys_id, writeable)}),
(8, RenewConfirmed, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (offer_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (accept_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (keys_id, writeable)}),
(9, Closing, {(buffer_transaction, writeable), (signed_cet, writeable), (contract_id, writeable), (attestations, vec), (keys_id, writeable)}),
(10, ClosedPunished, { (punishment_txid, writeable) }),
(11, CollaborativeCloseOffered, { (counter_payout, writeable), (offer_signature, writeable), (close_tx, writeable), (timeout, writeable) })
(11, CollaborativeCloseOffered, { (counter_payout, writeable), (offer_signature, writeable), (close_tx, writeable), (timeout, writeable), (keys_id, writeable) })
;;(12, Closed), (13, CounterClosed), (14, CollaborativelyClosed)
);

Expand Down
Loading
Loading