use crate::impl_byte_array_newtype;
pub use clarity::codec::StacksMessageCodec;
use clarity::address::AddressHashMode;
use clarity::address::{
C32_ADDRESS_VERSION_MAINNET_MULTISIG, C32_ADDRESS_VERSION_MAINNET_SINGLESIG,
C32_ADDRESS_VERSION_TESTNET_MULTISIG, C32_ADDRESS_VERSION_TESTNET_SINGLESIG,
};
use clarity::codec::MAX_MESSAGE_LEN;
use clarity::codec::{read_next, write_next, Error as CodecError};
use clarity::types::chainstate::{
BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, StacksBlockId, StacksWorkScore, TrieHash,
};
use clarity::types::chainstate::{StacksAddress, StacksPublicKey};
use clarity::types::PrivateKey;
use clarity::util::hash::{Hash160, Sha512Trunc256Sum};
use clarity::util::retry::BoundReader;
use clarity::util::secp256k1::{
MessageSignature, Secp256k1PrivateKey, Secp256k1PublicKey, MESSAGE_SIGNATURE_ENCODED_SIZE,
};
use clarity::util::vrf::VRFProof;
use clarity::vm::types::{
PrincipalData, QualifiedContractIdentifier, StandardPrincipalData, Value,
};
use clarity::vm::ClarityVersion;
use clarity::vm::{ClarityName, ContractName};
use clarity::{
impl_array_hexstring_fmt, impl_array_newtype, impl_byte_array_message_codec,
impl_byte_array_serde,
};
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::convert::TryInto;
use std::fmt;
use std::io::{Read, Write};
use std::ops::Deref;
use std::ops::DerefMut;
use std::str::FromStr;
use wsts::common::Signature as Secp256k1Signature;
use wsts::curve::point::{Compressed as Secp256k1Compressed, Point as Secp256k1Point};
use wsts::curve::scalar::Scalar as Secp256k1Scalar;
pub const MAX_BLOCK_LEN: u32 = 2 * 1024 * 1024;
pub const MAX_TRANSACTION_LEN: u32 = MAX_BLOCK_LEN;
#[macro_export]
macro_rules! impl_byte_array_message_codec {
($thing:ident, $len:expr) => {
impl clarity::codec::StacksMessageCodec for $thing {
fn consensus_serialize<W: std::io::Write>(
&self,
fd: &mut W,
) -> Result<(), clarity::codec::Error> {
fd.write_all(self.as_bytes())
.map_err(clarity::codec::Error::WriteError)
}
fn consensus_deserialize<R: std::io::Read>(
fd: &mut R,
) -> Result<$thing, clarity::codec::Error> {
let mut buf = [0u8; ($len as usize)];
fd.read_exact(&mut buf)
.map_err(clarity::codec::Error::ReadError)?;
let ret = $thing::from_bytes(&buf).expect("BUG: buffer is not the right size");
Ok(ret)
}
}
};
}
#[macro_export]
macro_rules! impl_array_newtype {
($thing:ident, $ty:ty, $len:expr) => {
impl $thing {
#[inline]
#[allow(dead_code)]
pub fn as_ptr(&self) -> *const $ty {
let &$thing(ref dat) = self;
dat.as_ptr()
}
#[inline]
#[allow(dead_code)]
pub fn as_mut_ptr(&mut self) -> *mut $ty {
let &mut $thing(ref mut dat) = self;
dat.as_mut_ptr()
}
#[inline]
#[allow(dead_code)]
pub fn len(&self) -> usize {
$len
}
#[inline]
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
false
}
#[inline]
#[allow(dead_code)]
pub fn as_bytes(&self) -> &[$ty; $len] {
&self.0
}
#[inline]
#[allow(dead_code)]
pub fn to_bytes(&self) -> [$ty; $len] {
self.0.clone()
}
#[inline]
#[allow(dead_code)]
pub fn into_bytes(self) -> [$ty; $len] {
self.0
}
}
impl<'a> From<&'a [$ty]> for $thing {
fn from(data: &'a [$ty]) -> $thing {
assert_eq!(data.len(), $len);
let mut ret = [0; $len];
ret.copy_from_slice(&data[..]);
$thing(ret)
}
}
impl ::std::ops::Index<usize> for $thing {
type Output = $ty;
#[inline]
fn index(&self, index: usize) -> &$ty {
let &$thing(ref dat) = self;
&dat[index]
}
}
impl_index_newtype!($thing, $ty);
impl PartialEq for $thing {
#[inline]
fn eq(&self, other: &$thing) -> bool {
&self[..] == &other[..]
}
}
impl Eq for $thing {}
impl PartialOrd for $thing {
#[inline]
fn partial_cmp(&self, other: &$thing) -> Option<::std::cmp::Ordering> {
Some(self.cmp(&other))
}
}
impl Ord for $thing {
#[inline]
fn cmp(&self, other: &$thing) -> ::std::cmp::Ordering {
for i in 0..$len {
if self[$len - 1 - i] < other[$len - 1 - i] {
return ::std::cmp::Ordering::Less;
}
if self[$len - 1 - i] > other[$len - 1 - i] {
return ::std::cmp::Ordering::Greater;
}
}
::std::cmp::Ordering::Equal
}
}
#[cfg_attr(feature = "clippy", allow(expl_impl_clone_on_copy))] impl Clone for $thing {
#[inline]
fn clone(&self) -> $thing {
*self
}
}
impl Copy for $thing {}
impl ::std::hash::Hash for $thing {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: ::std::hash::Hasher,
{
(&self[..]).hash(state);
}
fn hash_slice<H>(data: &[$thing], state: &mut H)
where
H: ::std::hash::Hasher,
{
for d in data.iter() {
(&d[..]).hash(state);
}
}
}
};
}
#[macro_export]
macro_rules! impl_index_newtype {
($thing:ident, $ty:ty) => {
impl ::std::ops::Index<::std::ops::Range<usize>> for $thing {
type Output = [$ty];
#[inline]
fn index(&self, index: ::std::ops::Range<usize>) -> &[$ty] {
&self.0[index]
}
}
impl ::std::ops::Index<::std::ops::RangeTo<usize>> for $thing {
type Output = [$ty];
#[inline]
fn index(&self, index: ::std::ops::RangeTo<usize>) -> &[$ty] {
&self.0[index]
}
}
impl ::std::ops::Index<::std::ops::RangeFrom<usize>> for $thing {
type Output = [$ty];
#[inline]
fn index(&self, index: ::std::ops::RangeFrom<usize>) -> &[$ty] {
&self.0[index]
}
}
impl ::std::ops::Index<::std::ops::RangeFull> for $thing {
type Output = [$ty];
#[inline]
fn index(&self, _: ::std::ops::RangeFull) -> &[$ty] {
&self.0[..]
}
}
};
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum TransactionAnchorMode {
OnChainOnly = 1, OffChainOnly = 2, Any = 3, }
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum TransactionAuthFlags {
AuthStandard = 0x04,
AuthSponsored = 0x05,
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum TransactionAuthFieldID {
PublicKeyCompressed = 0x00,
PublicKeyUncompressed = 0x01,
SignatureCompressed = 0x02,
SignatureUncompressed = 0x03,
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum TransactionPublicKeyEncoding {
Compressed = 0x00,
Uncompressed = 0x01,
}
impl TransactionPublicKeyEncoding {
pub fn from_u8(n: u8) -> Option<TransactionPublicKeyEncoding> {
match n {
x if x == TransactionPublicKeyEncoding::Compressed as u8 => {
Some(TransactionPublicKeyEncoding::Compressed)
}
x if x == TransactionPublicKeyEncoding::Uncompressed as u8 => {
Some(TransactionPublicKeyEncoding::Uncompressed)
}
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TransactionAuthField {
PublicKey(StacksPublicKey),
Signature(TransactionPublicKeyEncoding, MessageSignature),
}
impl TransactionAuthField {
pub fn is_public_key(&self) -> bool {
matches!(*self, TransactionAuthField::PublicKey(_))
}
pub fn is_signature(&self) -> bool {
matches!(*self, TransactionAuthField::Signature(_, _))
}
pub fn as_public_key(&self) -> Option<Secp256k1PublicKey> {
match *self {
#[allow(clippy::clone_on_copy)]
TransactionAuthField::PublicKey(ref pubk) => Some(pubk.clone()),
_ => None,
}
}
pub fn as_signature(&self) -> Option<(TransactionPublicKeyEncoding, MessageSignature)> {
match *self {
TransactionAuthField::Signature(ref key_fmt, ref sig) => Some((*key_fmt, *sig)),
_ => None,
}
}
pub fn get_public_key(&self, sighash_bytes: &[u8]) -> Result<Secp256k1PublicKey, CodecError> {
match *self {
#[allow(clippy::clone_on_copy)]
TransactionAuthField::PublicKey(ref pubk) => Ok(pubk.clone()),
TransactionAuthField::Signature(ref key_fmt, ref sig) => {
let mut pubk = Secp256k1PublicKey::recover_to_pubkey(sighash_bytes, sig)
.map_err(|e| CodecError::SigningError(e.to_string()))?;
pubk.set_compressed(*key_fmt == TransactionPublicKeyEncoding::Compressed);
Ok(pubk)
}
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SinglesigHashMode {
P2PKH = 0x00,
P2WPKH = 0x02,
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum MultisigHashMode {
P2SH = 0x01,
P2WSH = 0x03,
}
impl SinglesigHashMode {
pub fn to_address_hash_mode(&self) -> AddressHashMode {
match *self {
SinglesigHashMode::P2PKH => AddressHashMode::SerializeP2PKH,
SinglesigHashMode::P2WPKH => AddressHashMode::SerializeP2WPKH,
}
}
pub fn from_address_hash_mode(hm: AddressHashMode) -> Option<SinglesigHashMode> {
match hm {
AddressHashMode::SerializeP2PKH => Some(SinglesigHashMode::P2PKH),
AddressHashMode::SerializeP2WPKH => Some(SinglesigHashMode::P2WPKH),
_ => None,
}
}
pub fn from_u8(n: u8) -> Option<SinglesigHashMode> {
match n {
x if x == SinglesigHashMode::P2PKH as u8 => Some(SinglesigHashMode::P2PKH),
x if x == SinglesigHashMode::P2WPKH as u8 => Some(SinglesigHashMode::P2WPKH),
_ => None,
}
}
}
impl MultisigHashMode {
pub fn to_address_hash_mode(&self) -> AddressHashMode {
match *self {
MultisigHashMode::P2SH => AddressHashMode::SerializeP2SH,
MultisigHashMode::P2WSH => AddressHashMode::SerializeP2WSH,
}
}
pub fn from_address_hash_mode(hm: AddressHashMode) -> Option<MultisigHashMode> {
match hm {
AddressHashMode::SerializeP2SH => Some(MultisigHashMode::P2SH),
AddressHashMode::SerializeP2WSH => Some(MultisigHashMode::P2WSH),
_ => None,
}
}
pub fn from_u8(n: u8) -> Option<MultisigHashMode> {
match n {
x if x == MultisigHashMode::P2SH as u8 => Some(MultisigHashMode::P2SH),
x if x == MultisigHashMode::P2WSH as u8 => Some(MultisigHashMode::P2WSH),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MultisigSpendingCondition {
pub hash_mode: MultisigHashMode,
pub signer: Hash160,
pub nonce: u64, pub tx_fee: u64, pub fields: Vec<TransactionAuthField>,
pub signatures_required: u16,
}
impl MultisigSpendingCondition {
pub fn push_signature(
&mut self,
key_encoding: TransactionPublicKeyEncoding,
signature: MessageSignature,
) {
self.fields
.push(TransactionAuthField::Signature(key_encoding, signature));
}
pub fn push_public_key(&mut self, public_key: Secp256k1PublicKey) {
self.fields
.push(TransactionAuthField::PublicKey(public_key));
}
pub fn pop_auth_field(&mut self) -> Option<TransactionAuthField> {
self.fields.pop()
}
pub fn address_mainnet(&self) -> StacksAddress {
StacksAddress {
version: C32_ADDRESS_VERSION_MAINNET_MULTISIG,
bytes: self.signer,
}
}
pub fn address_testnet(&self) -> StacksAddress {
StacksAddress {
version: C32_ADDRESS_VERSION_TESTNET_MULTISIG,
bytes: self.signer,
}
}
pub fn verify(
&self,
initial_sighash: &Txid,
cond_code: &TransactionAuthFlags,
) -> Result<Txid, CodecError> {
let mut pubkeys = vec![];
let mut cur_sighash = *initial_sighash;
let mut num_sigs: u16 = 0;
let mut have_uncompressed = false;
for field in self.fields.iter() {
let pubkey = match field {
TransactionAuthField::PublicKey(ref pubkey) => {
if !pubkey.compressed() {
have_uncompressed = true;
}
#[allow(clippy::clone_on_copy)]
pubkey.clone()
}
TransactionAuthField::Signature(ref pubkey_encoding, ref sigbuf) => {
if *pubkey_encoding == TransactionPublicKeyEncoding::Uncompressed {
have_uncompressed = true;
}
let (pubkey, next_sighash) = TransactionSpendingCondition::next_verification(
&cur_sighash,
cond_code,
self.tx_fee,
self.nonce,
pubkey_encoding,
sigbuf,
)?;
cur_sighash = next_sighash;
num_sigs = num_sigs
.checked_add(1)
.ok_or(CodecError::SigningError("Too many signatures".to_string()))?;
pubkey
}
};
pubkeys.push(pubkey);
}
if num_sigs != self.signatures_required {
return Err(CodecError::SigningError(
"Incorrect number of signatures".to_string(),
));
}
if have_uncompressed && self.hash_mode == MultisigHashMode::P2WSH {
return Err(CodecError::SigningError(
"Uncompressed keys are not allowed in this hash mode".to_string(),
));
}
let addr_bytes = match StacksAddress::from_public_keys(
0,
&self.hash_mode.to_address_hash_mode(),
self.signatures_required as usize,
&pubkeys,
) {
Some(a) => a.bytes,
None => {
return Err(CodecError::SigningError(
"Failed to generate address from public keys".to_string(),
));
}
};
if addr_bytes != self.signer {
return Err(CodecError::SigningError(format!(
"Signer hash does not equal hash of public key(s): {} != {}",
addr_bytes, self.signer
)));
}
Ok(cur_sighash)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SinglesigSpendingCondition {
pub hash_mode: SinglesigHashMode,
pub signer: Hash160,
pub nonce: u64, pub tx_fee: u64, pub key_encoding: TransactionPublicKeyEncoding,
pub signature: MessageSignature,
}
impl SinglesigSpendingCondition {
pub fn set_signature(&mut self, signature: MessageSignature) {
self.signature = signature;
}
pub fn pop_signature(&mut self) -> Option<TransactionAuthField> {
if self.signature == MessageSignature::empty() {
return None;
}
let ret = self.signature;
self.signature = MessageSignature::empty();
Some(TransactionAuthField::Signature(self.key_encoding, ret))
}
pub fn address_mainnet(&self) -> StacksAddress {
let version = match self.hash_mode {
SinglesigHashMode::P2PKH => C32_ADDRESS_VERSION_MAINNET_SINGLESIG,
SinglesigHashMode::P2WPKH => C32_ADDRESS_VERSION_MAINNET_MULTISIG,
};
StacksAddress {
version,
bytes: self.signer,
}
}
pub fn address_testnet(&self) -> StacksAddress {
let version = match self.hash_mode {
SinglesigHashMode::P2PKH => C32_ADDRESS_VERSION_TESTNET_SINGLESIG,
SinglesigHashMode::P2WPKH => C32_ADDRESS_VERSION_TESTNET_MULTISIG,
};
StacksAddress {
version,
bytes: self.signer,
}
}
pub fn verify(
&self,
initial_sighash: &Txid,
cond_code: &TransactionAuthFlags,
) -> Result<Txid, CodecError> {
let (pubkey, next_sighash) = TransactionSpendingCondition::next_verification(
initial_sighash,
cond_code,
self.tx_fee,
self.nonce,
&self.key_encoding,
&self.signature,
)?;
let addr_bytes = match StacksAddress::from_public_keys(
0,
&self.hash_mode.to_address_hash_mode(),
1,
&vec![pubkey],
) {
Some(a) => a.bytes,
None => {
return Err(CodecError::SigningError(
"Failed to generate address from public key".to_string(),
));
}
};
if addr_bytes != self.signer {
return Err(CodecError::SigningError(format!(
"Signer hash does not equal hash of public key(s): {} != {}",
&addr_bytes, &self.signer
)));
}
Ok(next_sighash)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TransactionSpendingCondition {
Singlesig(SinglesigSpendingCondition),
Multisig(MultisigSpendingCondition),
}
impl TransactionSpendingCondition {
pub fn new_singlesig_p2pkh(pubkey: Secp256k1PublicKey) -> Option<TransactionSpendingCondition> {
let key_encoding = if pubkey.compressed() {
TransactionPublicKeyEncoding::Compressed
} else {
TransactionPublicKeyEncoding::Uncompressed
};
let signer_addr =
StacksAddress::from_public_keys(0, &AddressHashMode::SerializeP2PKH, 1, &vec![pubkey])?;
Some(TransactionSpendingCondition::Singlesig(
SinglesigSpendingCondition {
signer: signer_addr.bytes,
nonce: 0,
tx_fee: 0,
hash_mode: SinglesigHashMode::P2PKH,
key_encoding,
signature: MessageSignature::empty(),
},
))
}
pub fn new_singlesig_p2wpkh(
pubkey: Secp256k1PublicKey,
) -> Option<TransactionSpendingCondition> {
let signer_addr = StacksAddress::from_public_keys(
0,
&AddressHashMode::SerializeP2WPKH,
1,
&vec![pubkey],
)?;
Some(TransactionSpendingCondition::Singlesig(
SinglesigSpendingCondition {
signer: signer_addr.bytes,
nonce: 0,
tx_fee: 0,
hash_mode: SinglesigHashMode::P2WPKH,
key_encoding: TransactionPublicKeyEncoding::Compressed,
signature: MessageSignature::empty(),
},
))
}
pub fn new_multisig_p2sh(
num_sigs: u16,
pubkeys: Vec<Secp256k1PublicKey>,
) -> Option<TransactionSpendingCondition> {
let signer_addr = StacksAddress::from_public_keys(
0,
&AddressHashMode::SerializeP2SH,
num_sigs as usize,
&pubkeys,
)?;
Some(TransactionSpendingCondition::Multisig(
MultisigSpendingCondition {
signer: signer_addr.bytes,
nonce: 0,
tx_fee: 0,
hash_mode: MultisigHashMode::P2SH,
fields: vec![],
signatures_required: num_sigs,
},
))
}
pub fn new_multisig_p2wsh(
num_sigs: u16,
pubkeys: Vec<Secp256k1PublicKey>,
) -> Option<TransactionSpendingCondition> {
let signer_addr = StacksAddress::from_public_keys(
0,
&AddressHashMode::SerializeP2WSH,
num_sigs as usize,
&pubkeys,
)?;
Some(TransactionSpendingCondition::Multisig(
MultisigSpendingCondition {
signer: signer_addr.bytes,
nonce: 0,
tx_fee: 0,
hash_mode: MultisigHashMode::P2WSH,
fields: vec![],
signatures_required: num_sigs,
},
))
}
pub fn new_initial_sighash() -> TransactionSpendingCondition {
TransactionSpendingCondition::Singlesig(SinglesigSpendingCondition {
signer: Hash160([0u8; 20]),
nonce: 0,
tx_fee: 0,
hash_mode: SinglesigHashMode::P2PKH,
key_encoding: TransactionPublicKeyEncoding::Compressed,
signature: MessageSignature::empty(),
})
}
pub fn num_signatures(&self) -> u16 {
match *self {
TransactionSpendingCondition::Singlesig(ref data) => {
if data.signature != MessageSignature::empty() {
1
} else {
0
}
}
TransactionSpendingCondition::Multisig(ref data) => {
let mut num_sigs: u16 = 0;
for field in data.fields.iter() {
if field.is_signature() {
num_sigs = num_sigs
.checked_add(1)
.expect("Unreasonable amount of signatures"); }
}
num_sigs
}
}
}
pub fn signatures_required(&self) -> u16 {
match *self {
TransactionSpendingCondition::Singlesig(_) => 1,
TransactionSpendingCondition::Multisig(ref multisig_data) => {
multisig_data.signatures_required
}
}
}
pub fn nonce(&self) -> u64 {
match *self {
TransactionSpendingCondition::Singlesig(ref data) => data.nonce,
TransactionSpendingCondition::Multisig(ref data) => data.nonce,
}
}
pub fn tx_fee(&self) -> u64 {
match *self {
TransactionSpendingCondition::Singlesig(ref data) => data.tx_fee,
TransactionSpendingCondition::Multisig(ref data) => data.tx_fee,
}
}
pub fn set_nonce(&mut self, n: u64) {
match *self {
TransactionSpendingCondition::Singlesig(ref mut singlesig_data) => {
singlesig_data.nonce = n;
}
TransactionSpendingCondition::Multisig(ref mut multisig_data) => {
multisig_data.nonce = n;
}
}
}
pub fn set_tx_fee(&mut self, tx_fee: u64) {
match *self {
TransactionSpendingCondition::Singlesig(ref mut singlesig_data) => {
singlesig_data.tx_fee = tx_fee;
}
TransactionSpendingCondition::Multisig(ref mut multisig_data) => {
multisig_data.tx_fee = tx_fee;
}
}
}
pub fn get_tx_fee(&self) -> u64 {
match *self {
TransactionSpendingCondition::Singlesig(ref singlesig_data) => singlesig_data.tx_fee,
TransactionSpendingCondition::Multisig(ref multisig_data) => multisig_data.tx_fee,
}
}
pub fn address_mainnet(&self) -> StacksAddress {
match *self {
TransactionSpendingCondition::Singlesig(ref data) => data.address_mainnet(),
TransactionSpendingCondition::Multisig(ref data) => data.address_mainnet(),
}
}
pub fn address_testnet(&self) -> StacksAddress {
match *self {
TransactionSpendingCondition::Singlesig(ref data) => data.address_testnet(),
TransactionSpendingCondition::Multisig(ref data) => data.address_testnet(),
}
}
pub fn clear(&mut self) {
match *self {
TransactionSpendingCondition::Singlesig(ref mut singlesig_data) => {
singlesig_data.tx_fee = 0;
singlesig_data.nonce = 0;
singlesig_data.signature = MessageSignature::empty();
}
TransactionSpendingCondition::Multisig(ref mut multisig_data) => {
multisig_data.tx_fee = 0;
multisig_data.nonce = 0;
multisig_data.fields.clear();
}
}
}
pub fn make_sighash_presign(
cur_sighash: &Txid,
cond_code: &TransactionAuthFlags,
tx_fee: u64,
nonce: u64,
) -> Txid {
let new_tx_hash_bits_len = 32 + 1 + 8 + 8;
let mut new_tx_hash_bits = Vec::with_capacity(new_tx_hash_bits_len as usize);
new_tx_hash_bits.extend_from_slice(cur_sighash.as_bytes());
new_tx_hash_bits.extend_from_slice(&[*cond_code as u8]);
new_tx_hash_bits.extend_from_slice(&tx_fee.to_be_bytes());
new_tx_hash_bits.extend_from_slice(&nonce.to_be_bytes());
assert!(new_tx_hash_bits.len() == new_tx_hash_bits_len as usize);
Txid::from_sighash_bytes(&new_tx_hash_bits)
}
pub fn make_sighash_postsign(
cur_sighash: &Txid,
pubkey: &Secp256k1PublicKey,
sig: &MessageSignature,
) -> Txid {
let new_tx_hash_bits_len = 32 + 1 + MESSAGE_SIGNATURE_ENCODED_SIZE;
let mut new_tx_hash_bits = Vec::with_capacity(new_tx_hash_bits_len as usize);
let pubkey_encoding = if pubkey.compressed() {
TransactionPublicKeyEncoding::Compressed
} else {
TransactionPublicKeyEncoding::Uncompressed
};
new_tx_hash_bits.extend_from_slice(cur_sighash.as_bytes());
new_tx_hash_bits.extend_from_slice(&[pubkey_encoding as u8]);
new_tx_hash_bits.extend_from_slice(sig.as_bytes());
assert!(new_tx_hash_bits.len() == new_tx_hash_bits_len as usize);
Txid::from_sighash_bytes(&new_tx_hash_bits)
}
pub fn next_signature(
cur_sighash: &Txid,
cond_code: &TransactionAuthFlags,
tx_fee: u64,
nonce: u64,
privk: &Secp256k1PrivateKey,
) -> Result<(MessageSignature, Txid), CodecError> {
let sighash_presign = TransactionSpendingCondition::make_sighash_presign(
cur_sighash,
cond_code,
tx_fee,
nonce,
);
let sig = privk
.sign(sighash_presign.as_bytes())
.map_err(|se| CodecError::SigningError(se.to_string()))?;
let pubk = Secp256k1PublicKey::from_private(privk);
let next_sighash =
TransactionSpendingCondition::make_sighash_postsign(&sighash_presign, &pubk, &sig);
Ok((sig, next_sighash))
}
pub fn next_verification(
cur_sighash: &Txid,
cond_code: &TransactionAuthFlags,
tx_fee: u64,
nonce: u64,
key_encoding: &TransactionPublicKeyEncoding,
sig: &MessageSignature,
) -> Result<(Secp256k1PublicKey, Txid), CodecError> {
let sighash_presign = TransactionSpendingCondition::make_sighash_presign(
cur_sighash,
cond_code,
tx_fee,
nonce,
);
let mut pubk = Secp256k1PublicKey::recover_to_pubkey(sighash_presign.as_bytes(), sig)
.map_err(|ve| CodecError::SigningError(ve.to_string()))?;
match key_encoding {
TransactionPublicKeyEncoding::Compressed => pubk.set_compressed(true),
TransactionPublicKeyEncoding::Uncompressed => pubk.set_compressed(false),
};
let next_sighash =
TransactionSpendingCondition::make_sighash_postsign(&sighash_presign, &pubk, sig);
Ok((pubk, next_sighash))
}
pub fn verify(
&self,
initial_sighash: &Txid,
cond_code: &TransactionAuthFlags,
) -> Result<Txid, CodecError> {
match *self {
TransactionSpendingCondition::Singlesig(ref data) => {
data.verify(initial_sighash, cond_code)
}
TransactionSpendingCondition::Multisig(ref data) => {
data.verify(initial_sighash, cond_code)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TransactionAuth {
Standard(TransactionSpendingCondition),
Sponsored(TransactionSpendingCondition, TransactionSpendingCondition), }
impl TransactionAuth {
pub fn from_p2pkh(privk: &Secp256k1PrivateKey) -> Option<TransactionAuth> {
TransactionSpendingCondition::new_singlesig_p2pkh(Secp256k1PublicKey::from_private(privk))
.map(TransactionAuth::Standard)
}
pub fn from_p2sh(privks: &[Secp256k1PrivateKey], num_sigs: u16) -> Option<TransactionAuth> {
let mut pubks = vec![];
for privk in privks.iter() {
pubks.push(Secp256k1PublicKey::from_private(privk));
}
TransactionSpendingCondition::new_multisig_p2sh(num_sigs, pubks)
.map(TransactionAuth::Standard)
}
pub fn from_p2wpkh(privk: &Secp256k1PrivateKey) -> Option<TransactionAuth> {
TransactionSpendingCondition::new_singlesig_p2wpkh(Secp256k1PublicKey::from_private(privk))
.map(TransactionAuth::Standard)
}
pub fn from_p2wsh(privks: &[Secp256k1PrivateKey], num_sigs: u16) -> Option<TransactionAuth> {
let mut pubks = vec![];
for privk in privks.iter() {
pubks.push(Secp256k1PublicKey::from_private(privk));
}
TransactionSpendingCondition::new_multisig_p2wsh(num_sigs, pubks)
.map(TransactionAuth::Standard)
}
pub fn into_sponsored(self, sponsor_auth: TransactionAuth) -> Option<TransactionAuth> {
match (self, sponsor_auth) {
(TransactionAuth::Standard(sc), TransactionAuth::Standard(sp)) => {
Some(TransactionAuth::Sponsored(sc, sp))
}
(_, _) => None,
}
}
pub fn set_sponsor(
&mut self,
sponsor_spending_cond: TransactionSpendingCondition,
) -> Result<(), CodecError> {
match *self {
TransactionAuth::Sponsored(_, ref mut ssc) => {
*ssc = sponsor_spending_cond;
Ok(())
}
_ => Err(CodecError::GenericError(
"IncompatibleSpendingConditionError".into(),
)),
}
}
pub fn is_standard(&self) -> bool {
matches!(*self, TransactionAuth::Standard(_))
}
pub fn is_sponsored(&self) -> bool {
matches!(*self, TransactionAuth::Sponsored(_, _))
}
pub fn into_initial_sighash_auth(self) -> TransactionAuth {
match self {
TransactionAuth::Standard(mut origin) => {
origin.clear();
TransactionAuth::Standard(origin)
}
TransactionAuth::Sponsored(mut origin, _) => {
origin.clear();
TransactionAuth::Sponsored(
origin,
TransactionSpendingCondition::new_initial_sighash(),
)
}
}
}
pub fn origin(&self) -> &TransactionSpendingCondition {
match *self {
TransactionAuth::Standard(ref s) => s,
TransactionAuth::Sponsored(ref s, _) => s,
}
}
pub fn get_origin_nonce(&self) -> u64 {
self.origin().nonce()
}
pub fn set_origin_nonce(&mut self, n: u64) {
match *self {
TransactionAuth::Standard(ref mut s) => s.set_nonce(n),
TransactionAuth::Sponsored(ref mut s, _) => s.set_nonce(n),
}
}
pub fn sponsor(&self) -> Option<&TransactionSpendingCondition> {
match *self {
TransactionAuth::Standard(_) => None,
TransactionAuth::Sponsored(_, ref s) => Some(s),
}
}
pub fn get_sponsor_nonce(&self) -> Option<u64> {
self.sponsor().map(|s| s.nonce())
}
pub fn set_sponsor_nonce(&mut self, n: u64) -> Result<(), CodecError> {
match *self {
TransactionAuth::Standard(_) => Err(CodecError::GenericError(
"IncompatibleSpendingConditionError".into(),
)),
TransactionAuth::Sponsored(_, ref mut s) => {
s.set_nonce(n);
Ok(())
}
}
}
pub fn set_tx_fee(&mut self, tx_fee: u64) {
match *self {
TransactionAuth::Standard(ref mut s) => s.set_tx_fee(tx_fee),
TransactionAuth::Sponsored(_, ref mut s) => s.set_tx_fee(tx_fee),
}
}
pub fn get_tx_fee(&self) -> u64 {
match *self {
TransactionAuth::Standard(ref s) => s.get_tx_fee(),
TransactionAuth::Sponsored(_, ref s) => s.get_tx_fee(),
}
}
pub fn verify_origin(&self, initial_sighash: &Txid) -> Result<Txid, CodecError> {
match *self {
TransactionAuth::Standard(ref origin_condition) => {
origin_condition.verify(initial_sighash, &TransactionAuthFlags::AuthStandard)
}
TransactionAuth::Sponsored(ref origin_condition, _) => {
origin_condition.verify(initial_sighash, &TransactionAuthFlags::AuthStandard)
}
}
}
pub fn verify(&self, initial_sighash: &Txid) -> Result<(), CodecError> {
let origin_sighash = self.verify_origin(initial_sighash)?;
match *self {
TransactionAuth::Standard(_) => Ok(()),
TransactionAuth::Sponsored(_, ref sponsor_condition) => sponsor_condition
.verify(&origin_sighash, &TransactionAuthFlags::AuthSponsored)
.map(|_sigh| ()),
}
}
pub fn clear(&mut self) {
match *self {
TransactionAuth::Standard(ref mut origin_condition) => {
origin_condition.clear();
}
TransactionAuth::Sponsored(ref mut origin_condition, ref mut sponsor_condition) => {
origin_condition.clear();
sponsor_condition.clear();
}
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TransactionContractCall {
pub address: StacksAddress,
pub contract_name: ContractName,
pub function_name: ClarityName,
pub function_args: Vec<Value>,
}
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub struct StacksString(Vec<u8>);
impl fmt::Display for StacksString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(String::from_utf8_lossy(self).into_owned().as_str())
}
}
impl fmt::Debug for StacksString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(String::from_utf8_lossy(self).into_owned().as_str())
}
}
impl std::str::FromStr for StacksString {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if !StacksString::is_valid_string(&String::from(s)) {
return Err("Invalid string".to_string());
}
Ok(StacksString(s.as_bytes().to_vec()))
}
}
impl Deref for StacksString {
type Target = Vec<u8>;
fn deref(&self) -> &Vec<u8> {
&self.0
}
}
impl DerefMut for StacksString {
fn deref_mut(&mut self) -> &mut Vec<u8> {
&mut self.0
}
}
impl From<ClarityName> for StacksString {
fn from(clarity_name: ClarityName) -> StacksString {
StacksString::from_str(&clarity_name).unwrap()
}
}
impl From<ContractName> for StacksString {
fn from(contract_name: ContractName) -> StacksString {
StacksString::from_str(&contract_name).unwrap()
}
}
impl StacksString {
pub fn is_valid_string(s: &String) -> bool {
s.is_ascii() && StacksString::is_printable(s)
}
pub fn is_printable(s: &String) -> bool {
if !s.is_ascii() {
return false;
}
for c in s.as_bytes().iter() {
if (*c < 0x20 && *c != b'\t' && *c != b'\n') || (*c > 0x7e) {
return false;
}
}
true
}
pub fn is_clarity_variable(&self) -> bool {
ClarityName::try_from(self.to_string()).is_ok()
}
pub fn from_string(s: &String) -> Option<StacksString> {
if !StacksString::is_valid_string(s) {
return None;
}
Some(StacksString(s.as_bytes().to_vec()))
}
}
#[test]
fn test_display() {
let stxstr = StacksString::from_string(&"hello".to_string()).unwrap();
println!("log: {:#?}", stxstr);
println!("log: {:#?}", stxstr.to_string());
}
impl StacksMessageCodec for StacksString {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
write_next(fd, &self.0)
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<StacksString, CodecError> {
let bytes: Vec<u8> = {
let mut bound_read = BoundReader::from_reader(fd, MAX_MESSAGE_LEN as u64);
read_next(&mut bound_read)
}?;
let s = String::from_utf8(bytes.clone()).map_err(|_e| {
CodecError::DeserializeError(
"Invalid Stacks string: could not build from utf8".to_string(),
)
})?;
if !StacksString::is_valid_string(&s) {
return Err(CodecError::DeserializeError(
"Invalid Stacks string: non-printable or non-ASCII string".to_string(),
));
}
Ok(StacksString(bytes))
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TransactionSmartContract {
pub name: ContractName,
pub code_body: StacksString,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ThresholdSignature(pub wsts::common::Signature);
impl StacksMessageCodec for ThresholdSignature {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
let compressed = self.0.R.compress();
let bytes = compressed.as_bytes();
fd.write_all(bytes).map_err(CodecError::WriteError)?;
write_next(fd, &self.0.z.to_bytes())?;
Ok(())
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<Self, CodecError> {
let mut buf = [0u8; 33];
fd.read_exact(&mut buf).map_err(CodecError::ReadError)?;
let r = Secp256k1Point::try_from(&Secp256k1Compressed::from(buf))
.map_err(|_| CodecError::DeserializeError("Failed to read curve point".into()))?;
let mut buf = [0u8; 32];
fd.read_exact(&mut buf).map_err(CodecError::ReadError)?;
let z = Secp256k1Scalar::from(buf);
Ok(Self(Secp256k1Signature { R: r, z }))
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum TenureChangeCause {
BlockFound = 0,
Extended = 1,
}
impl TryFrom<u8> for TenureChangeCause {
type Error = ();
fn try_from(num: u8) -> Result<Self, Self::Error> {
match num {
0 => Ok(Self::BlockFound),
1 => Ok(Self::Extended),
_ => Err(()),
}
}
}
impl StacksMessageCodec for TenureChangeCause {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
let byte = (*self) as u8;
write_next(fd, &byte)
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<TenureChangeCause, CodecError> {
let byte: u8 = read_next(fd)?;
TenureChangeCause::try_from(byte).map_err(|_| {
CodecError::DeserializeError(format!("Unrecognized TenureChangeCause byte {byte}"))
})
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TenureChangePayload {
pub tenure_consensus_hash: ConsensusHash,
pub prev_tenure_consensus_hash: ConsensusHash,
pub burn_view_consensus_hash: ConsensusHash,
pub previous_tenure_end: StacksBlockId,
pub previous_tenure_blocks: u32,
pub cause: TenureChangeCause,
pub pubkey_hash: Hash160,
pub signature: ThresholdSignature,
pub signers: Vec<u8>,
}
impl StacksMessageCodec for TenureChangePayload {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
write_next(fd, &self.tenure_consensus_hash)?;
write_next(fd, &self.prev_tenure_consensus_hash)?;
write_next(fd, &self.burn_view_consensus_hash)?;
write_next(fd, &self.previous_tenure_end)?;
write_next(fd, &self.previous_tenure_blocks)?;
write_next(fd, &self.cause)?;
write_next(fd, &self.pubkey_hash)?;
write_next(fd, &self.signature)?;
write_next(fd, &self.signers)
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<Self, CodecError> {
Ok(Self {
tenure_consensus_hash: read_next(fd)?,
prev_tenure_consensus_hash: read_next(fd)?,
burn_view_consensus_hash: read_next(fd)?,
previous_tenure_end: read_next(fd)?,
previous_tenure_blocks: read_next(fd)?,
cause: read_next(fd)?,
pubkey_hash: read_next(fd)?,
signature: read_next(fd)?,
signers: read_next(fd)?,
})
}
}
pub struct CoinbasePayload(pub [u8; 32]);
impl_byte_array_message_codec!(CoinbasePayload, 32);
impl_array_newtype!(CoinbasePayload, u8, 32);
impl_array_hexstring_fmt!(CoinbasePayload);
impl_byte_array_newtype!(CoinbasePayload, u8, 32);
impl_byte_array_serde!(CoinbasePayload);
pub struct TokenTransferMemo(pub [u8; 34]); impl_byte_array_message_codec!(TokenTransferMemo, 34);
impl_array_newtype!(TokenTransferMemo, u8, 34);
impl_array_hexstring_fmt!(TokenTransferMemo);
impl_byte_array_newtype!(TokenTransferMemo, u8, 34);
impl_byte_array_serde!(TokenTransferMemo);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TransactionPayload {
TokenTransfer(PrincipalData, u64, TokenTransferMemo),
ContractCall(TransactionContractCall),
SmartContract(TransactionSmartContract, Option<ClarityVersion>),
PoisonMicroblock(StacksMicroblockHeader, StacksMicroblockHeader),
Coinbase(CoinbasePayload, Option<PrincipalData>, Option<VRFProof>),
TenureChange(TenureChangePayload),
}
impl TransactionPayload {
pub fn name(&self) -> &'static str {
match self {
TransactionPayload::TokenTransfer(..) => "TokenTransfer",
TransactionPayload::ContractCall(..) => "ContractCall",
TransactionPayload::SmartContract(..) => "SmartContract",
TransactionPayload::PoisonMicroblock(..) => "PoisonMicroblock",
TransactionPayload::Coinbase(..) => "Coinbase",
TransactionPayload::TenureChange(..) => "TenureChange",
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum TransactionPayloadID {
TokenTransfer = 0,
SmartContract = 1,
ContractCall = 2,
PoisonMicroblock = 3,
Coinbase = 4,
CoinbaseToAltRecipient = 5,
VersionedSmartContract = 6,
TenureChange = 7,
NakamotoCoinbase = 8,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AssetInfo {
pub contract_address: StacksAddress,
pub contract_name: ContractName,
pub asset_name: ClarityName,
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum AssetInfoID {
STX = 0,
FungibleAsset = 1,
NonfungibleAsset = 2,
}
impl AssetInfoID {
pub fn from_u8(b: u8) -> Option<AssetInfoID> {
match b {
0 => Some(AssetInfoID::STX),
1 => Some(AssetInfoID::FungibleAsset),
2 => Some(AssetInfoID::NonfungibleAsset),
_ => None,
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum FungibleConditionCode {
SentEq = 0x01,
SentGt = 0x02,
SentGe = 0x03,
SentLt = 0x04,
SentLe = 0x05,
}
impl FungibleConditionCode {
pub fn from_u8(b: u8) -> Option<FungibleConditionCode> {
match b {
0x01 => Some(FungibleConditionCode::SentEq),
0x02 => Some(FungibleConditionCode::SentGt),
0x03 => Some(FungibleConditionCode::SentGe),
0x04 => Some(FungibleConditionCode::SentLt),
0x05 => Some(FungibleConditionCode::SentLe),
_ => None,
}
}
pub fn check(&self, amount_sent_condition: u128, amount_sent: u128) -> bool {
match *self {
FungibleConditionCode::SentEq => amount_sent == amount_sent_condition,
FungibleConditionCode::SentGt => amount_sent > amount_sent_condition,
FungibleConditionCode::SentGe => amount_sent >= amount_sent_condition,
FungibleConditionCode::SentLt => amount_sent < amount_sent_condition,
FungibleConditionCode::SentLe => amount_sent <= amount_sent_condition,
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum NonfungibleConditionCode {
Sent = 0x10,
NotSent = 0x11,
}
impl NonfungibleConditionCode {
pub fn from_u8(b: u8) -> Option<NonfungibleConditionCode> {
match b {
0x10 => Some(NonfungibleConditionCode::Sent),
0x11 => Some(NonfungibleConditionCode::NotSent),
_ => None,
}
}
pub fn was_sent(nft_sent_condition: &Value, nfts_sent: &[Value]) -> bool {
for asset_sent in nfts_sent.iter() {
if *asset_sent == *nft_sent_condition {
return true;
}
}
false
}
pub fn check(&self, nft_sent_condition: &Value, nfts_sent: &[Value]) -> bool {
match *self {
NonfungibleConditionCode::Sent => {
NonfungibleConditionCode::was_sent(nft_sent_condition, nfts_sent)
}
NonfungibleConditionCode::NotSent => {
!NonfungibleConditionCode::was_sent(nft_sent_condition, nfts_sent)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PostConditionPrincipal {
Origin,
Standard(StacksAddress),
Contract(StacksAddress, ContractName),
}
impl PostConditionPrincipal {
pub fn to_principal_data(&self, origin_principal: &PrincipalData) -> PrincipalData {
match *self {
PostConditionPrincipal::Origin => origin_principal.clone(),
PostConditionPrincipal::Standard(ref addr) => {
PrincipalData::Standard(StandardPrincipalData::from(*addr))
}
PostConditionPrincipal::Contract(ref addr, ref contract_name) => {
PrincipalData::Contract(QualifiedContractIdentifier::new(
StandardPrincipalData::from(*addr),
contract_name.clone(),
))
}
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum PostConditionPrincipalID {
Origin = 0x01,
Standard = 0x02,
Contract = 0x03,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TransactionPostCondition {
STX(PostConditionPrincipal, FungibleConditionCode, u64),
Fungible(
PostConditionPrincipal,
AssetInfo,
FungibleConditionCode,
u64,
),
Nonfungible(
PostConditionPrincipal,
AssetInfo,
Value,
NonfungibleConditionCode,
),
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum TransactionPostConditionMode {
Allow = 0x01, Deny = 0x02, }
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum TransactionVersion {
Mainnet = 0x00,
Testnet = 0x80,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StacksTransaction {
pub version: TransactionVersion,
pub chain_id: u32,
pub auth: TransactionAuth,
pub anchor_mode: TransactionAnchorMode,
pub post_condition_mode: TransactionPostConditionMode,
pub post_conditions: Vec<TransactionPostCondition>,
pub payload: TransactionPayload,
}
impl StacksTransaction {
pub fn new(
version: TransactionVersion,
auth: TransactionAuth,
payload: TransactionPayload,
) -> StacksTransaction {
let anchor_mode = TransactionAnchorMode::Any;
StacksTransaction {
version,
chain_id: 0,
auth,
anchor_mode,
post_condition_mode: TransactionPostConditionMode::Deny,
post_conditions: vec![],
payload,
}
}
pub fn get_tx_fee(&self) -> u64 {
self.auth.get_tx_fee()
}
pub fn set_tx_fee(&mut self, tx_fee: u64) {
self.auth.set_tx_fee(tx_fee);
}
pub fn get_origin_nonce(&self) -> u64 {
self.auth.get_origin_nonce()
}
pub fn get_sponsor_nonce(&self) -> Option<u64> {
self.auth.get_sponsor_nonce()
}
pub fn set_origin_nonce(&mut self, n: u64) {
self.auth.set_origin_nonce(n);
}
pub fn set_sponsor_nonce(&mut self, n: u64) -> Result<(), CodecError> {
self.auth.set_sponsor_nonce(n)
}
pub fn set_anchor_mode(&mut self, anchor_mode: TransactionAnchorMode) {
self.anchor_mode = anchor_mode;
}
pub fn set_post_condition_mode(&mut self, postcond_mode: TransactionPostConditionMode) {
self.post_condition_mode = postcond_mode;
}
pub fn add_post_condition(&mut self, post_condition: TransactionPostCondition) {
self.post_conditions.push(post_condition);
}
pub fn txid(&self) -> Txid {
let mut bytes = vec![];
self.consensus_serialize(&mut bytes)
.expect("BUG: failed to serialize to a vec");
Txid::from_stacks_tx(&bytes)
}
pub fn borrow_auth(&mut self) -> &mut TransactionAuth {
&mut self.auth
}
pub fn auth(&self) -> &TransactionAuth {
&self.auth
}
fn sign_begin(&self) -> Txid {
let mut tx = self.clone();
tx.auth = tx.auth.into_initial_sighash_auth();
tx.txid()
}
fn verify_begin(&self) -> Txid {
let mut tx = self.clone();
tx.auth = tx.auth.into_initial_sighash_auth();
tx.txid()
}
fn sign_and_append(
condition: &mut TransactionSpendingCondition,
cur_sighash: &Txid,
auth_flag: &TransactionAuthFlags,
privk: &Secp256k1PrivateKey,
) -> Result<Txid, CodecError> {
let (next_sig, next_sighash) = TransactionSpendingCondition::next_signature(
cur_sighash,
auth_flag,
condition.tx_fee(),
condition.nonce(),
privk,
)?;
match condition {
TransactionSpendingCondition::Multisig(ref mut cond) => {
cond.push_signature(
if privk.compress_public() {
TransactionPublicKeyEncoding::Compressed
} else {
TransactionPublicKeyEncoding::Uncompressed
},
next_sig,
);
Ok(next_sighash)
}
TransactionSpendingCondition::Singlesig(ref mut cond) => {
cond.set_signature(next_sig);
Ok(next_sighash)
}
}
}
fn pop_auth_field(
condition: &mut TransactionSpendingCondition,
) -> Option<TransactionAuthField> {
match condition {
TransactionSpendingCondition::Multisig(ref mut cond) => cond.pop_auth_field(),
TransactionSpendingCondition::Singlesig(ref mut cond) => cond.pop_signature(),
}
}
fn append_pubkey(
condition: &mut TransactionSpendingCondition,
pubkey: &Secp256k1PublicKey,
) -> Result<(), CodecError> {
match condition {
TransactionSpendingCondition::Multisig(ref mut cond) => {
#[allow(clippy::clone_on_copy)]
cond.push_public_key(pubkey.clone());
Ok(())
}
_ => Err(CodecError::SigningError(
"Not a multisig condition".to_string(),
)),
}
}
pub fn sign_next_origin(
&mut self,
cur_sighash: &Txid,
privk: &Secp256k1PrivateKey,
) -> Result<Txid, CodecError> {
let next_sighash = match self.auth {
TransactionAuth::Standard(ref mut origin_condition) => {
StacksTransaction::sign_and_append(
origin_condition,
cur_sighash,
&TransactionAuthFlags::AuthStandard,
privk,
)?
}
TransactionAuth::Sponsored(ref mut origin_condition, _) => {
StacksTransaction::sign_and_append(
origin_condition,
cur_sighash,
&TransactionAuthFlags::AuthStandard,
privk,
)?
}
};
Ok(next_sighash)
}
pub fn append_next_origin(&mut self, pubk: &Secp256k1PublicKey) -> Result<(), CodecError> {
match self.auth {
TransactionAuth::Standard(ref mut origin_condition) => {
StacksTransaction::append_pubkey(origin_condition, pubk)
}
TransactionAuth::Sponsored(ref mut origin_condition, _) => {
StacksTransaction::append_pubkey(origin_condition, pubk)
}
}
}
pub fn sign_next_sponsor(
&mut self,
cur_sighash: &Txid,
privk: &Secp256k1PrivateKey,
) -> Result<Txid, CodecError> {
let next_sighash = match self.auth {
TransactionAuth::Standard(_) => {
return Err(CodecError::SigningError(
"Cannot sign standard authorization with a sponsoring private key".to_string(),
));
}
TransactionAuth::Sponsored(_, ref mut sponsor_condition) => {
StacksTransaction::sign_and_append(
sponsor_condition,
cur_sighash,
&TransactionAuthFlags::AuthSponsored,
privk,
)?
}
};
Ok(next_sighash)
}
pub fn append_next_sponsor(&mut self, pubk: &Secp256k1PublicKey) -> Result<(), CodecError> {
match self.auth {
TransactionAuth::Standard(_) => Err(CodecError::SigningError(
"Cannot appned a public key to the sponsor of a standard auth condition"
.to_string(),
)),
TransactionAuth::Sponsored(_, ref mut sponsor_condition) => {
StacksTransaction::append_pubkey(sponsor_condition, pubk)
}
}
}
pub fn verify(&self) -> Result<(), CodecError> {
self.auth.verify(&self.verify_begin())
}
pub fn verify_origin(&self) -> Result<Txid, CodecError> {
self.auth.verify_origin(&self.verify_begin())
}
pub fn origin_address(&self) -> StacksAddress {
match (&self.version, &self.auth) {
(&TransactionVersion::Mainnet, TransactionAuth::Standard(origin_condition)) => {
origin_condition.address_mainnet()
}
(&TransactionVersion::Testnet, TransactionAuth::Standard(origin_condition)) => {
origin_condition.address_testnet()
}
(
&TransactionVersion::Mainnet,
TransactionAuth::Sponsored(origin_condition, _unused),
) => origin_condition.address_mainnet(),
(
&TransactionVersion::Testnet,
TransactionAuth::Sponsored(origin_condition, _unused),
) => origin_condition.address_testnet(),
}
}
pub fn sponsor_address(&self) -> Option<StacksAddress> {
match (&self.version, &self.auth) {
(&TransactionVersion::Mainnet, TransactionAuth::Standard(_unused)) => None,
(&TransactionVersion::Testnet, TransactionAuth::Standard(_unused)) => None,
(
&TransactionVersion::Mainnet,
TransactionAuth::Sponsored(_unused, sponsor_condition),
) => Some(sponsor_condition.address_mainnet()),
(
&TransactionVersion::Testnet,
TransactionAuth::Sponsored(_unused, sponsor_condition),
) => Some(sponsor_condition.address_testnet()),
}
}
pub fn get_origin(&self) -> TransactionSpendingCondition {
self.auth.origin().clone()
}
pub fn get_payer(&self) -> TransactionSpendingCondition {
match self.auth.sponsor() {
Some(tsc) => tsc.clone(),
None => self.auth.origin().clone(),
}
}
pub fn is_mainnet(&self) -> bool {
matches!(self.version, TransactionVersion::Mainnet)
}
pub fn tx_len(&self) -> u64 {
let mut tx_bytes = vec![];
self.consensus_serialize(&mut tx_bytes)
.expect("BUG: Failed to serialize a transaction object");
tx_bytes.len() as u64
}
pub fn consensus_deserialize_with_len<R: Read>(
fd: &mut R,
) -> Result<(StacksTransaction, u64), CodecError> {
let mut bound_read = BoundReader::from_reader(fd, MAX_TRANSACTION_LEN.into());
let fd = &mut bound_read;
let version_u8: u8 = read_next(fd)?;
let chain_id: u32 = read_next(fd)?;
let auth: TransactionAuth = read_next(fd)?;
let anchor_mode_u8: u8 = read_next(fd)?;
let post_condition_mode_u8: u8 = read_next(fd)?;
let post_conditions: Vec<TransactionPostCondition> = read_next(fd)?;
let payload: TransactionPayload = read_next(fd)?;
let version = if (version_u8 & 0x80) == 0 {
TransactionVersion::Mainnet
} else {
TransactionVersion::Testnet
};
let anchor_mode = match anchor_mode_u8 {
x if x == TransactionAnchorMode::OffChainOnly as u8 => {
TransactionAnchorMode::OffChainOnly
}
x if x == TransactionAnchorMode::OnChainOnly as u8 => {
TransactionAnchorMode::OnChainOnly
}
x if x == TransactionAnchorMode::Any as u8 => TransactionAnchorMode::Any,
_ => {
return Err(CodecError::DeserializeError(format!(
"Failed to parse transaction: invalid anchor mode {}",
anchor_mode_u8
)));
}
};
let post_condition_mode = match post_condition_mode_u8 {
x if x == TransactionPostConditionMode::Allow as u8 => {
TransactionPostConditionMode::Allow
}
x if x == TransactionPostConditionMode::Deny as u8 => {
TransactionPostConditionMode::Deny
}
_ => {
return Err(CodecError::DeserializeError(format!(
"Failed to parse transaction: invalid post-condition mode {}",
post_condition_mode_u8
)));
}
};
Ok((
StacksTransaction {
version,
chain_id,
auth,
anchor_mode,
post_condition_mode,
post_conditions,
payload,
},
fd.num_read(),
))
}
}
#[derive(Serialize, Deserialize)]
pub struct Txid(pub [u8; 32]);
impl_array_newtype!(Txid, u8, 32);
impl_array_hexstring_fmt!(Txid);
impl_byte_array_newtype!(Txid, u8, 32);
impl Txid {
pub fn from_stacks_tx(txdata: &[u8]) -> Txid {
let h = Sha512Trunc256Sum::from_data(txdata);
let mut bytes = [0u8; 32];
bytes.copy_from_slice(h.as_bytes());
Txid(bytes)
}
pub fn from_sighash_bytes(txdata: &[u8]) -> Txid {
Txid::from_stacks_tx(txdata)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct StacksTransactionSigner {
pub tx: StacksTransaction,
pub sighash: Txid,
origin_done: bool,
check_oversign: bool,
check_overlap: bool,
}
impl StacksTransactionSigner {
pub fn new(tx: &StacksTransaction) -> StacksTransactionSigner {
StacksTransactionSigner {
tx: tx.clone(),
sighash: tx.sign_begin(),
origin_done: false,
check_oversign: true,
check_overlap: true,
}
}
pub fn new_sponsor(
tx: &StacksTransaction,
spending_condition: TransactionSpendingCondition,
) -> Result<StacksTransactionSigner, CodecError> {
if !tx.auth.is_sponsored() {
return Err(CodecError::GenericError(
"IncompatibleSpendingConditionError".into(),
));
}
let mut new_tx = tx.clone();
new_tx.auth.set_sponsor(spending_condition)?;
let origin_sighash = new_tx.verify_origin()?;
Ok(StacksTransactionSigner {
tx: new_tx,
sighash: origin_sighash,
origin_done: true,
check_oversign: true,
check_overlap: true,
})
}
pub fn resume(&mut self, tx: &StacksTransaction) {
self.tx = tx.clone()
}
pub fn disable_checks(&mut self) {
self.check_oversign = false;
self.check_overlap = false;
}
pub fn sign_origin(&mut self, privk: &Secp256k1PrivateKey) -> Result<(), CodecError> {
if self.check_overlap && self.origin_done {
return Err(CodecError::SigningError(
"Cannot sign origin after sponsor key".to_string(),
));
}
match self.tx.auth {
TransactionAuth::Standard(ref origin_condition) => {
if self.check_oversign
&& origin_condition.num_signatures() >= origin_condition.signatures_required()
{
return Err(CodecError::SigningError(
"Origin would have too many signatures".to_string(),
));
}
}
TransactionAuth::Sponsored(ref origin_condition, _) => {
if self.check_oversign
&& origin_condition.num_signatures() >= origin_condition.signatures_required()
{
return Err(CodecError::SigningError(
"Origin would have too many signatures".to_string(),
));
}
}
}
let next_sighash = self.tx.sign_next_origin(&self.sighash, privk)?;
self.sighash = next_sighash;
Ok(())
}
pub fn append_origin(&mut self, pubk: &Secp256k1PublicKey) -> Result<(), CodecError> {
if self.check_overlap && self.origin_done {
return Err(CodecError::SigningError(
"Cannot append public key to origin after sponsor key".to_string(),
));
}
self.tx.append_next_origin(pubk)
}
pub fn sign_sponsor(&mut self, privk: &Secp256k1PrivateKey) -> Result<(), CodecError> {
match self.tx.auth {
TransactionAuth::Sponsored(_, ref sponsor_condition) => {
if self.check_oversign
&& sponsor_condition.num_signatures() >= sponsor_condition.signatures_required()
{
return Err(CodecError::SigningError(
"Sponsor would have too many signatures".to_string(),
));
}
}
TransactionAuth::Standard(_) => todo!(),
}
let next_sighash = self.tx.sign_next_sponsor(&self.sighash, privk)?;
self.sighash = next_sighash;
self.origin_done = true;
Ok(())
}
pub fn append_sponsor(&mut self, pubk: &Secp256k1PublicKey) -> Result<(), CodecError> {
self.tx.append_next_sponsor(pubk)
}
pub fn pop_origin_auth_field(&mut self) -> Option<TransactionAuthField> {
match self.tx.auth {
TransactionAuth::Standard(ref mut origin_condition) => {
StacksTransaction::pop_auth_field(origin_condition)
}
TransactionAuth::Sponsored(ref mut origin_condition, _) => {
StacksTransaction::pop_auth_field(origin_condition)
}
}
}
pub fn pop_sponsor_auth_field(&mut self) -> Option<TransactionAuthField> {
match self.tx.auth {
TransactionAuth::Sponsored(_, ref mut sponsor_condition) => {
StacksTransaction::pop_auth_field(sponsor_condition)
}
_ => None,
}
}
pub fn complete(&self) -> bool {
match self.tx.auth {
TransactionAuth::Standard(ref origin_condition) => {
origin_condition.num_signatures() >= origin_condition.signatures_required()
}
TransactionAuth::Sponsored(ref origin_condition, ref sponsored_condition) => {
origin_condition.num_signatures() >= origin_condition.signatures_required()
&& sponsored_condition.num_signatures()
>= sponsored_condition.signatures_required()
&& (self.origin_done || !self.check_overlap)
}
}
}
pub fn get_tx_incomplete(&self) -> StacksTransaction {
self.tx.clone()
}
pub fn get_tx(&self) -> Option<StacksTransaction> {
if self.complete() {
Some(self.tx.clone())
} else {
None
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StacksBlock {
pub header: StacksBlockHeader,
pub txs: Vec<StacksTransaction>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StacksMicroblock {
pub header: StacksMicroblockHeader,
pub txs: Vec<StacksTransaction>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct StacksBlockHeader {
pub version: u8,
pub total_work: StacksWorkScore, pub proof: String,
pub parent_block: BlockHeaderHash, pub parent_microblock: BlockHeaderHash,
pub parent_microblock_sequence: u16,
pub tx_merkle_root: Sha512Trunc256Sum,
pub state_index_root: TrieHash,
pub microblock_pubkey_hash: Hash160, }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StacksMicroblockHeader {
pub version: u8,
pub sequence: u16,
pub prev_block: BlockHeaderHash,
pub tx_merkle_root: Sha512Trunc256Sum,
pub signature: MessageSignature,
}
impl StacksMessageCodec for StacksMicroblockHeader {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
self.serialize(fd, false)
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<StacksMicroblockHeader, CodecError> {
let version: u8 = read_next(fd)?;
let sequence: u16 = read_next(fd)?;
let prev_block: BlockHeaderHash = read_next(fd)?;
let tx_merkle_root: Sha512Trunc256Sum = read_next(fd)?;
let signature: MessageSignature = read_next(fd)?;
Ok(StacksMicroblockHeader {
version,
sequence,
prev_block,
tx_merkle_root,
signature,
})
}
}
impl StacksMicroblockHeader {
fn serialize<W: Write>(&self, fd: &mut W, empty_sig: bool) -> Result<(), CodecError> {
write_next(fd, &self.version)?;
write_next(fd, &self.sequence)?;
write_next(fd, &self.prev_block)?;
write_next(fd, &self.tx_merkle_root)?;
if empty_sig {
write_next(fd, &MessageSignature::empty())?;
} else {
write_next(fd, &self.signature)?;
}
Ok(())
}
}
pub const MINER_BLOCK_CONSENSUS_HASH: ConsensusHash = ConsensusHash([1u8; 20]);
pub const MINER_BLOCK_HEADER_HASH: BlockHeaderHash = BlockHeaderHash([1u8; 32]);
#[derive(Debug, Clone, PartialEq)]
pub struct StacksHeaderInfo {
pub anchored_header: StacksBlockHeader,
pub microblock_tail: Option<StacksMicroblockHeader>,
pub stacks_block_height: u64,
pub index_root: TrieHash,
pub consensus_hash: ConsensusHash,
pub burn_header_hash: BurnchainHeaderHash,
pub burn_header_height: u32,
pub burn_header_timestamp: u64,
pub anchored_block_size: u64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MinerReward {
pub address: StacksAddress,
pub recipient: PrincipalData,
pub coinbase: u128,
pub tx_fees_anchored: u128,
pub tx_fees_streamed_produced: u128,
pub tx_fees_streamed_confirmed: u128,
pub vtxindex: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MinerRewardInfo {
pub from_block_consensus_hash: ConsensusHash,
pub from_stacks_block_hash: BlockHeaderHash,
pub from_parent_block_consensus_hash: ConsensusHash,
pub from_parent_stacks_block_hash: BlockHeaderHash,
}
pub const MAX_EPOCH_SIZE: u32 = 2 * 1024 * 1024;
pub const MAX_MICROBLOCK_SIZE: u32 = 65536;
pub fn build_contrat_call_transaction(
contract_id: String,
function_name: String,
args: Vec<Value>,
nonce: u64,
fee: u64,
sender_secret_key: &[u8],
) -> StacksTransaction {
let contract_id =
QualifiedContractIdentifier::parse(&contract_id).expect("Contract identifier invalid");
let payload = TransactionContractCall {
address: contract_id.issuer.into(),
contract_name: contract_id.name,
function_name: function_name.try_into().unwrap(),
function_args: args,
};
let secret_key = Secp256k1PrivateKey::from_slice(sender_secret_key).unwrap();
let mut public_key = Secp256k1PublicKey::from_private(&secret_key);
public_key.set_compressed(true);
let anchor_mode = TransactionAnchorMode::Any;
let signer_addr =
StacksAddress::from_public_keys(0, &AddressHashMode::SerializeP2PKH, 1, &vec![public_key])
.unwrap();
let spending_condition = TransactionSpendingCondition::Singlesig(SinglesigSpendingCondition {
signer: signer_addr.bytes,
nonce,
tx_fee: fee,
hash_mode: SinglesigHashMode::P2PKH,
key_encoding: TransactionPublicKeyEncoding::Compressed,
signature: MessageSignature::empty(),
});
let auth = TransactionAuth::Standard(spending_condition);
let unsigned_tx = StacksTransaction {
version: TransactionVersion::Testnet,
chain_id: 0x80000000, auth,
anchor_mode,
post_condition_mode: TransactionPostConditionMode::Allow,
post_conditions: vec![],
payload: TransactionPayload::ContractCall(payload),
};
let mut unsigned_tx_bytes = vec![];
unsigned_tx
.consensus_serialize(&mut unsigned_tx_bytes)
.expect("FATAL: invalid transaction");
let mut tx_signer = StacksTransactionSigner::new(&unsigned_tx);
tx_signer.sign_origin(&secret_key).unwrap();
tx_signer.get_tx().unwrap()
}
impl StacksMessageCodec for TransactionContractCall {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
write_next(fd, &self.address)?;
write_next(fd, &self.contract_name)?;
write_next(fd, &self.function_name)?;
write_next(fd, &self.function_args)?;
Ok(())
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<TransactionContractCall, CodecError> {
let address: StacksAddress = read_next(fd)?;
let contract_name: ContractName = read_next(fd)?;
let function_name: ClarityName = read_next(fd)?;
let function_args: Vec<Value> = {
let mut bound_read = BoundReader::from_reader(fd, MAX_TRANSACTION_LEN as u64);
read_next(&mut bound_read)
}?;
if !StacksString::from(function_name.clone()).is_clarity_variable() {
return Err(CodecError::DeserializeError(
"Failed to parse transaction: invalid function name -- not a Clarity variable"
.to_string(),
));
}
Ok(TransactionContractCall {
address,
contract_name,
function_name,
function_args,
})
}
}
impl StacksMessageCodec for TransactionSmartContract {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
write_next(fd, &self.name)?;
write_next(fd, &self.code_body)?;
Ok(())
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<TransactionSmartContract, CodecError> {
let name: ContractName = read_next(fd)?;
let code_body: StacksString = read_next(fd)?;
Ok(TransactionSmartContract { name, code_body })
}
}
fn clarity_version_consensus_serialize<W: Write>(
version: &ClarityVersion,
fd: &mut W,
) -> Result<(), CodecError> {
match *version {
ClarityVersion::Clarity1 => write_next(fd, &1u8)?,
ClarityVersion::Clarity2 => write_next(fd, &2u8)?,
}
Ok(())
}
fn clarity_version_consensus_deserialize<R: Read>(
fd: &mut R,
) -> Result<ClarityVersion, CodecError> {
let version_byte: u8 = read_next(fd)?;
match version_byte {
1u8 => Ok(ClarityVersion::Clarity1),
2u8 => Ok(ClarityVersion::Clarity2),
_ => Err(CodecError::DeserializeError(format!(
"Unrecognized ClarityVersion byte {}",
&version_byte
))),
}
}
impl StacksMessageCodec for TransactionPayload {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
match *self {
TransactionPayload::TokenTransfer(ref address, ref amount, ref memo) => {
write_next(fd, &(TransactionPayloadID::TokenTransfer as u8))?;
write_next(fd, address)?;
write_next(fd, amount)?;
write_next(fd, memo)?;
}
TransactionPayload::ContractCall(ref cc) => {
write_next(fd, &(TransactionPayloadID::ContractCall as u8))?;
cc.consensus_serialize(fd)?;
}
TransactionPayload::SmartContract(ref sc, ref version_opt) => {
if let Some(version) = version_opt {
write_next(fd, &(TransactionPayloadID::VersionedSmartContract as u8))?;
clarity_version_consensus_serialize(version, fd)?;
sc.consensus_serialize(fd)?;
} else {
write_next(fd, &(TransactionPayloadID::SmartContract as u8))?;
sc.consensus_serialize(fd)?;
}
}
_ => {
unreachable!()
}
}
Ok(())
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<TransactionPayload, CodecError> {
let type_id: u8 = read_next(fd)?;
let payload = match type_id {
x if x == TransactionPayloadID::TokenTransfer as u8 => {
let principal = read_next(fd)?;
let amount = read_next(fd)?;
let memo = read_next(fd)?;
TransactionPayload::TokenTransfer(principal, amount, memo)
}
x if x == TransactionPayloadID::ContractCall as u8 => {
let payload: TransactionContractCall = read_next(fd)?;
TransactionPayload::ContractCall(payload)
}
x if x == TransactionPayloadID::SmartContract as u8 => {
let payload: TransactionSmartContract = read_next(fd)?;
TransactionPayload::SmartContract(payload, None)
}
x if x == TransactionPayloadID::VersionedSmartContract as u8 => {
let version = clarity_version_consensus_deserialize(fd)?;
let payload: TransactionSmartContract = read_next(fd)?;
TransactionPayload::SmartContract(payload, Some(version))
}
x if x == TransactionPayloadID::PoisonMicroblock as u8 => {
let micrblock1: StacksMicroblockHeader = read_next(fd)?;
let micrblock2: StacksMicroblockHeader = read_next(fd)?;
TransactionPayload::PoisonMicroblock(micrblock1, micrblock2)
}
x if x == TransactionPayloadID::Coinbase as u8 => {
let payload: CoinbasePayload = read_next(fd)?;
TransactionPayload::Coinbase(payload, None, None)
}
x if x == TransactionPayloadID::CoinbaseToAltRecipient as u8 => {
let payload: CoinbasePayload = read_next(fd)?;
let principal_value: Value = read_next(fd)?;
let recipient = match principal_value {
Value::Principal(recipient_principal) => recipient_principal,
_ => {
return Err(CodecError::DeserializeError("Failed to parse coinbase transaction -- did not receive a recipient principal value".to_string()));
}
};
TransactionPayload::Coinbase(payload, Some(recipient), None)
}
x if x == TransactionPayloadID::NakamotoCoinbase as u8 => {
let payload: CoinbasePayload = read_next(fd)?;
let principal_value_opt: Value = read_next(fd)?;
let recipient_opt = if let Value::Optional(optional_data) = principal_value_opt {
if let Some(principal_value) = optional_data.data {
if let Value::Principal(recipient_principal) = *principal_value {
Some(recipient_principal)
} else {
None
}
} else {
None
}
} else {
return Err(CodecError::DeserializeError("Failed to parse nakamoto coinbase transaction -- did not receive an optional recipient principal value".to_string()));
};
let vrf_proof: VRFProof = read_next(fd)?;
TransactionPayload::Coinbase(payload, recipient_opt, Some(vrf_proof))
}
x if x == TransactionPayloadID::TenureChange as u8 => {
let payload: TenureChangePayload = read_next(fd)?;
TransactionPayload::TenureChange(payload)
}
_ => {
return Err(CodecError::DeserializeError(format!(
"Failed to parse transaction -- unknown payload ID {}",
type_id
)));
}
};
Ok(payload)
}
}
impl StacksMessageCodec for AssetInfo {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
write_next(fd, &self.contract_address)?;
write_next(fd, &self.contract_name)?;
write_next(fd, &self.asset_name)?;
Ok(())
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<AssetInfo, CodecError> {
let contract_address: StacksAddress = read_next(fd)?;
let contract_name: ContractName = read_next(fd)?;
let asset_name: ClarityName = read_next(fd)?;
Ok(AssetInfo {
contract_address,
contract_name,
asset_name,
})
}
}
impl StacksMessageCodec for PostConditionPrincipal {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
match *self {
PostConditionPrincipal::Origin => {
write_next(fd, &(PostConditionPrincipalID::Origin as u8))?;
}
PostConditionPrincipal::Standard(ref address) => {
write_next(fd, &(PostConditionPrincipalID::Standard as u8))?;
write_next(fd, address)?;
}
PostConditionPrincipal::Contract(ref address, ref contract_name) => {
write_next(fd, &(PostConditionPrincipalID::Contract as u8))?;
write_next(fd, address)?;
write_next(fd, contract_name)?;
}
}
Ok(())
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<PostConditionPrincipal, CodecError> {
let principal_id: u8 = read_next(fd)?;
let principal = match principal_id {
x if x == PostConditionPrincipalID::Origin as u8 => PostConditionPrincipal::Origin,
x if x == PostConditionPrincipalID::Standard as u8 => {
let addr: StacksAddress = read_next(fd)?;
PostConditionPrincipal::Standard(addr)
}
x if x == PostConditionPrincipalID::Contract as u8 => {
let addr: StacksAddress = read_next(fd)?;
let contract_name: ContractName = read_next(fd)?;
PostConditionPrincipal::Contract(addr, contract_name)
}
_ => {
return Err(CodecError::DeserializeError(format!(
"Failed to parse transaction: unknown post condition principal ID {}",
principal_id
)));
}
};
Ok(principal)
}
}
impl StacksMessageCodec for TransactionPostCondition {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
match *self {
TransactionPostCondition::STX(ref principal, ref fungible_condition, ref amount) => {
write_next(fd, &(AssetInfoID::STX as u8))?;
write_next(fd, principal)?;
write_next(fd, &(*fungible_condition as u8))?;
write_next(fd, amount)?;
}
TransactionPostCondition::Fungible(
ref principal,
ref asset_info,
ref fungible_condition,
ref amount,
) => {
write_next(fd, &(AssetInfoID::FungibleAsset as u8))?;
write_next(fd, principal)?;
write_next(fd, asset_info)?;
write_next(fd, &(*fungible_condition as u8))?;
write_next(fd, amount)?;
}
TransactionPostCondition::Nonfungible(
ref principal,
ref asset_info,
ref asset_value,
ref nonfungible_condition,
) => {
write_next(fd, &(AssetInfoID::NonfungibleAsset as u8))?;
write_next(fd, principal)?;
write_next(fd, asset_info)?;
write_next(fd, asset_value)?;
write_next(fd, &(*nonfungible_condition as u8))?;
}
};
Ok(())
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<TransactionPostCondition, CodecError> {
let asset_info_id: u8 = read_next(fd)?;
let postcond = match asset_info_id {
x if x == AssetInfoID::STX as u8 => {
let principal: PostConditionPrincipal = read_next(fd)?;
let condition_u8: u8 = read_next(fd)?;
let amount: u64 = read_next(fd)?;
let condition_code = FungibleConditionCode::from_u8(condition_u8).ok_or(
CodecError::DeserializeError(format!(
"Failed to parse transaction: Failed to parse STX fungible condition code {}",
condition_u8
)),
)?;
TransactionPostCondition::STX(principal, condition_code, amount)
}
x if x == AssetInfoID::FungibleAsset as u8 => {
let principal: PostConditionPrincipal = read_next(fd)?;
let asset: AssetInfo = read_next(fd)?;
let condition_u8: u8 = read_next(fd)?;
let amount: u64 = read_next(fd)?;
let condition_code = FungibleConditionCode::from_u8(condition_u8).ok_or(
CodecError::DeserializeError(format!(
"Failed to parse transaction: Failed to parse FungibleAsset condition code {}",
condition_u8
)),
)?;
TransactionPostCondition::Fungible(principal, asset, condition_code, amount)
}
x if x == AssetInfoID::NonfungibleAsset as u8 => {
let principal: PostConditionPrincipal = read_next(fd)?;
let asset: AssetInfo = read_next(fd)?;
let asset_value: Value = read_next(fd)?;
let condition_u8: u8 = read_next(fd)?;
let condition_code = NonfungibleConditionCode::from_u8(condition_u8)
.ok_or(CodecError::DeserializeError(format!(
"Failed to parse transaction: Failed to parse NonfungibleAsset condition code {}",
condition_u8
)))?;
TransactionPostCondition::Nonfungible(principal, asset, asset_value, condition_code)
}
_ => {
return Err(CodecError::DeserializeError(format!(
"Failed to parse transaction: unknown asset info ID {}",
asset_info_id
)));
}
};
Ok(postcond)
}
}
impl StacksMessageCodec for TransactionAuth {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
match *self {
TransactionAuth::Standard(ref origin_condition) => {
write_next(fd, &(TransactionAuthFlags::AuthStandard as u8))?;
write_next(fd, origin_condition)?;
}
TransactionAuth::Sponsored(ref origin_condition, ref sponsor_condition) => {
write_next(fd, &(TransactionAuthFlags::AuthSponsored as u8))?;
write_next(fd, origin_condition)?;
write_next(fd, sponsor_condition)?;
}
}
Ok(())
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<TransactionAuth, CodecError> {
let type_id: u8 = read_next(fd)?;
let auth = match type_id {
x if x == TransactionAuthFlags::AuthStandard as u8 => {
let origin_auth: TransactionSpendingCondition = read_next(fd)?;
TransactionAuth::Standard(origin_auth)
}
x if x == TransactionAuthFlags::AuthSponsored as u8 => {
let origin_auth: TransactionSpendingCondition = read_next(fd)?;
let sponsor_auth: TransactionSpendingCondition = read_next(fd)?;
TransactionAuth::Sponsored(origin_auth, sponsor_auth)
}
_ => {
return Err(CodecError::DeserializeError(format!(
"Failed to parse transaction authorization: unrecognized auth flags {}",
type_id
)));
}
};
Ok(auth)
}
}
impl StacksMessageCodec for TransactionSpendingCondition {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
match *self {
TransactionSpendingCondition::Singlesig(ref data) => {
data.consensus_serialize(fd)?;
}
TransactionSpendingCondition::Multisig(ref data) => {
data.consensus_serialize(fd)?;
}
}
Ok(())
}
fn consensus_deserialize<R: Read>(
fd: &mut R,
) -> Result<TransactionSpendingCondition, CodecError> {
let hash_mode_u8: u8 = read_next(fd)?;
let peek_buf = [hash_mode_u8];
let mut rrd = peek_buf.chain(fd);
let cond = {
if SinglesigHashMode::from_u8(hash_mode_u8).is_some() {
let cond = SinglesigSpendingCondition::consensus_deserialize(&mut rrd)?;
TransactionSpendingCondition::Singlesig(cond)
} else if MultisigHashMode::from_u8(hash_mode_u8).is_some() {
let cond = MultisigSpendingCondition::consensus_deserialize(&mut rrd)?;
TransactionSpendingCondition::Multisig(cond)
} else {
return Err(CodecError::DeserializeError(format!(
"Failed to parse spending condition: invalid hash mode {}",
hash_mode_u8
)));
}
};
Ok(cond)
}
}
impl StacksMessageCodec for SinglesigSpendingCondition {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
write_next(fd, &(self.hash_mode.clone() as u8))?;
write_next(fd, &self.signer)?;
write_next(fd, &self.nonce)?;
write_next(fd, &self.tx_fee)?;
write_next(fd, &(self.key_encoding as u8))?;
write_next(fd, &self.signature)?;
Ok(())
}
fn consensus_deserialize<R: Read>(
fd: &mut R,
) -> Result<SinglesigSpendingCondition, CodecError> {
let hash_mode_u8: u8 = read_next(fd)?;
let hash_mode = SinglesigHashMode::from_u8(hash_mode_u8).ok_or(
CodecError::DeserializeError(format!(
"Failed to parse singlesig spending condition: unknown hash mode {}",
hash_mode_u8
)),
)?;
let signer: Hash160 = read_next(fd)?;
let nonce: u64 = read_next(fd)?;
let tx_fee: u64 = read_next(fd)?;
let key_encoding_u8: u8 = read_next(fd)?;
let key_encoding = TransactionPublicKeyEncoding::from_u8(key_encoding_u8).ok_or(
CodecError::DeserializeError(format!(
"Failed to parse singlesig spending condition: unknown key encoding {}",
key_encoding_u8
)),
)?;
let signature: MessageSignature = read_next(fd)?;
if hash_mode == SinglesigHashMode::P2WPKH
&& key_encoding != TransactionPublicKeyEncoding::Compressed
{
return Err(CodecError::DeserializeError("Failed to parse singlesig spending condition: incomaptible hash mode and key encoding".to_string()));
}
Ok(SinglesigSpendingCondition {
signer,
nonce,
tx_fee,
hash_mode,
key_encoding,
signature,
})
}
}
impl StacksMessageCodec for MultisigSpendingCondition {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
write_next(fd, &(self.hash_mode.clone() as u8))?;
write_next(fd, &self.signer)?;
write_next(fd, &self.nonce)?;
write_next(fd, &self.tx_fee)?;
write_next(fd, &self.fields)?;
write_next(fd, &self.signatures_required)?;
Ok(())
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<MultisigSpendingCondition, CodecError> {
let hash_mode_u8: u8 = read_next(fd)?;
let hash_mode = MultisigHashMode::from_u8(hash_mode_u8).ok_or(
CodecError::DeserializeError(format!(
"Failed to parse multisig spending condition: unknown hash mode {}",
hash_mode_u8
)),
)?;
let signer: Hash160 = read_next(fd)?;
let nonce: u64 = read_next(fd)?;
let tx_fee: u64 = read_next(fd)?;
let fields: Vec<TransactionAuthField> = {
let mut bound_read = BoundReader::from_reader(fd, MAX_MESSAGE_LEN as u64);
read_next(&mut bound_read)
}?;
let signatures_required: u16 = read_next(fd)?;
let mut num_sigs_given: u16 = 0;
let mut have_uncompressed = false;
for f in fields.iter() {
match *f {
TransactionAuthField::Signature(ref key_encoding, _) => {
num_sigs_given =
num_sigs_given
.checked_add(1)
.ok_or(CodecError::DeserializeError(
"Failed to parse multisig spending condition: too many signatures"
.to_string(),
))?;
if *key_encoding == TransactionPublicKeyEncoding::Uncompressed {
have_uncompressed = true;
}
}
TransactionAuthField::PublicKey(ref pubk) => {
if !pubk.compressed() {
have_uncompressed = true;
}
}
};
}
if num_sigs_given != signatures_required {
return Err(CodecError::DeserializeError(format!(
"Failed to parse multisig spending condition: got {} sigs, expected {}",
num_sigs_given, signatures_required
)));
}
if have_uncompressed && hash_mode == MultisigHashMode::P2WSH {
return Err(CodecError::DeserializeError(
"Failed to parse multisig spending condition: expected compressed keys only"
.to_string(),
));
}
Ok(MultisigSpendingCondition {
signer,
nonce,
tx_fee,
hash_mode,
fields,
signatures_required,
})
}
}
pub struct StacksPublicKeyBuffer(pub [u8; 33]);
impl_array_newtype!(StacksPublicKeyBuffer, u8, 33);
impl_array_hexstring_fmt!(StacksPublicKeyBuffer);
impl_byte_array_newtype!(StacksPublicKeyBuffer, u8, 33);
impl_byte_array_message_codec!(StacksPublicKeyBuffer, 33);
impl StacksPublicKeyBuffer {
pub fn from_public_key(pubkey: &Secp256k1PublicKey) -> StacksPublicKeyBuffer {
let pubkey_bytes_vec = pubkey.to_bytes_compressed();
let mut pubkey_bytes = [0u8; 33];
pubkey_bytes.copy_from_slice(&pubkey_bytes_vec[..]);
StacksPublicKeyBuffer(pubkey_bytes)
}
pub fn to_public_key(&self) -> Result<Secp256k1PublicKey, CodecError> {
Secp256k1PublicKey::from_slice(&self.0).map_err(|_e_str| {
CodecError::DeserializeError("Failed to decode Stacks public key".to_string())
})
}
}
impl StacksMessageCodec for TransactionAuthField {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
match *self {
TransactionAuthField::PublicKey(ref pubk) => {
let field_id = if pubk.compressed() {
TransactionAuthFieldID::PublicKeyCompressed
} else {
TransactionAuthFieldID::PublicKeyUncompressed
};
let pubkey_buf = StacksPublicKeyBuffer::from_public_key(pubk);
write_next(fd, &(field_id as u8))?;
write_next(fd, &pubkey_buf)?;
}
TransactionAuthField::Signature(ref key_encoding, ref sig) => {
let field_id = if *key_encoding == TransactionPublicKeyEncoding::Compressed {
TransactionAuthFieldID::SignatureCompressed
} else {
TransactionAuthFieldID::SignatureUncompressed
};
write_next(fd, &(field_id as u8))?;
write_next(fd, sig)?;
}
}
Ok(())
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<TransactionAuthField, CodecError> {
let field_id: u8 = read_next(fd)?;
let field = match field_id {
x if x == TransactionAuthFieldID::PublicKeyCompressed as u8 => {
let pubkey_buf: StacksPublicKeyBuffer = read_next(fd)?;
let mut pubkey = pubkey_buf.to_public_key()?;
pubkey.set_compressed(true);
TransactionAuthField::PublicKey(pubkey)
}
x if x == TransactionAuthFieldID::PublicKeyUncompressed as u8 => {
let pubkey_buf: StacksPublicKeyBuffer = read_next(fd)?;
let mut pubkey = pubkey_buf.to_public_key()?;
pubkey.set_compressed(false);
TransactionAuthField::PublicKey(pubkey)
}
x if x == TransactionAuthFieldID::SignatureCompressed as u8 => {
let sig: MessageSignature = read_next(fd)?;
TransactionAuthField::Signature(TransactionPublicKeyEncoding::Compressed, sig)
}
x if x == TransactionAuthFieldID::SignatureUncompressed as u8 => {
let sig: MessageSignature = read_next(fd)?;
TransactionAuthField::Signature(TransactionPublicKeyEncoding::Uncompressed, sig)
}
_ => {
return Err(CodecError::DeserializeError(format!(
"Failed to parse auth field: unkonwn auth field ID {}",
field_id
)));
}
};
Ok(field)
}
}
impl StacksMessageCodec for StacksTransaction {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
write_next(fd, &(self.version as u8))?;
write_next(fd, &self.chain_id)?;
write_next(fd, &self.auth)?;
write_next(fd, &(self.anchor_mode as u8))?;
write_next(fd, &(self.post_condition_mode as u8))?;
write_next(fd, &self.post_conditions)?;
write_next(fd, &self.payload)?;
Ok(())
}
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<StacksTransaction, CodecError> {
StacksTransaction::consensus_deserialize_with_len(fd).map(|(result, _)| result)
}
}