diff --git a/crates/floresta-cli/src/lib.rs b/crates/floresta-cli/src/lib.rs new file mode 100644 index 00000000..0e56f70b --- /dev/null +++ b/crates/floresta-cli/src/lib.rs @@ -0,0 +1,115 @@ +// SPDX license specifier: MIT + +//! # floresta-cli - A command line interface for florestad +//! +//! Florestad is a lightweight Bitcoin full node, built with libfloresta. It gives +//! you complete control over your Bitcoin node with a simple json-rpc interface that +//! may be used either from command line or programmatically. This crate provides a +//! ready-to-use library for interacting with florestad's json-rpc interface in your rust +//! application. + +mod rpc_types; + +use bitcoin::block::Header as BlockHeader; +use bitcoin::BlockHash; +use bitcoin::Txid; +use rpc_types::*; +use serde_json::Value; + +type Result = std::result::Result; + +/// A trait specifying all possible methods for floresta's json-rpc +pub trait FlorestaRPC { + /// Get the BIP158 filter for a given block height + /// + /// BIP158 filters are a compact representation of the set of transactions in a block, + /// designed for efficient light client synchronization. This method returns the filter + /// for a given block height, encoded as a hexadecimal string. + /// You need to have enabled block filters by setting the `blockfilters=1` option + fn get_block_filter(&self, heigth: u32) -> Result; + /// Returns general information about the chain we are on + /// + /// This method returns a bunch of information about the chain we are on, including + /// the current height, the best block hash, the difficulty, and whether we are + /// currently in IBD (Initial Block Download) mode. + fn get_blockchain_info(&self) -> Result; + /// Returns the hash of the block at the given height + /// + /// This method returns the hash of the block at the given height. If the height is + /// invalid, an error is returned. + fn get_block_hash(&self, height: u32) -> Result; + /// Returns the block header for the given block hash + /// + /// This method returns the block header for the given block hash, as defined + /// in the Bitcoin protocol specification. A header contains the block's version, + /// the previous block hash, the merkle root, the timestamp, the difficulty target, + /// and the nonce. + fn get_block_header(&self, hash: BlockHash) -> Result; + /// Gets a transaction from the blockchain + /// + /// This method returns a transaction that's cached in our wallet. If the verbosity flag is + /// set to false, the transaction is returned as a hexadecimal string. If the verbosity + /// flag is set to true, the transaction is returned as a json object. + fn get_transaction(&self, tx_id: Txid, verbosity: Option) -> Result; + /// Returns the proof that one or more transactions were included in a block + /// + /// This method returns the Merkle proof, showing that a transaction was included in a block. + /// The pooof is returned as a vector hexadecimal string. + fn get_tx_proof(&self, tx_id: Txid) -> Result>; + /// Loads up a descriptor into the wallet + /// + /// This method loads up a descriptor into the wallet. If the rescan option is not None, + /// the wallet will be rescanned for transactions matching the descriptor. If you have + /// compact block filters enabled, this process will be much faster and use less bandwidth. + /// The rescan parameter is the height at which to start the rescan, and should be at least + /// as old as the oldest transaction this descriptor could have been used in. + fn load_descriptor(&self, descriptor: String, rescan: Option) -> Result<()>; + /// Trigger a rescan of the wallet + /// + /// This method triggers a rescan of the wallet. If you have compact block filters enabled, + /// this process will be much faster and use less bandwidth. If you don't have compact block + /// filters, we'll need to download the entire blockchain again, which will take a while. + /// The rescan parameter is the height at which to start the rescan, and should be at least + /// as old as the oldest transaction this descriptor could have been used in. + fn rescan(&self, rescan: u32) -> Result; + /// Returns the current height of the blockchain + fn get_height(&self) -> Result; + /// Sends a hex-encoded transaction to the network + /// + /// This method sends a transaction to the network. The transaction should be encoded as a + /// hexadecimal string. If the transaction is valid, it will be broadcast to the network, and + /// return the transaction id. If the transaction is invalid, an error will be returned. + fn send_raw_transaction(&self, tx: String) -> Result; + /// Gets the current accumulator for the chain we're on + /// + /// This method returns the current accumulator for the chain we're on. The accumulator is + /// a set of roots, that let's us prove that a UTXO exists in the chain. This method returns + /// a vector of hexadecimal strings, each of which is a root in the accumulator. + fn get_roots(&self) -> Result>; + /// Gets information about the peers we're connected with + /// + /// This method returns information about the peers we're connected with. This includes + /// the peer's IP address, the peer's version, the peer's user agent, and the peer's + /// current height. + fn get_peer_info(&self) -> Result>; + /// Returns a block, given a block hash + /// + /// This method returns a block, given a block hash. If the verbosity flag is 0, the block + /// is returned as a hexadecimal string. If the verbosity flag is 1, the block is returned + /// as a json object. + fn get_block(&self, hash: BlockHash, verbosity: Option) -> Result; + /// Finds an specific utxo in the chain + /// + /// You can use this to look for a utxo. If it exists, it will return the amount and + /// scriptPubKey of this utxo. It returns an empty object if the utxo doesn't exist. + /// You must have enabled block filters by setting the `blockfilters=1` option. + fn get_tx_out(&self, tx_id: Txid, outpoint: u32) -> Result; + /// Stops the florestad process + /// + /// This can be used to gracefully stop the florestad process. + fn stop(&self) -> Result; + /// Tells florestad to connect with a peer + /// + /// You can use this to connect with a given node, providing it's IP address and port. + fn add_node(&self, node: String) -> Result; +} diff --git a/crates/floresta-cli/src/rpc_types.rs b/crates/floresta-cli/src/rpc_types.rs new file mode 100644 index 00000000..ca11b9fb --- /dev/null +++ b/crates/floresta-cli/src/rpc_types.rs @@ -0,0 +1,115 @@ +use serde::Deserialize; +use serde::Serialize; + +#[derive(Deserialize, Serialize)] +pub struct GetBlockchainInfoRes { + pub best_block: String, + pub height: u32, + pub ibd: bool, + pub validated: u32, + pub latest_work: String, + pub latest_block_time: u32, + pub leaf_count: u32, + pub root_count: u32, + pub root_hashes: Vec, + pub chain: String, + pub progress: f32, + pub difficulty: u64, +} + +#[derive(Deserialize, Serialize)] +pub struct RawTxJson { + pub in_active_chain: bool, + pub hex: String, + pub txid: String, + pub hash: String, + pub size: u32, + pub vsize: u32, + pub weight: u32, + pub version: u32, + pub locktime: u32, + pub vin: Vec, + pub vout: Vec, + pub blockhash: String, + pub confirmations: u32, + pub blocktime: u32, + pub time: u32, +} + +#[derive(Deserialize, Serialize)] +pub struct TxOutJson { + pub value: u64, + pub n: u32, + pub script_pub_key: ScriptPubKeyJson, +} + +#[derive(Deserialize, Serialize)] +pub struct ScriptPubKeyJson { + pub asm: String, + pub hex: String, + pub req_sigs: u32, + #[serde(rename = "type")] + pub type_: String, + pub address: String, +} + +#[derive(Deserialize, Serialize)] +pub struct TxInJson { + pub txid: String, + pub vout: u32, + pub script_sig: ScriptSigJson, + pub sequence: u32, + pub witness: Vec, +} + +#[derive(Deserialize, Serialize)] +pub struct ScriptSigJson { + pub asm: String, + pub hex: String, +} + +#[derive(Deserialize, Serialize)] +pub struct BlockJson { + pub hash: String, + pub confirmations: u32, + pub strippedsize: usize, + pub size: usize, + pub weight: usize, + pub height: u32, + pub version: i32, + #[serde(rename = "versionHex")] + pub version_hex: String, + pub merkleroot: String, + pub tx: Vec, + pub time: u32, + pub mediantime: u32, + pub nonce: u32, + pub bits: String, + pub difficulty: u128, + pub chainwork: String, + pub n_tx: usize, + pub previousblockhash: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub nextblockhash: Option, +} + +#[derive(Debug)] +pub enum Error { + TxNotFound, + InvalidDescriptor, + BlockNotFound, + Chain, + InvalidPort, + InvalidAddress, + Node, + NoBlockFilters, + InvalidNetwork, +} + +#[derive(Deserialize, Serialize)] +pub struct PeerInfo { + pub address: String, + pub services: String, + pub user_agent: String, + pub initial_height: u32, +}