Simplify zmq & one track for all messaging
This commit is contained in:
parent
459756815f
commit
4d3dc8123a
653
Cargo.lock
generated
653
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,6 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
bitcoincore-rpc = { version = "0.18" }
|
bitcoincore-rpc = { version = "0.18" }
|
||||||
bitcoincore-zmq = "1.4.0"
|
|
||||||
electrum-client = { git = "https://github.com/cygnet3/rust-electrum-client", branch = "sp_tweaks" }
|
electrum-client = { git = "https://github.com/cygnet3/rust-electrum-client", branch = "sp_tweaks" }
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
futures-util = { version = "0.3.28", default-features = false, features = ["sink", "std"] }
|
futures-util = { version = "0.3.28", default-features = false, features = ["sink", "std"] }
|
||||||
@ -20,3 +19,4 @@ sp_backend = { git = "https://github.com/Sosthene00/sp-backend", branch = "sp_cl
|
|||||||
tokio = { version = "1.0.0", features = ["io-util", "rt-multi-thread", "macros", "sync"] }
|
tokio = { version = "1.0.0", features = ["io-util", "rt-multi-thread", "macros", "sync"] }
|
||||||
tokio-stream = "0.1"
|
tokio-stream = "0.1"
|
||||||
tokio-tungstenite = "0.21.0"
|
tokio-tungstenite = "0.21.0"
|
||||||
|
zeromq = "0.3.5"
|
||||||
|
244
src/main.rs
244
src/main.rs
@ -1,28 +1,22 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
any::Any, collections::HashMap, env, fmt::Debug, net::SocketAddr, str::FromStr, sync::{Arc, Mutex, MutexGuard}
|
||||||
env,
|
|
||||||
fmt::Debug,
|
|
||||||
net::SocketAddr,
|
|
||||||
ops::Deref,
|
|
||||||
str::FromStr,
|
|
||||||
sync::{Arc, Mutex, MutexGuard},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use bitcoincore_rpc::json::{self as bitcoin_json};
|
use bitcoincore_rpc::json::{self as bitcoin_json};
|
||||||
use futures_util::{future, pin_mut, stream::TryStreamExt, FutureExt, StreamExt};
|
use futures_util::{future, pin_mut, stream::TryStreamExt, FutureExt, StreamExt};
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
|
use sdk_common::network::{AnkFlag, AnkNetworkMsg, FaucetMessage, NewTxMessage};
|
||||||
use sp_backend::bitcoin::{
|
use sp_backend::bitcoin::{
|
||||||
absolute::LockTime,
|
absolute::LockTime,
|
||||||
consensus::deserialize,
|
consensus::{deserialize, serialize},
|
||||||
hex::DisplayHex,
|
hex::DisplayHex,
|
||||||
key::TapTweak,
|
key::TapTweak,
|
||||||
sighash::{Prevouts, SighashCache},
|
sighash::{Prevouts, SighashCache},
|
||||||
taproot::Signature,
|
taproot::Signature,
|
||||||
transaction::Version,
|
transaction::Version,
|
||||||
Amount, OutPoint, Psbt, ScriptBuf, TapSighashType, Transaction, TxIn, TxOut, Txid, Witness,
|
Amount, OutPoint, Psbt, ScriptBuf, TapSighashType, Transaction, TxIn, TxOut, Witness,
|
||||||
XOnlyPublicKey,
|
XOnlyPublicKey,
|
||||||
};
|
};
|
||||||
use sp_backend::spclient::OutputList;
|
|
||||||
use sp_backend::{
|
use sp_backend::{
|
||||||
bitcoin::secp256k1::{
|
bitcoin::secp256k1::{
|
||||||
rand::{thread_rng, Rng},
|
rand::{thread_rng, Rng},
|
||||||
@ -48,6 +42,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
|
|||||||
use tokio_tungstenite::tungstenite::Message;
|
use tokio_tungstenite::tungstenite::Message;
|
||||||
|
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
|
use zeromq::{Socket, SocketRecv};
|
||||||
|
|
||||||
mod daemon;
|
mod daemon;
|
||||||
mod electrumclient;
|
mod electrumclient;
|
||||||
@ -97,8 +92,18 @@ enum BroadcastType {
|
|||||||
ToAll,
|
ToAll,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn broadcast_message(peers: PeerMap, msg: Message, broadcast: BroadcastType) -> Result<()> {
|
fn broadcast_message(
|
||||||
|
peers: PeerMap,
|
||||||
|
flag: AnkFlag,
|
||||||
|
payload: String,
|
||||||
|
broadcast: BroadcastType,
|
||||||
|
) -> Result<()> {
|
||||||
// log::debug!("Broadcasting message: {}", msg);
|
// log::debug!("Broadcasting message: {}", msg);
|
||||||
|
let ank_msg = AnkNetworkMsg {
|
||||||
|
flag,
|
||||||
|
content: payload,
|
||||||
|
};
|
||||||
|
let msg = Message::Text(serde_json::to_string(&ank_msg)?);
|
||||||
match broadcast {
|
match broadcast {
|
||||||
BroadcastType::Sender(addr) => {
|
BroadcastType::Sender(addr) => {
|
||||||
peers
|
peers
|
||||||
@ -505,41 +510,55 @@ async fn handle_connection(
|
|||||||
let broadcast_incoming = incoming.try_for_each({
|
let broadcast_incoming = incoming.try_for_each({
|
||||||
let peers = peers.clone();
|
let peers = peers.clone();
|
||||||
move |msg| {
|
move |msg| {
|
||||||
if msg.is_text() {
|
if let Ok(raw_msg) = msg.to_text() {
|
||||||
if msg.to_string().starts_with("faucet") {
|
debug!("Received msg: {}", raw_msg);
|
||||||
match handle_faucet_request(
|
let parsed = serde_json::from_str::<AnkNetworkMsg>(raw_msg);
|
||||||
&msg.to_string(),
|
match parsed {
|
||||||
sp_wallet.clone(),
|
Ok(ank_msg) => match ank_msg.flag {
|
||||||
shared_daemon.clone(),
|
AnkFlag::Faucet => {
|
||||||
) {
|
debug!("Received a faucet message");
|
||||||
Ok(txid) => {
|
if let Ok(content) =
|
||||||
if let Err(e) = broadcast_message(
|
serde_json::from_str::<FaucetMessage>(&ank_msg.content)
|
||||||
peers.clone(),
|
{
|
||||||
Message::Text(format!("faucet{}", txid.to_string())),
|
match handle_faucet_request(
|
||||||
BroadcastType::Sender(addr),
|
&content.sp_address,
|
||||||
) {
|
sp_wallet.clone(),
|
||||||
log::error!("Failed to broadcast message: {}", e.to_string());
|
shared_daemon.clone(),
|
||||||
|
) {
|
||||||
|
Ok(new_tx_msg) => {
|
||||||
|
if let Err(e) = broadcast_message(
|
||||||
|
peers.clone(),
|
||||||
|
AnkFlag::NewTx,
|
||||||
|
serde_json::to_string(&new_tx_msg)
|
||||||
|
.expect("This should not fail"),
|
||||||
|
BroadcastType::Sender(addr),
|
||||||
|
) {
|
||||||
|
log::error!(
|
||||||
|
"Failed to broadcast message: {}",
|
||||||
|
e.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if let Err(e) = broadcast_message(
|
||||||
|
peers.clone(),
|
||||||
|
AnkFlag::Error,
|
||||||
|
e.to_string(),
|
||||||
|
BroadcastType::Sender(addr),
|
||||||
|
) {
|
||||||
|
log::error!("Failed to broadcast message: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log::debug!("Successfully broadcasted message: {}", txid);
|
log::error!("Invalid content for faucet message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
AnkFlag::NewTx => unimplemented!(),
|
||||||
if let Err(e) = broadcast_message(
|
AnkFlag::Error => unimplemented!(),
|
||||||
peers.clone(),
|
AnkFlag::Unknown => unimplemented!(),
|
||||||
Message::Text(e.to_string()),
|
},
|
||||||
BroadcastType::Sender(addr),
|
Err(_) => log::error!("Failed to parse network message"),
|
||||||
) {
|
|
||||||
log::error!("Failed to broadcast message: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// we just send it `as is` to everyone except sender
|
|
||||||
if let Err(e) =
|
|
||||||
broadcast_message(peers.clone(), msg, BroadcastType::ExcludeSender(addr))
|
|
||||||
{
|
|
||||||
log::error!("Failed to broadcast message: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// we don't care
|
// we don't care
|
||||||
@ -565,26 +584,10 @@ async fn handle_connection(
|
|||||||
peers.lock().unwrap().remove(&addr);
|
peers.lock().unwrap().remove(&addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flatten_msg(parts: &[Vec<u8>]) -> Vec<u8> {
|
fn compute_partial_tweak_to_transaction(
|
||||||
let total_len = parts.iter().fold(0, |acc, v| acc + v.len());
|
tx: Transaction,
|
||||||
let mut final_vec = Vec::with_capacity(total_len);
|
|
||||||
for p in parts {
|
|
||||||
final_vec.extend(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
final_vec
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_raw_tx_message(
|
|
||||||
core_msg: &bitcoincore_zmq::Message,
|
|
||||||
daemon: Arc<Mutex<Daemon>>,
|
daemon: Arc<Mutex<Daemon>>,
|
||||||
) -> Result<Vec<u8>> {
|
) -> Result<PublicKey> {
|
||||||
let tx: Transaction = deserialize(&core_msg.serialize_data_to_vec())?;
|
|
||||||
|
|
||||||
if tx.is_coinbase() {
|
|
||||||
return Err(Error::msg("Can't process coinbase transaction"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut outpoints: Vec<(String, u32)> = Vec::with_capacity(tx.input.len());
|
let mut outpoints: Vec<(String, u32)> = Vec::with_capacity(tx.input.len());
|
||||||
let mut pubkeys: Vec<PublicKey> = Vec::with_capacity(tx.input.len());
|
let mut pubkeys: Vec<PublicKey> = Vec::with_capacity(tx.input.len());
|
||||||
for input in tx.input {
|
for input in tx.input {
|
||||||
@ -619,64 +622,73 @@ fn process_raw_tx_message(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let input_pub_keys: Vec<&PublicKey> = pubkeys.iter().collect();
|
let input_pub_keys: Vec<&PublicKey> = pubkeys.iter().collect();
|
||||||
match calculate_tweak_data(&input_pub_keys, &outpoints) {
|
let partial_tweak = calculate_tweak_data(&input_pub_keys, &outpoints)?;
|
||||||
Ok(partial_tweak) => {
|
Ok(partial_tweak)
|
||||||
let mut vecs = core_msg.serialize_to_vecs().to_vec();
|
|
||||||
vecs.push(partial_tweak.serialize().to_vec());
|
|
||||||
Ok(flatten_msg(&vecs))
|
|
||||||
}
|
|
||||||
Err(e) => Err(Error::msg(format!(
|
|
||||||
"Failed to compute tweak data: {}",
|
|
||||||
e.to_string()
|
|
||||||
))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_zmq(
|
fn create_new_tx_message(transaction: Vec<u8>, daemon: Arc<Mutex<Daemon>>) -> Result<NewTxMessage> {
|
||||||
peers: PeerMap,
|
let tx: Transaction = deserialize(&transaction)?;
|
||||||
shared_daemon: SharedDaemon,
|
|
||||||
sp_wallet: Arc<SilentPaymentWallet>,
|
|
||||||
) {
|
|
||||||
tokio::task::spawn_blocking(move || {
|
|
||||||
debug!("Starting listening on Core");
|
|
||||||
for msg in bitcoincore_zmq::subscribe_receiver(&["tcp://127.0.0.1:29000"]).unwrap() {
|
|
||||||
let core_msg = match msg {
|
|
||||||
Ok(core_msg) => core_msg,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Error receiving ZMQ message: {}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
debug!("Received a {} message", core_msg.topic_str());
|
|
||||||
|
|
||||||
let payload: Vec<u8> = match core_msg.topic_str() {
|
if tx.is_coinbase() {
|
||||||
"rawtx" => {
|
return Err(Error::msg("Can't process coinbase transaction"));
|
||||||
let processed = process_raw_tx_message(&core_msg, shared_daemon.clone());
|
}
|
||||||
match processed {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(_) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"rawblock" => {
|
|
||||||
// scan the block for our outputs
|
|
||||||
if let Err(e) = scan_blocks(shared_daemon.clone(), sp_wallet.clone(), 1) {
|
|
||||||
log::error!("{}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
flatten_msg(&core_msg.serialize_to_vecs())
|
let partial_tweak = compute_partial_tweak_to_transaction(tx, daemon)?;
|
||||||
}
|
Ok(NewTxMessage::new(
|
||||||
_ => flatten_msg(&core_msg.serialize_to_vecs()),
|
transaction.to_lower_hex_string(),
|
||||||
};
|
Some(partial_tweak.to_string()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(e) = broadcast_message(
|
async fn handle_zmq(peers: PeerMap, shared_daemon: SharedDaemon) {
|
||||||
peers.clone(),
|
debug!("Starting listening on Core");
|
||||||
Message::Binary(payload),
|
let mut socket = zeromq::SubSocket::new();
|
||||||
BroadcastType::ToAll,
|
socket.connect("tcp://127.0.0.1:29100").await.unwrap();
|
||||||
) {
|
socket.subscribe("rawtx").await.unwrap();
|
||||||
log::error!("{}", e.to_string());
|
// socket.subscribe("hashblock");
|
||||||
|
debug!("{:?}", socket.type_id());
|
||||||
|
loop {
|
||||||
|
let core_msg = match socket.recv().await {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Zmq error: {}", e);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
debug!("Received a message");
|
||||||
|
|
||||||
|
let payload: String =
|
||||||
|
if let (Some(topic), Some(data)) = (core_msg.get(0), core_msg.get(1)) {
|
||||||
|
match std::str::from_utf8(&topic) {
|
||||||
|
Ok("rawtx") => {
|
||||||
|
match create_new_tx_message(data.to_vec(), shared_daemon.clone()) {
|
||||||
|
Ok(m) => serde_json::to_string(&m).expect("This shouldn't fail"),
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok("hashblock") => todo!(),
|
||||||
|
_ => {
|
||||||
|
error!("Unexpected message in zmq");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("Empty message");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = broadcast_message(
|
||||||
|
peers.clone(),
|
||||||
|
AnkFlag::NewTx,
|
||||||
|
payload,
|
||||||
|
BroadcastType::ToAll,
|
||||||
|
) {
|
||||||
|
log::error!("{}", e.to_string());
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread")]
|
#[tokio::main(flavor = "multi_thread")]
|
||||||
@ -770,11 +782,7 @@ async fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe to Bitcoin Core
|
// Subscribe to Bitcoin Core
|
||||||
tokio::spawn(handle_zmq(
|
tokio::spawn(handle_zmq(peers.clone(), shared_daemon.clone()));
|
||||||
peers.clone(),
|
|
||||||
shared_daemon.clone(),
|
|
||||||
sp_wallet.clone(),
|
|
||||||
));
|
|
||||||
|
|
||||||
// Create the event loop and TCP listener we'll accept connections on.
|
// Create the event loop and TCP listener we'll accept connections on.
|
||||||
let try_socket = TcpListener::bind(&addr).await;
|
let try_socket = TcpListener::bind(&addr).await;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user