stacks_core/
lib.rs

1#![forbid(missing_docs)]
2#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
3//! # stacks-core library: a library for interacting with the Stacks protocol
4
5use std::{array::TryFromSliceError, io};
6
7use bdk::bitcoin::Network as BitcoinNetwork;
8use codec::{Codec, CodecError};
9use serde::{Deserialize, Serialize};
10use strum::{Display, EnumIter, EnumString, FromRepr};
11use thiserror::Error;
12use uint::Uint256;
13
14/// Module for interacting with stacks addresses
15pub mod address;
16/// Module for c32 encoding and decoding
17pub mod c32;
18pub mod codec;
19pub mod contract_name;
20/// Module for crypto functions
21pub mod crypto;
22/// Module for creating large integers and performing basic arithmetic
23pub mod uint;
24/// Module for utility functions
25pub mod utils;
26pub mod wallet;
27
28/// Error type for the stacks-core library
29#[derive(Error, Debug)]
30pub enum StacksError {
31	#[error("Invalid arguments: {0}")]
32	/// Invalid arguments
33	InvalidArguments(&'static str),
34	#[error("Could not crackford32 encode or decode: {0}")]
35	/// C32 encoding or decoding error
36	C32Error(#[from] c32::C32Error),
37	#[error("Address version is invalid: {0}")]
38	/// Invalid address version
39	InvalidAddressVersion(u8),
40	#[error("Could not build array from slice: {0}")]
41	/// Invalid slice length
42	InvalidSliceLength(#[from] TryFromSliceError),
43	#[error("Could not encode or decode hex: {0}")]
44	/// Hex encoding or decoding error due
45	BadHex(#[from] hex::FromHexError),
46	#[error("Could not create Uint from {0} bytes")]
47	/// Invalid Uint bytes
48	InvalidUintBytes(usize),
49	#[error("Codec error: {0}")]
50	/// Codec error
51	CodecError(#[from] CodecError),
52	#[error("Invalid data: {0}")]
53	/// Invalid data
54	InvalidData(&'static str),
55	/// BIP32 Error
56	#[error("BIP32 error: {0}")]
57	BIP32(#[from] bdk::bitcoin::util::bip32::Error),
58	/// BIP32 Error
59	#[error("BIP39 error: {0}")]
60	BIP39(#[from] bdk::keys::bip39::Error),
61	/// SECP Error
62	#[error("SECP error: {0}")]
63	SECP(#[from] bdk::bitcoin::secp256k1::Error),
64	/// Base58 Error
65	#[error("Base58 error: {0}")]
66	Base58(#[from] bdk::bitcoin::util::base58::Error),
67}
68
69/// Result type for the stacks-core library
70pub type StacksResult<T> = Result<T, StacksError>;
71
72/// A stacks block ID
73pub struct BlockId(Uint256);
74
75impl BlockId {
76	/// Creates a new StacksBlockId from a slice of bytes
77	pub fn new(number: Uint256) -> Self {
78		Self(number)
79	}
80}
81
82impl Codec for BlockId {
83	fn codec_serialize<W: io::Write>(&self, dest: &mut W) -> io::Result<()> {
84		self.0.codec_serialize(dest)
85	}
86
87	fn codec_deserialize<R: io::Read>(data: &mut R) -> io::Result<Self>
88	where
89		Self: Sized,
90	{
91		Ok(Self(Uint256::codec_deserialize(data)?))
92	}
93}
94
95/// Stacks network kind
96#[repr(u8)]
97#[derive(
98	Debug,
99	Clone,
100	Copy,
101	PartialEq,
102	Eq,
103	EnumString,
104	Display,
105	EnumIter,
106	FromRepr,
107	Serialize,
108	Deserialize,
109)]
110#[strum(ascii_case_insensitive)]
111#[strum(serialize_all = "lowercase")]
112#[serde(try_from = "String", into = "String")]
113pub enum Network {
114	/// Mainnet
115	Mainnet = 0,
116	/// Testnet
117	Testnet = 1,
118}
119
120impl TryFrom<String> for Network {
121	type Error = strum::ParseError;
122
123	fn try_from(value: String) -> Result<Self, Self::Error> {
124		Self::try_from(value.as_str())
125	}
126}
127
128// Other way around is fallible, so we don't implement it
129#[allow(clippy::from_over_into)]
130impl Into<String> for Network {
131	fn into(self) -> String {
132		self.to_string()
133	}
134}
135
136// For some reason From impl fails to compile
137#[allow(clippy::from_over_into)]
138impl Into<Network> for BitcoinNetwork {
139	fn into(self) -> Network {
140		match self {
141			BitcoinNetwork::Bitcoin => Network::Mainnet,
142			_ => Network::Testnet,
143		}
144	}
145}
146
147// For some reason From impl fails to compile
148#[allow(clippy::from_over_into)]
149impl Into<BitcoinNetwork> for Network {
150	fn into(self) -> BitcoinNetwork {
151		match self {
152			Network::Mainnet => BitcoinNetwork::Bitcoin,
153			Network::Testnet => BitcoinNetwork::Testnet,
154		}
155	}
156}