|
@@ -1,31 +1,29 @@
|
|
|
use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
|
|
use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
|
|
|
|
|
+use crate::rpc::{jsonrpc, websockets::WsStream};
|
|
|
use crate::serial::{deserialize, serialize, Decodable, Encodable};
|
|
use crate::serial::{deserialize, serialize, Decodable, Encodable};
|
|
|
-use crate::{Error, Result};
|
|
|
|
|
use crate::util::{generate_id, NetworkName};
|
|
use crate::util::{generate_id, NetworkName};
|
|
|
|
|
+use crate::{Error, Result};
|
|
|
use async_trait::async_trait;
|
|
use async_trait::async_trait;
|
|
|
use bitcoin::blockdata::{
|
|
use bitcoin::blockdata::{
|
|
|
- script::Script,
|
|
|
|
|
- transaction::{
|
|
|
|
|
- OutPoint,
|
|
|
|
|
- Transaction,
|
|
|
|
|
- TxIn,
|
|
|
|
|
- TxOut,
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ script::{Builder, Script},
|
|
|
|
|
+ transaction::{OutPoint, SigHashType, Transaction, TxIn, TxOut},
|
|
|
};
|
|
};
|
|
|
-use bitcoin::hash_types::{PubkeyHash as BtcPubKeyHash};
|
|
|
|
|
|
|
+use bitcoin::hash_types::PubkeyHash as BtcPubKeyHash;
|
|
|
use bitcoin::network::constants::Network;
|
|
use bitcoin::network::constants::Network;
|
|
|
use bitcoin::util::address::Address;
|
|
use bitcoin::util::address::Address;
|
|
|
use bitcoin::util::ecdsa::{PrivateKey as BtcPrivKey, PublicKey as BtcPubKey};
|
|
use bitcoin::util::ecdsa::{PrivateKey as BtcPrivKey, PublicKey as BtcPubKey};
|
|
|
use electrum_client::{Client as ElectrumClient, ElectrumApi, GetBalanceRes};
|
|
use electrum_client::{Client as ElectrumClient, ElectrumApi, GetBalanceRes};
|
|
|
|
|
+use futures::{SinkExt, StreamExt};
|
|
|
use log::*;
|
|
use log::*;
|
|
|
-use std::convert::From;
|
|
|
|
|
|
|
+use secp256k1::{
|
|
|
|
|
+ constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE},
|
|
|
|
|
+ key::{PublicKey, SecretKey},
|
|
|
|
|
+ {rand::rngs::OsRng, Secp256k1},
|
|
|
|
|
+ {All, Message as BtcMessage, /*Secp256k1,*/},
|
|
|
|
|
+};
|
|
|
use serde_json::json;
|
|
use serde_json::json;
|
|
|
-use crate::rpc::{jsonrpc, websockets::WsStream};
|
|
|
|
|
|
|
+use std::convert::From;
|
|
|
use tungstenite::Message;
|
|
use tungstenite::Message;
|
|
|
-use futures::{SinkExt, StreamExt};
|
|
|
|
|
-use secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE};
|
|
|
|
|
-use secp256k1::key::{PublicKey, SecretKey};
|
|
|
|
|
-use secp256k1::{rand::rngs::OsRng, Secp256k1};
|
|
|
|
|
|
|
|
|
|
use async_std::sync::Arc;
|
|
use async_std::sync::Arc;
|
|
|
use std::str::FromStr;
|
|
use std::str::FromStr;
|
|
@@ -37,10 +35,11 @@ pub type PrivKey = BtcPrivKey;
|
|
|
|
|
|
|
|
const KEYPAIR_LENGTH: usize = SECRET_KEY_SIZE + PUBLIC_KEY_SIZE;
|
|
const KEYPAIR_LENGTH: usize = SECRET_KEY_SIZE + PUBLIC_KEY_SIZE;
|
|
|
|
|
|
|
|
-#[derive(Copy, Clone)]
|
|
|
|
|
|
|
+#[derive(Clone)]
|
|
|
pub struct Keypair {
|
|
pub struct Keypair {
|
|
|
secret: SecretKey,
|
|
secret: SecretKey,
|
|
|
public: PublicKey,
|
|
public: PublicKey,
|
|
|
|
|
+ context: Secp256k1<All>,
|
|
|
}
|
|
}
|
|
|
impl Keypair {
|
|
impl Keypair {
|
|
|
pub fn new() -> Self {
|
|
pub fn new() -> Self {
|
|
@@ -48,7 +47,11 @@ impl Keypair {
|
|
|
let mut rng = OsRng::new().expect("OsRng");
|
|
let mut rng = OsRng::new().expect("OsRng");
|
|
|
|
|
|
|
|
let (secret, public) = secp.generate_keypair(&mut rng);
|
|
let (secret, public) = secp.generate_keypair(&mut rng);
|
|
|
- Self { secret, public }
|
|
|
|
|
|
|
+ Self {
|
|
|
|
|
+ secret,
|
|
|
|
|
+ public,
|
|
|
|
|
+ context: secp,
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
|
|
pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
|
|
@@ -63,11 +66,17 @@ impl Keypair {
|
|
|
if bytes.len() != KEYPAIR_LENGTH {
|
|
if bytes.len() != KEYPAIR_LENGTH {
|
|
|
return Err(Error::BtcFailed("Not right size".to_string()));
|
|
return Err(Error::BtcFailed("Not right size".to_string()));
|
|
|
}
|
|
}
|
|
|
- //TODO: Map to errors properly
|
|
|
|
|
|
|
+ let secp = Secp256k1::new();
|
|
|
|
|
+
|
|
|
|
|
+ //TODO: Map to errors properly, use context for public gen
|
|
|
let secret = SecretKey::from_slice(&bytes[..SECRET_KEY_SIZE]).unwrap();
|
|
let secret = SecretKey::from_slice(&bytes[..SECRET_KEY_SIZE]).unwrap();
|
|
|
let public = PublicKey::from_slice(&bytes[SECRET_KEY_SIZE..]).unwrap();
|
|
let public = PublicKey::from_slice(&bytes[SECRET_KEY_SIZE..]).unwrap();
|
|
|
|
|
|
|
|
- Ok(Keypair { secret, public })
|
|
|
|
|
|
|
+ Ok(Keypair {
|
|
|
|
|
+ secret,
|
|
|
|
|
+ public,
|
|
|
|
|
+ context: secp,
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
fn secret(&self) -> SecretKey {
|
|
fn secret(&self) -> SecretKey {
|
|
|
self.secret
|
|
self.secret
|
|
@@ -85,7 +94,7 @@ impl Default for Keypair {
|
|
|
Self::new()
|
|
Self::new()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+#[derive(Clone)]
|
|
|
pub struct BtcKeys {
|
|
pub struct BtcKeys {
|
|
|
keypair: Arc<Keypair>,
|
|
keypair: Arc<Keypair>,
|
|
|
btc_privkey: BtcPrivKey,
|
|
btc_privkey: BtcPrivKey,
|
|
@@ -96,18 +105,14 @@ pub struct BtcKeys {
|
|
|
|
|
|
|
|
impl BtcKeys {
|
|
impl BtcKeys {
|
|
|
pub fn new(keypair: &Keypair, network: Network) -> Self {
|
|
pub fn new(keypair: &Keypair, network: Network) -> Self {
|
|
|
-
|
|
|
|
|
let (secret_key, public_key) = keypair.as_tuple();
|
|
let (secret_key, public_key) = keypair.as_tuple();
|
|
|
|
|
|
|
|
let btc_privkey = BtcPrivKey::new(secret_key, network);
|
|
let btc_privkey = BtcPrivKey::new(secret_key, network);
|
|
|
let btc_pubkey = BtcPubKey::new(public_key);
|
|
let btc_pubkey = BtcPubKey::new(public_key);
|
|
|
- let script_pubkey = BtcKeys::derive_btc_script_pubkey(
|
|
|
|
|
- public_key,
|
|
|
|
|
- network
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ let script_pubkey = BtcKeys::derive_btc_script_pubkey(public_key, network);
|
|
|
|
|
|
|
|
Self {
|
|
Self {
|
|
|
- keypair: Arc::new(*keypair),
|
|
|
|
|
|
|
+ keypair: Arc::new(keypair.clone()),
|
|
|
btc_privkey,
|
|
btc_privkey,
|
|
|
btc_pubkey,
|
|
btc_pubkey,
|
|
|
script_pubkey,
|
|
script_pubkey,
|
|
@@ -129,10 +134,7 @@ impl BtcKeys {
|
|
|
pub fn btc_pubkey_hash(&self) -> BtcPubKeyHash {
|
|
pub fn btc_pubkey_hash(&self) -> BtcPubKeyHash {
|
|
|
self.btc_pubkey.pubkey_hash()
|
|
self.btc_pubkey.pubkey_hash()
|
|
|
}
|
|
}
|
|
|
- pub fn derive_btc_script_pubkey(
|
|
|
|
|
- pubkey: PublicKey,
|
|
|
|
|
- network: Network
|
|
|
|
|
- ) -> Script {
|
|
|
|
|
|
|
+ pub fn derive_btc_script_pubkey(pubkey: PublicKey, network: Network) -> Script {
|
|
|
let btc_pubkey = BtcPubKey::new(pubkey);
|
|
let btc_pubkey = BtcPubKey::new(pubkey);
|
|
|
let address = Address::p2pkh(&btc_pubkey, network);
|
|
let address = Address::p2pkh(&btc_pubkey, network);
|
|
|
address.script_pubkey()
|
|
address.script_pubkey()
|
|
@@ -149,14 +151,13 @@ impl BtcKeys {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub struct BtcClient {
|
|
pub struct BtcClient {
|
|
|
- main_keypair: Keypair,
|
|
|
|
|
|
|
+ main_account: BtcKeys,
|
|
|
notify_channel: (
|
|
notify_channel: (
|
|
|
async_channel::Sender<TokenNotification>,
|
|
async_channel::Sender<TokenNotification>,
|
|
|
async_channel::Receiver<TokenNotification>,
|
|
async_channel::Receiver<TokenNotification>,
|
|
|
),
|
|
),
|
|
|
client: Arc<ElectrumClient>,
|
|
client: Arc<ElectrumClient>,
|
|
|
network: Network,
|
|
network: Network,
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl BtcClient {
|
|
impl BtcClient {
|
|
@@ -171,11 +172,13 @@ impl BtcClient {
|
|
|
_ => return Err(Error::NotSupportedNetwork),
|
|
_ => return Err(Error::NotSupportedNetwork),
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ let main_account = BtcKeys::new(&main_keypair, network);
|
|
|
|
|
+
|
|
|
let electrum_client = ElectrumClient::new(&url)
|
|
let electrum_client = ElectrumClient::new(&url)
|
|
|
.map_err(|err| crate::Error::from(super::BtcFailed::from(err)))?;
|
|
.map_err(|err| crate::Error::from(super::BtcFailed::from(err)))?;
|
|
|
|
|
|
|
|
Ok(Arc::new(Self {
|
|
Ok(Arc::new(Self {
|
|
|
- main_keypair,
|
|
|
|
|
|
|
+ main_account,
|
|
|
notify_channel,
|
|
notify_channel,
|
|
|
client: Arc::new(electrum_client),
|
|
client: Arc::new(electrum_client),
|
|
|
network,
|
|
network,
|
|
@@ -193,8 +196,9 @@ impl BtcClient {
|
|
|
);
|
|
);
|
|
|
let client = &self.client;
|
|
let client = &self.client;
|
|
|
|
|
|
|
|
|
|
+ let keys_clone = btc_keys.clone();
|
|
|
// p2pkh script
|
|
// p2pkh script
|
|
|
- let script = btc_keys.script_pubkey;
|
|
|
|
|
|
|
+ let script = keys_clone.script_pubkey;
|
|
|
|
|
|
|
|
//Subscribe to script, returns Error::AlreadySubscribed otherwise
|
|
//Subscribe to script, returns Error::AlreadySubscribed otherwise
|
|
|
let status = client.script_subscribe(&script)?;
|
|
let status = client.script_subscribe(&script)?;
|
|
@@ -208,7 +212,7 @@ impl BtcClient {
|
|
|
let current_status = client.script_pop(&script)?;
|
|
let current_status = client.script_pop(&script)?;
|
|
|
if current_status != status {
|
|
if current_status != status {
|
|
|
debug!(target: "BTC CLIENT", "ScriptPubKey status has not changed");
|
|
debug!(target: "BTC CLIENT", "ScriptPubKey status has not changed");
|
|
|
- continue
|
|
|
|
|
|
|
+ continue;
|
|
|
}
|
|
}
|
|
|
match current_status {
|
|
match current_status {
|
|
|
// Script has a notification update
|
|
// Script has a notification update
|
|
@@ -248,7 +252,8 @@ impl BtcClient {
|
|
|
.map_err(Error::from)?;
|
|
.map_err(Error::from)?;
|
|
|
|
|
|
|
|
debug!(target: "BTC BRIDGE", "Received {} btc", ui_amnt);
|
|
debug!(target: "BTC BRIDGE", "Received {} btc", ui_amnt);
|
|
|
- let _ = self.send_btc_to_main_wallet(amnt, script)?;
|
|
|
|
|
|
|
+ let _ = self.send_btc_to_main_wallet(amnt, btc_keys)?;
|
|
|
|
|
+
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -261,10 +266,10 @@ impl BtcClient {
|
|
|
{
|
|
{
|
|
|
let client = &self.client;
|
|
let client = &self.client;
|
|
|
|
|
|
|
|
- let script_pubkey =
|
|
|
|
|
- BtcKeys::derive_btc_script_pubkey(*pubkey, self.network);
|
|
|
|
|
|
|
+ let script_pubkey = BtcKeys::derive_btc_script_pubkey(*pubkey, self.network);
|
|
|
|
|
+
|
|
|
|
|
+ let _ = client.script_unsubscribe(&script_pubkey).unwrap();
|
|
|
|
|
|
|
|
- client.script_unsubscribe(&script_pubkey);
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let unsubscription = jsonrpc::request(json!("accountUnsubscribe"), json!([sub_id]));
|
|
let unsubscription = jsonrpc::request(json!("accountUnsubscribe"), json!([sub_id]));
|
|
@@ -275,16 +280,12 @@ impl BtcClient {
|
|
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
- // fn create_transaction(self: Arc<Self>, script: Script) -> Transaction {
|
|
|
|
|
|
|
|
|
|
- // }
|
|
|
|
|
- fn send_btc_to_main_wallet(
|
|
|
|
|
- self: Arc<Self>,
|
|
|
|
|
- amount: u64,
|
|
|
|
|
- script: Script,
|
|
|
|
|
- ) -> BtcResult<()> {
|
|
|
|
|
|
|
+ fn send_btc_to_main_wallet(self: Arc<Self>, amount: u64, btc_keys: BtcKeys) -> BtcResult<()> {
|
|
|
debug!(target: "BTC BRIDGE", "Sending {} BTC to main wallet", amount);
|
|
debug!(target: "BTC BRIDGE", "Sending {} BTC to main wallet", amount);
|
|
|
let client = &self.client;
|
|
let client = &self.client;
|
|
|
|
|
+ let keys_clone = btc_keys.clone();
|
|
|
|
|
+ let script = keys_clone.script_pubkey;
|
|
|
let utxo = client.script_list_unspent(&script)?;
|
|
let utxo = client.script_list_unspent(&script)?;
|
|
|
|
|
|
|
|
let mut inputs = Vec::new();
|
|
let mut inputs = Vec::new();
|
|
@@ -302,25 +303,38 @@ impl BtcClient {
|
|
|
inputs.push(tx_in);
|
|
inputs.push(tx_in);
|
|
|
amounts += tx.value;
|
|
amounts += tx.value;
|
|
|
}
|
|
}
|
|
|
- let main_script_pubkey = BtcKeys::derive_btc_script_pubkey(
|
|
|
|
|
- self.main_keypair.pubkey(),
|
|
|
|
|
- self.network
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ let main_script_pubkey = self.main_account.script_pubkey.clone();
|
|
|
|
|
+
|
|
|
|
|
+ //Estimate fee for getting in 2 blocks ahead
|
|
|
|
|
+ let estimated_fee = client.estimate_fee(2)?;
|
|
|
|
|
+
|
|
|
|
|
+ //TODO: Better handling of fees, don't cast to u64
|
|
|
|
|
+ let value = amounts - estimated_fee as u64;
|
|
|
|
|
|
|
|
let transaction = Transaction {
|
|
let transaction = Transaction {
|
|
|
input: inputs,
|
|
input: inputs,
|
|
|
output: vec![TxOut {
|
|
output: vec![TxOut {
|
|
|
script_pubkey: main_script_pubkey,
|
|
script_pubkey: main_script_pubkey,
|
|
|
- value: amounts,
|
|
|
|
|
|
|
+ value: value,
|
|
|
}],
|
|
}],
|
|
|
lock_time: 0,
|
|
lock_time: 0,
|
|
|
version: 2,
|
|
version: 2,
|
|
|
};
|
|
};
|
|
|
- //TODO: Sign and send transaction
|
|
|
|
|
- debug!(target: "BTC BRIDGE", "Sent {} BTC to main wallet:", amount,/* signature*/);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let _signed_tx = sign_transaction(
|
|
|
|
|
+ transaction,
|
|
|
|
|
+ script,
|
|
|
|
|
+ btc_keys.keypair.secret,
|
|
|
|
|
+ btc_keys.btc_pubkey,
|
|
|
|
|
+ &btc_keys.keypair.context,
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ //TODO: Serialize and send tx
|
|
|
|
|
+ //let txid = client.transaction_broadcast_raw(serialize(&signed_tx))?;
|
|
|
|
|
+
|
|
|
|
|
+ debug!(target: "BTC BRIDGE", "Sent {} BTC to main wallet:", amount, /*txid*/);
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
|
#[async_trait]
|
|
@@ -339,19 +353,17 @@ impl NetworkClient for BtcClient {
|
|
|
// start scheduler for checking balance
|
|
// start scheduler for checking balance
|
|
|
debug!(target: "BRIDGE BITCOIN", "Subscribing for deposit");
|
|
debug!(target: "BRIDGE BITCOIN", "Subscribing for deposit");
|
|
|
|
|
|
|
|
- smol::spawn( async move {
|
|
|
|
|
- let result = self
|
|
|
|
|
- .handle_subscribe_request(btc_keys, drk_pub_key)
|
|
|
|
|
- .await;
|
|
|
|
|
|
|
+ smol::spawn(async move {
|
|
|
|
|
+ let result = self.handle_subscribe_request(btc_keys, drk_pub_key).await;
|
|
|
if let Err(e) = result {
|
|
if let Err(e) = result {
|
|
|
error!(target: "BTC BRIDGE SUBSCRIPTION","{}", e.to_string());
|
|
error!(target: "BTC BRIDGE SUBSCRIPTION","{}", e.to_string());
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
- .detach();
|
|
|
|
|
|
|
+ .detach();
|
|
|
|
|
|
|
|
Ok(TokenSubscribtion {
|
|
Ok(TokenSubscribtion {
|
|
|
secret_key,
|
|
secret_key,
|
|
|
- public_key
|
|
|
|
|
|
|
+ public_key,
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -367,9 +379,7 @@ impl NetworkClient for BtcClient {
|
|
|
let public_key = keypair.pubkey().to_string();
|
|
let public_key = keypair.pubkey().to_string();
|
|
|
|
|
|
|
|
smol::spawn(async move {
|
|
smol::spawn(async move {
|
|
|
- let result = self
|
|
|
|
|
- .handle_subscribe_request(btc_keys, drk_pub_key)
|
|
|
|
|
- .await;
|
|
|
|
|
|
|
+ let result = self.handle_subscribe_request(btc_keys, drk_pub_key).await;
|
|
|
if let Err(e) = result {
|
|
if let Err(e) = result {
|
|
|
error!(target: "BTC BRIDGE SUBSCRIPTION","{}", e.to_string());
|
|
error!(target: "BTC BRIDGE SUBSCRIPTION","{}", e.to_string());
|
|
|
}
|
|
}
|
|
@@ -385,46 +395,84 @@ impl NetworkClient for BtcClient {
|
|
|
// address is not a btc address, so derive the btc address
|
|
// address is not a btc address, so derive the btc address
|
|
|
let client = &self.client;
|
|
let client = &self.client;
|
|
|
let public_key = deserialize(&address)?;
|
|
let public_key = deserialize(&address)?;
|
|
|
|
|
+ let script_pubkey = BtcKeys::derive_btc_script_pubkey(public_key, self.network);
|
|
|
|
|
|
|
|
- let script_pubkey = BtcKeys::derive_btc_script_pubkey(
|
|
|
|
|
- public_key,
|
|
|
|
|
- self.network
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- let main_script_pubkey = BtcKeys::derive_btc_script_pubkey(
|
|
|
|
|
- self.main_keypair.pubkey(),
|
|
|
|
|
- self.network
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ let main_script_pubkey = &self.main_account.script_pubkey;
|
|
|
|
|
|
|
|
//TODO: Map to errors properly
|
|
//TODO: Map to errors properly
|
|
|
let main_utxo = client.script_list_unspent(&main_script_pubkey).unwrap();
|
|
let main_utxo = client.script_list_unspent(&main_script_pubkey).unwrap();
|
|
|
|
|
|
|
|
let transaction = Transaction {
|
|
let transaction = Transaction {
|
|
|
- input: vec![
|
|
|
|
|
- TxIn {
|
|
|
|
|
- previous_output: OutPoint {
|
|
|
|
|
|
|
+ input: vec![TxIn {
|
|
|
|
|
+ previous_output: OutPoint {
|
|
|
txid: main_utxo[0].tx_hash,
|
|
txid: main_utxo[0].tx_hash,
|
|
|
vout: main_utxo[0].tx_pos as u32,
|
|
vout: main_utxo[0].tx_pos as u32,
|
|
|
},
|
|
},
|
|
|
sequence: 0xffffffff,
|
|
sequence: 0xffffffff,
|
|
|
witness: Vec::new(),
|
|
witness: Vec::new(),
|
|
|
script_sig: Script::new(),
|
|
script_sig: Script::new(),
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- output: vec![
|
|
|
|
|
- TxOut {
|
|
|
|
|
- script_pubkey: script_pubkey,
|
|
|
|
|
- value: amount,
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
|
|
+ }],
|
|
|
|
|
+ output: vec![TxOut {
|
|
|
|
|
+ script_pubkey: script_pubkey.clone(),
|
|
|
|
|
+ value: amount,
|
|
|
|
|
+ }],
|
|
|
lock_time: 0,
|
|
lock_time: 0,
|
|
|
version: 2,
|
|
version: 2,
|
|
|
};
|
|
};
|
|
|
- //TODO: Sign and send tx
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let _signed_tx = sign_transaction(
|
|
|
|
|
+ transaction,
|
|
|
|
|
+ script_pubkey,
|
|
|
|
|
+ self.main_account.keypair.secret,
|
|
|
|
|
+ self.main_account.btc_pubkey,
|
|
|
|
|
+ &self.main_account.keypair.context,
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ //TODO: Serialize and send tx
|
|
|
|
|
+ //let txid = client.transaction_broadcast_raw(serialize(&signed_tx))?;
|
|
|
|
|
+
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+pub fn sign_transaction(
|
|
|
|
|
+ tx: Transaction,
|
|
|
|
|
+ script_pubkey: Script,
|
|
|
|
|
+ priv_key: SecretKey,
|
|
|
|
|
+ pub_key: BtcPubKey,
|
|
|
|
|
+ curve: &Secp256k1<All>,
|
|
|
|
|
+) -> Transaction {
|
|
|
|
|
+ let mut signed_inputs: Vec<TxIn> = Vec::new();
|
|
|
|
|
+
|
|
|
|
|
+ for (i, unsigned_input) in tx.input.iter().enumerate() {
|
|
|
|
|
+ let sighash = tx.signature_hash(i, &script_pubkey, SigHashType::All as u32);
|
|
|
|
|
+ //TODO: replace unwrap
|
|
|
|
|
+ let msg = BtcMessage::from_slice(&sighash.as_ref()).unwrap();
|
|
|
|
|
+
|
|
|
|
|
+ let signature = curve.sign(&msg, &priv_key);
|
|
|
|
|
+ let byte_signature = &signature.serialize_der();
|
|
|
|
|
+ let mut with_hashtype = byte_signature.to_vec();
|
|
|
|
|
+ with_hashtype.push(SigHashType::All as u8);
|
|
|
|
|
+
|
|
|
|
|
+ let redeem_script = Builder::new()
|
|
|
|
|
+ .push_slice(with_hashtype.as_slice())
|
|
|
|
|
+ .push_key(&pub_key)
|
|
|
|
|
+ .into_script();
|
|
|
|
|
+ signed_inputs.push(TxIn {
|
|
|
|
|
+ previous_output: unsigned_input.previous_output,
|
|
|
|
|
+ script_sig: redeem_script,
|
|
|
|
|
+ sequence: unsigned_input.sequence,
|
|
|
|
|
+ witness: unsigned_input.witness.clone(),
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Transaction {
|
|
|
|
|
+ version: tx.version,
|
|
|
|
|
+ lock_time: tx.lock_time,
|
|
|
|
|
+ input: signed_inputs,
|
|
|
|
|
+ output: tx.output,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
impl Encodable for bitcoin::Address {
|
|
impl Encodable for bitcoin::Address {
|
|
|
fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
|
|
fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
|
|
|
let addr = self.to_string();
|
|
let addr = self.to_string();
|