1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use crate::block::BlockType;
use crate::blockchain::Blockchain;
use crate::consensus::SaitoMessage;
use crate::mempool::Mempool;
use crate::network::Result;
use crate::transaction::Transaction;
use crate::wallet::Wallet;
use base58::ToBase58;
use std::sync::Arc;
use tokio::sync::{broadcast, RwLock};
use warp::reject::Reject;
use warp::reply::Response;
use warp::{Buf, Rejection, Reply};

use crate::peer::{handle_inbound_peer_connection, PeersDB};

#[derive(Debug)]
struct Invalid;
impl Reject for Invalid {}

#[derive(Debug)]
struct AlreadyExists;
impl Reject for AlreadyExists {}

/// It seems that Warp handlers must return a Result<impl Reply>.
/// It looks like this was used as a simple way to turn a String
/// into a warp::Reply. It may be possilbe to use use Response::new
/// directly, or this may not be needed if we get rid of the http
/// handlers defined below...
struct Message {
    msg: String,
}

impl warp::Reply for Message {
    fn into_response(self) -> warp::reply::Response {
        Response::new(format!("message: {}", self.msg).into())
    }
}

/// websocket upgrade handler. accepts an http connection and upgrades it to WebSocket.
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Upgrade
/// Thanks, Ryan Dahl!!
pub async fn ws_upgrade_handler(
    ws: warp::ws::Ws,
    peer_db_lock: Arc<RwLock<PeersDB>>,
    wallet_lock: Arc<RwLock<Wallet>>,
    mempool_lock: Arc<RwLock<Mempool>>,
    blockchain_lock: Arc<RwLock<Blockchain>>,
    broadcast_channel_sender: broadcast::Sender<SaitoMessage>,
) -> std::result::Result<impl Reply, Rejection> {
    Ok(ws.on_upgrade(move |socket| {
        handle_inbound_peer_connection(
            socket,
            peer_db_lock,
            wallet_lock,
            mempool_lock,
            blockchain_lock,
            broadcast_channel_sender,
        )
    }))
}
/// POST tx filter.
/// TODO remove this? I believe we want ot use the socket for everything...
/// There is a SNDTRANS command which does this, but is currently unused
/// Let's keep this around for now in case we want to resurrect the spammer...
/// Once SNDBLKHD is being actively used, this should be deleted.
pub async fn post_transaction_handler(
    mut body: impl Buf,
    mempool_lock: Arc<RwLock<Mempool>>,
    blockchain_lock: Arc<RwLock<Blockchain>>,
) -> Result<impl Reply> {
    let mut buffer = vec![];
    while body.has_remaining() {
        buffer.append(&mut body.chunk().to_vec());
        let cnt = body.chunk().len();
        body.advance(cnt);
    }

    let mut tx = Transaction::deserialize_from_net(buffer);
    let blockchain = blockchain_lock.read().await;
    tx.generate_metadata(tx.inputs[0].get_publickey());
    if tx.validate(&blockchain.utxoset, &blockchain.staking) {
        let response = std::str::from_utf8(&tx.get_signature().to_base58().as_bytes())
            .unwrap()
            .to_string();
        let mut mempool = mempool_lock.write().await;
        mempool.add_transaction(tx).await;
        Ok(Message { msg: response })
    } else {
        Err(warp::reject::custom(Invalid))
    }
}

/// get block handler.
// TODO remove this. For now it is just in place as a simple means to transfer blocks to saito-lite so we
// can test the ability to serialize/deserialize blocks.
pub async fn get_block_handler(
    str_block_hash: String,
    blockchain_lock: Arc<RwLock<Blockchain>>,
) -> Result<impl Reply> {
    let mut block_hash = [0u8; 32];
    hex::decode_to_slice(str_block_hash.clone(), &mut block_hash).expect("Failed to parse hash");
    {
        let blockchain = blockchain_lock.read().await;
        let block = blockchain.get_block(&block_hash).await;
        match block {
            Some(block) => {
                let block_bytes = block.serialize_for_net(BlockType::Full);
                Ok(block_bytes)
            }
            None => Err(warp::reject()),
        }
    }
}