From 30363edf0f3a2deb11658aa6946267399d22be31 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Thu, 26 Oct 2023 17:41:20 +0200 Subject: [PATCH] feat: support for TenureChange transaction types --- src/stacks_tx/deserialize.rs | 81 ++++++++++++++++++++++++++++++++--- src/stacks_tx/neon_encoder.rs | 37 +++++++++++++++- 2 files changed, 112 insertions(+), 6 deletions(-) diff --git a/src/stacks_tx/deserialize.rs b/src/stacks_tx/deserialize.rs index a62f5fc..883c678 100644 --- a/src/stacks_tx/deserialize.rs +++ b/src/stacks_tx/deserialize.rs @@ -378,13 +378,18 @@ impl TransactionPayload { } x if x == TransactionPayloadID::VersionedSmartContract as u8 => { let clarity_version_u8 = fd.read_u8()?; - let clarity_version = ClarityVersion::from_u8(clarity_version_u8).ok_or(format!( - "Failed to parse smart contract Clarity version: unknown value {}", - clarity_version_u8 - ))?; + let clarity_version = + ClarityVersion::from_u8(clarity_version_u8).ok_or(format!( + "Failed to parse smart contract Clarity version: unknown value {}", + clarity_version_u8 + ))?; let payload = TransactionSmartContract::deserialize(fd)?; TransactionPayload::VersionedSmartContract(payload, clarity_version) } + x if x == TransactionPayloadID::TenureChange as u8 => { + let payload = TransactionTenureChange::deserialize(fd)?; + TransactionPayload::TenureChange(payload) + } _ => { return Err(format!( "Failed to parse transaction -- unknown payload ID {}", @@ -428,6 +433,40 @@ impl TransactionSmartContract { } } +impl TransactionTenureChange { + pub fn deserialize(fd: &mut Cursor<&[u8]>) -> Result { + let mut previous_tenure_end = [0u8; 32]; + fd.read_exact(&mut previous_tenure_end)?; + + let previous_tenure_blocks = fd.read_u16::()?; + + let cause_u8: u8 = fd.read_u8()?; + let cause = TenureChangeCause::from_u8(cause_u8).ok_or(format!( + "Failed to parse transaction: invalid tenure change cause {}", + cause_u8 + ))?; + + let mut pubkey_hash = [0u8; 20]; + fd.read_exact(&mut pubkey_hash)?; + + let mut signature = [0u8; 65]; + fd.read_exact(&mut signature)?; + + let signers_len: u32 = fd.read_u32::()?; + let mut signers: Vec = vec![0u8; signers_len as usize]; + fd.read_exact(&mut signers)?; + + Ok(TransactionTenureChange { + previous_tenure_end, + previous_tenure_blocks, + cause, + pubkey_hash, + signature, + signers, + }) + } +} + impl StacksMicroblockHeader { pub fn deserialize(fd: &mut Cursor<&[u8]>) -> Result { let cursor_pos = fd.position() as usize; @@ -622,6 +661,7 @@ pub enum TransactionPayloadID { Coinbase = 4, CoinbaseToAltRecipient = 5, VersionedSmartContract = 6, + TenureChange = 7, } pub enum TransactionPayload { @@ -631,11 +671,42 @@ pub enum TransactionPayload { PoisonMicroblock(StacksMicroblockHeader, StacksMicroblockHeader), Coinbase(CoinbasePayload), CoinbaseToAltRecipient(CoinbasePayload, PrincipalData), - VersionedSmartContract(TransactionSmartContract, ClarityVersion) + VersionedSmartContract(TransactionSmartContract, ClarityVersion), + TenureChange(TransactionTenureChange), } pub struct CoinbasePayload(pub [u8; 32]); +pub struct TransactionTenureChange { + pub previous_tenure_end: [u8; 32], + pub previous_tenure_blocks: u16, + pub cause: TenureChangeCause, + pub pubkey_hash: [u8; 20], + pub signature: [u8; 65], + pub signers: Vec, +} + +#[repr(u8)] +#[derive(PartialEq, Copy, Clone)] +pub enum TenureChangeCause { + BlockFound = 0, + NoBlockFound = 1, + NullMiner = 2, +} + +impl TenureChangeCause { + pub fn from_u8(n: u8) -> Option { + match n { + x if x == TenureChangeCause::BlockFound as u8 => Some(TenureChangeCause::BlockFound), + x if x == TenureChangeCause::NoBlockFound as u8 => { + Some(TenureChangeCause::NoBlockFound) + } + x if x == TenureChangeCause::NullMiner as u8 => Some(TenureChangeCause::NullMiner), + _ => None, + } + } +} + pub struct TransactionSmartContract { pub name: ClarityName, pub code_body: StacksString, diff --git a/src/stacks_tx/neon_encoder.rs b/src/stacks_tx/neon_encoder.rs index 64b7a3d..9d231ec 100644 --- a/src/stacks_tx/neon_encoder.rs +++ b/src/stacks_tx/neon_encoder.rs @@ -19,7 +19,7 @@ use super::deserialize::{ StacksTransaction, StandardPrincipalData, TransactionAuth, TransactionAuthField, TransactionAuthFieldID, TransactionAuthFlags, TransactionContractCall, TransactionPayload, TransactionPayloadID, TransactionPublicKeyEncoding, TransactionSmartContract, - TransactionSpendingCondition, TransactionVersion, + TransactionSpendingCondition, TransactionTenureChange, TransactionVersion, }; struct TxSerializationContext { @@ -516,6 +516,12 @@ impl NeonJsSerialize for TransactionPayload { smart_contract.neon_js_serialize(cx, obj, extra_ctx)?; } + TransactionPayload::TenureChange(ref tenure_change) => { + let type_id = cx.number(TransactionPayloadID::TenureChange as u8); + obj.set(cx, "type_id", type_id)?; + + tenure_change.neon_js_serialize(cx, obj, extra_ctx)?; + } } Ok(()) } @@ -629,6 +635,35 @@ impl NeonJsSerialize for TransactionSmartContract { } } +impl NeonJsSerialize for TransactionTenureChange { + fn neon_js_serialize( + &self, + cx: &mut FunctionContext, + obj: &Handle, + _extra_ctx: &(), + ) -> NeonResult<()> { + let previous_tenure_end = cx.string(encode_hex(&self.previous_tenure_end)); + obj.set(cx, "previous_tenure_end", previous_tenure_end)?; + + let previous_tenure_blocks = cx.number(self.previous_tenure_blocks); + obj.set(cx, "previous_tenure_blocks", previous_tenure_blocks)?; + + let cause = cx.number(self.cause as u8); + obj.set(cx, "cause", cause)?; + + let pubkey_hash = cx.string(encode_hex(&self.pubkey_hash)); + obj.set(cx, "pubkey_hash", pubkey_hash)?; + + let signature = cx.string(encode_hex(&self.signature)); + obj.set(cx, "signature", signature)?; + + let signers = cx.string(encode_hex(&self.signers)); + obj.set(cx, "signers", signers)?; + + Ok(()) + } +} + impl NeonJsSerialize for StacksMicroblockHeader { fn neon_js_serialize( &self,