From cc93873dacc88e5df97d1006394b99744e63009b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20C=C3=A1rdenas?= Date: Thu, 24 Oct 2024 11:50:41 -0600 Subject: [PATCH 1/3] fix: signer pubkey calculation (#665) Fixes the `slot_id` and `slot_version` field sizes so the signer pubkey is calculated correctly --- .../chainhook-sdk/src/indexer/stacks/mod.rs | 4 +- .../chainhook-sdk/src/indexer/stacks/tests.rs | 43 ++++++++++++------- .../indexer/tests/helpers/stacks_events.rs | 29 ------------- 3 files changed, 30 insertions(+), 46 deletions(-) diff --git a/components/chainhook-sdk/src/indexer/stacks/mod.rs b/components/chainhook-sdk/src/indexer/stacks/mod.rs index 21109f4e8..1d6a4f844 100644 --- a/components/chainhook-sdk/src/indexer/stacks/mod.rs +++ b/components/chainhook-sdk/src/indexer/stacks/mod.rs @@ -326,8 +326,8 @@ pub struct NewStackerDbChunksContractId { pub struct NewSignerModifiedSlot { pub sig: String, pub data: String, - pub slot_id: u64, - pub slot_version: u64, + pub slot_id: u32, + pub slot_version: u32, } #[cfg(feature = "stacks-signers")] diff --git a/components/chainhook-sdk/src/indexer/stacks/tests.rs b/components/chainhook-sdk/src/indexer/stacks/tests.rs index e5ae1bf98..493f6fd5e 100644 --- a/components/chainhook-sdk/src/indexer/stacks/tests.rs +++ b/components/chainhook-sdk/src/indexer/stacks/tests.rs @@ -401,36 +401,49 @@ fn into_chainhook_event_rejects_invalid_missing_event() { #[test] #[cfg(feature = "stacks-signers")] -fn stackerdb_chunks_covert_into_signer_messages() { +fn parses_block_response_signer_message() { use chainhook_types::{BlockResponseData, StacksSignerMessage}; - use crate::indexer::tests::helpers::stacks_events::create_new_stackerdb_chunk; + use crate::indexer::stacks::{ + NewSignerModifiedSlot, NewStackerDbChunkIssuerId, NewStackerDbChunkIssuerSlots, + NewStackerDbChunks, NewStackerDbChunksContractId, + }; use super::standardize_stacks_stackerdb_chunks; - let new_chunks = create_new_stackerdb_chunk( - "signers-1-1".to_string(), - "01fc3c06f6e0ae5b13c9bb53763661817e55c8e7f1ecab8b4d4b65b283d2dd39f0099e3ea1e25e765f4f0e1dfb0a432309a16a2ec10940e1a14cb9e9b1cbf27edc".to_string(), - "010074aff146904763a787aa14c614d0dd1fc63b537bdb2fd351cdf881f6db75f986005eb55250597b25acbf99d3dd3c2fa8189046e1b5d21309a44cbaf2b327c09b0159a01ed3f0094bfa9e5f72f5d894e12ce252081eab5396eb8bba137bddfc365b".to_string() - ); + let new_chunks = NewStackerDbChunks { + contract_id: NewStackerDbChunksContractId { + name: "signers-0-1".to_string(), + issuer: ( + NewStackerDbChunkIssuerId(26), + NewStackerDbChunkIssuerSlots(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ), + }, + modified_slots: vec![NewSignerModifiedSlot { + sig: "01060cc1bef9ccfe7139f5240ff5c33c44c83206e851e21b63234a996654f70d750b44d9c76466a5c45515b63183dfcfaefe5877fbd3593859e50d5df39cd469a1".to_string(), + data: "01008f913dd2bcc2cfbd1c82166e0ad99230f76de098a5ba6ee1b15b042c8f67c6f000a1c66742e665e981d10f7a70a5df312c9cba729331129ff1b510e71133d79c0122b25266bf47e8c1c923b4fde0464756ced884030e9983f797c902961fc9b0b10000005d737461636b732d7369676e657220302e302e3120283a646431656265363436303366353464616534383535386135643832643962643838356539376130312c206465627567206275696c642c206c696e7578205b616172636836345d29".to_string(), + slot_id: 1, + slot_version: 11, + }], + }; let parsed_chunk = standardize_stacks_stackerdb_chunks(&new_chunks).unwrap(); assert_eq!(parsed_chunk.len(), 1); let message = &parsed_chunk[0]; - assert_eq!(message.contract, "signers-1-1"); + assert_eq!(message.contract, "signers-0-1"); assert_eq!( message.pubkey, - "0x03c76290f48909b4d49e111d69236a138ce96df3e05f709e425153d99f4fe671b4" + "0x028efa20fa5706567008ebaf48f7ae891342eeb944d96392f719c505c89f84ed8d" ); - assert_eq!(message.sig, "0x01fc3c06f6e0ae5b13c9bb53763661817e55c8e7f1ecab8b4d4b65b283d2dd39f0099e3ea1e25e765f4f0e1dfb0a432309a16a2ec10940e1a14cb9e9b1cbf27edc"); + assert_eq!(message.sig, "0x01060cc1bef9ccfe7139f5240ff5c33c44c83206e851e21b63234a996654f70d750b44d9c76466a5c45515b63183dfcfaefe5877fbd3593859e50d5df39cd469a1"); match &message.message { - StacksSignerMessage::BlockResponse(block_response_data) => match block_response_data { - BlockResponseData::Accepted(block_accepted_response) => { - assert_eq!(block_accepted_response.sig, "0x005eb55250597b25acbf99d3dd3c2fa8189046e1b5d21309a44cbaf2b327c09b0159a01ed3f0094bfa9e5f72f5d894e12ce252081eab5396eb8bba137bddfc365b"); + StacksSignerMessage::BlockResponse(response) => match response { + BlockResponseData::Accepted(accepted) => { + assert_eq!(accepted.sig, "0x00a1c66742e665e981d10f7a70a5df312c9cba729331129ff1b510e71133d79c0122b25266bf47e8c1c923b4fde0464756ced884030e9983f797c902961fc9b0b1"); assert_eq!( - block_accepted_response.signer_signature_hash, - "0x74aff146904763a787aa14c614d0dd1fc63b537bdb2fd351cdf881f6db75f986" + accepted.signer_signature_hash, + "0x8f913dd2bcc2cfbd1c82166e0ad99230f76de098a5ba6ee1b15b042c8f67c6f0" ); } _ => assert!(false), diff --git a/components/chainhook-sdk/src/indexer/tests/helpers/stacks_events.rs b/components/chainhook-sdk/src/indexer/tests/helpers/stacks_events.rs index 6f8386861..4ca977bc5 100644 --- a/components/chainhook-sdk/src/indexer/tests/helpers/stacks_events.rs +++ b/components/chainhook-sdk/src/indexer/tests/helpers/stacks_events.rs @@ -119,32 +119,3 @@ pub fn create_new_event_from_stacks_event(event: StacksTransactionEventPayload) contract_event, } } - -#[cfg(feature = "stacks-signers")] -pub fn create_new_stackerdb_chunk( - contract_name: String, - slot_sig: String, - slot_data: String, -) -> crate::indexer::stacks::NewStackerDbChunks { - use crate::indexer::stacks::{ - NewSignerModifiedSlot, NewStackerDbChunkIssuerId, NewStackerDbChunkIssuerSlots, - NewStackerDbChunksContractId, - }; - crate::indexer::stacks::NewStackerDbChunks { - contract_id: NewStackerDbChunksContractId { - name: contract_name, - issuer: ( - NewStackerDbChunkIssuerId(26), - NewStackerDbChunkIssuerSlots(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]), - ), - }, - modified_slots: vec![NewSignerModifiedSlot { - sig: slot_sig, - data: slot_data, - slot_id: 1, - slot_version: 141, - }], - } -} From b5ad4ba11dd72722bb6cbe936ec29411cde9a606 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Thu, 24 Oct 2024 19:53:58 +0200 Subject: [PATCH 2/3] feat: include recovered signer pubkeys in new block payload (#662) --- .../service/tests/helpers/mock_stacks_node.rs | 1 + .../fixtures/stacks/testnet/occurrence.json | 42 ++++++++++----- .../chainhook-sdk/src/indexer/stacks/mod.rs | 54 +++++++++++++++++++ .../indexer/tests/helpers/stacks_blocks.rs | 1 + components/chainhook-types-js/src/index.ts | 1 + components/chainhook-types-rs/src/rosetta.rs | 1 + .../typescript/src/schemas/stacks/payload.ts | 1 + 7 files changed, 87 insertions(+), 14 deletions(-) diff --git a/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs b/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs index 883853689..f4e064e53 100644 --- a/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs +++ b/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs @@ -264,6 +264,7 @@ pub fn create_stacks_new_block( tenure_height: Some(1122), signer_bitvec: Some("000800000001ff".to_owned()), signer_signature: Some(vec!["1234".to_owned(), "2345".to_owned()]), + signer_signature_hash: None, cycle_number: Some(1), reward_set: Some(RewardSet { pox_ustx_threshold: "50000".to_owned(), diff --git a/components/chainhook-sdk/src/chainhooks/tests/fixtures/stacks/testnet/occurrence.json b/components/chainhook-sdk/src/chainhooks/tests/fixtures/stacks/testnet/occurrence.json index d1f9474b3..51ce27c9e 100644 --- a/components/chainhook-sdk/src/chainhooks/tests/fixtures/stacks/testnet/occurrence.json +++ b/components/chainhook-sdk/src/chainhooks/tests/fixtures/stacks/testnet/occurrence.json @@ -20,7 +20,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -108,7 +109,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -195,7 +197,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -283,7 +286,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -370,7 +374,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -459,7 +464,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -547,7 +553,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -635,7 +642,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -724,7 +732,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -812,7 +821,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -900,7 +910,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -988,7 +999,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -1076,7 +1088,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", @@ -1174,7 +1187,8 @@ "cycle_number": null, "reward_set": null, "signer_bitvec": null, - "signer_signature": null + "signer_signature": null, + "signer_public_keys": null }, "parent_block_identifier": { "hash": "0x", diff --git a/components/chainhook-sdk/src/indexer/stacks/mod.rs b/components/chainhook-sdk/src/indexer/stacks/mod.rs index 1d6a4f844..7478dddd8 100644 --- a/components/chainhook-sdk/src/indexer/stacks/mod.rs +++ b/components/chainhook-sdk/src/indexer/stacks/mod.rs @@ -45,6 +45,9 @@ pub struct NewBlock { #[serde(skip_serializing_if = "Option::is_none")] pub signer_bitvec: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub signer_signature_hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub signer_signature: Option>, @@ -472,6 +475,13 @@ pub fn standardize_stacks_block( }) }; + let signer_sig_hash = block + .signer_signature_hash + .as_ref() + .map(|hash| { + hex::decode(&hash[2..]).expect("unable to decode signer_signature hex") + }); + let block = StacksBlockData { block_identifier: BlockIdentifier { hash: block.index_block_hash.clone(), @@ -502,6 +512,20 @@ pub fn standardize_stacks_block( signer_bitvec: block.signer_bitvec.clone(), signer_signature: block.signer_signature.clone(), + signer_public_keys: match (signer_sig_hash, &block.signer_signature) { + (Some(signer_sig_hash), Some(signatures)) => { + Some(signatures.iter().map(|sig_hex| { + let sig_msg = clarity::util::secp256k1::MessageSignature::from_hex(sig_hex) + .map_err(|e| format!("unable to parse signer signature message: {}", e))?; + let pubkey = get_signer_pubkey_from_message_hash(&signer_sig_hash, &sig_msg) + .map_err(|e| format!("unable to recover signer sig pubkey: {}", e))?; + Ok(format!("0x{}", hex::encode(pubkey))) + }) + .collect::, String>>()?) + } + _ => None, + }, + cycle_number: block.cycle_number, reward_set: block.reward_set.as_ref().and_then(|r| { Some(StacksBlockMetadataRewardSet { @@ -848,6 +872,36 @@ fn get_nakamoto_index_block_hash( Ok(format!("0x{}", hex::encode(hash))) } +pub fn get_signer_pubkey_from_message_hash( + message_hash: &Vec, + signature: &clarity::util::secp256k1::MessageSignature, +) -> Result<[u8; 33], String> { + use miniscript::bitcoin::{ + key::Secp256k1, + secp256k1::{ + ecdsa::{RecoverableSignature, RecoveryId}, + Message, + }, + }; + + let (first, sig) = signature.0.split_at(1); + let rec_id = first[0]; + + let secp = Secp256k1::new(); + let recovery_id = + RecoveryId::from_i32(rec_id as i32).map_err(|e| format!("invalid recovery id: {e}"))?; + let signature = RecoverableSignature::from_compact(&sig, recovery_id) + .map_err(|e| format!("invalid signature: {e}"))?; + let message = + Message::from_digest_slice(&message_hash).map_err(|e| format!("invalid digest message: {e}"))?; + + let pubkey = secp + .recover_ecdsa(&message, &signature) + .map_err(|e| format!("unable to recover pubkey: {e}"))?; + + Ok(pubkey.serialize()) +} + #[cfg(feature = "stacks-signers")] pub fn get_signer_pubkey_from_stackerdb_chunk_slot( slot: &NewSignerModifiedSlot, diff --git a/components/chainhook-sdk/src/indexer/tests/helpers/stacks_blocks.rs b/components/chainhook-sdk/src/indexer/tests/helpers/stacks_blocks.rs index 71123b9fe..fcbd1e795 100644 --- a/components/chainhook-sdk/src/indexer/tests/helpers/stacks_blocks.rs +++ b/components/chainhook-sdk/src/indexer/tests/helpers/stacks_blocks.rs @@ -76,6 +76,7 @@ pub fn generate_test_stacks_block( tenure_height: Some(1122), signer_bitvec: Some("1010101010101".to_owned()), signer_signature: Some(vec!["1234".to_owned(), "2345".to_owned()]), + signer_public_keys: Some(vec!["12".to_owned(), "23".to_owned()]), cycle_number: Some(1), reward_set: Some(StacksBlockMetadataRewardSet { pox_ustx_threshold: "50000".to_owned(), diff --git a/components/chainhook-types-js/src/index.ts b/components/chainhook-types-js/src/index.ts index 8f6251d32..5c51c5308 100644 --- a/components/chainhook-types-js/src/index.ts +++ b/components/chainhook-types-js/src/index.ts @@ -699,6 +699,7 @@ export interface StacksBlockMetadata { tenure_height?: number | null; signer_bitvec?: string | null; signer_signature?: string[] | null; + signer_public_keys?: string[] | null; cycle_number?: number | null; reward_set?: { pox_ustx_threshold: string; diff --git a/components/chainhook-types-rs/src/rosetta.rs b/components/chainhook-types-rs/src/rosetta.rs index ee9066b9f..6af1972b4 100644 --- a/components/chainhook-types-rs/src/rosetta.rs +++ b/components/chainhook-types-rs/src/rosetta.rs @@ -119,6 +119,7 @@ pub struct StacksBlockMetadata { pub block_time: Option, pub signer_bitvec: Option, pub signer_signature: Option>, + pub signer_public_keys: Option>, // Available starting in epoch3, only included in blocks where the pox cycle rewards are first calculated pub cycle_number: Option, diff --git a/components/client/typescript/src/schemas/stacks/payload.ts b/components/client/typescript/src/schemas/stacks/payload.ts index e25974bf8..b87683167 100644 --- a/components/client/typescript/src/schemas/stacks/payload.ts +++ b/components/client/typescript/src/schemas/stacks/payload.ts @@ -74,6 +74,7 @@ export const StacksEventMetadataSchema = Type.Object({ block_time: Nullable(Type.Integer()), signer_bitvec: Nullable(Type.String()), signer_signature: Nullable(Type.Array(Type.String())), + signer_public_keys: Nullable(Type.Array(Type.String())), // Available starting in epoch3, only included in blocks where the pox cycle rewards are first calculated cycle_number: Nullable(Type.Integer()), From 71364c15a88a06d5c71b3f3d3c88c489ff4733d1 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Thu, 24 Oct 2024 19:54:28 +0200 Subject: [PATCH 3/3] fix: update SignerMessage deserializing (#663) --- .vscode/chainhook.toml | 46 +++++++++++++++++++ .vscode/launch.json | 24 ++++++++++ Cargo.lock | 4 +- Cargo.toml | 2 +- .../src/chainhooks/stacks/mod.rs | 7 ++- .../chainhook-sdk/src/indexer/stacks/mod.rs | 18 +++++--- .../chainhook-sdk/src/indexer/stacks/tests.rs | 2 +- components/chainhook-types-rs/src/signers.rs | 16 ++++++- .../typescript/src/schemas/stacks/signers.ts | 20 +++++++- 9 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 .vscode/chainhook.toml diff --git a/.vscode/chainhook.toml b/.vscode/chainhook.toml new file mode 100644 index 000000000..a01390bea --- /dev/null +++ b/.vscode/chainhook.toml @@ -0,0 +1,46 @@ +[storage] +working_dir = "cache" + +# The HTTP API allows you to register / deregister +# predicates dynamically. +# This is disabled by default. +# +[http_api] +http_port = 20456 +database_uri = "redis://localhost:6379/" + +[network] +mode = "testnet" +bitcoind_rpc_url = "http://localhost:18443" +bitcoind_rpc_username = "btc" +bitcoind_rpc_password = "btc" + +# Chainhook must be able to receive Bitcoin block events. +# These events can originate from either a Stacks node or a Bitcoin node's ZeroMQ interface. + +# By default, the service is set to receive Bitcoin block events from the Stacks node: +stacks_node_rpc_url = "http://localhost:20443" +stacks_events_ingestion_port = 20455 + +# However, events can also be received directly from a Bitcoin node. +# To achieve this, comment out the `stacks_node_rpc_url` line and uncomment the following line: +# bitcoind_zmq_url = "tcp://0.0.0.0:18543" + +[limits] +max_number_of_bitcoin_predicates = 100 +max_number_of_concurrent_bitcoin_scans = 100 +max_number_of_stacks_predicates = 10 +max_number_of_concurrent_stacks_scans = 10 +max_number_of_processing_threads = 16 +max_number_of_networking_threads = 16 +max_caching_memory_size_mb = 32000 + +# The TSV file is required for downloading historical data for your predicates. +# If this is not a requirement, you can comment out the `tsv_file_url` line. +# [[event_source]] +# tsv_file_url = "https://archive.hiro.so/regtest/stacks-blockchain-api/regtest-stacks-blockchain-api-latest" + +# Enables a server that provides metrics that can be scraped by Prometheus. +# This is disabled by default. +# [monitoring] +# prometheus_monitoring_port = 20457 diff --git a/.vscode/launch.json b/.vscode/launch.json index 448c74c77..9d0644e2c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,30 @@ { "version": "0.2.0", "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'chainhook'", + "cargo": { + "args": [ + "build", + "--bin=chainhook", + "--package=chainhook" + ], + "filter": { + "name": "chainhook", + "kind": "bin" + } + }, + "args": [ + "service", + "start", + "--config-path=${workspaceFolder}/.vscode/chainhook.toml", + ], + "cwd": "${workspaceFolder}", + "preLaunchTask": "redis:start", + "postDebugTask": "redis:stop" + }, { "type": "lldb", "request": "launch", diff --git a/Cargo.lock b/Cargo.lock index 23d5c23e3..989eeb269 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3549,8 +3549,8 @@ dependencies = [ [[package]] name = "stacks-codec" -version = "2.9.0" -source = "git+https://github.com/hirosystems/clarinet.git?rev=b0683675115562d719ed4b5245f620e0990030a0#b0683675115562d719ed4b5245f620e0990030a0" +version = "2.10.0" +source = "git+https://github.com/hirosystems/clarinet.git?rev=fcebfb5a986ded32d5a450c34f8e5e5f2da97de4#fcebfb5a986ded32d5a450c34f8e5e5f2da97de4" dependencies = [ "clarity", "serde", diff --git a/Cargo.toml b/Cargo.toml index e7694ce18..97b5fc91f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,4 @@ default-members = ["components/chainhook-cli", "components/chainhook-sdk"] resolver = "2" [patch.crates-io] -stacks-codec = { git = "https://github.com/hirosystems/clarinet.git", rev = "b0683675115562d719ed4b5245f620e0990030a0" } +stacks-codec = { git = "https://github.com/hirosystems/clarinet.git", rev = "fcebfb5a986ded32d5a450c34f8e5e5f2da97de4" } diff --git a/components/chainhook-sdk/src/chainhooks/stacks/mod.rs b/components/chainhook-sdk/src/chainhooks/stacks/mod.rs index 1eb2c7fbf..a59344ea4 100644 --- a/components/chainhook-sdk/src/chainhooks/stacks/mod.rs +++ b/components/chainhook-sdk/src/chainhooks/stacks/mod.rs @@ -873,7 +873,10 @@ pub fn evaluate_stacks_predicate_on_non_consensus_events<'a>( | StacksPredicate::NftEvent(_) | StacksPredicate::StxEvent(_) | StacksPredicate::PrintEvent(_) - | StacksPredicate::Txid(_) => unreachable!(), + | StacksPredicate::Txid(_) => { + // Ignore, possibly expected behavior? + // https://github.com/hirosystems/chainhook/pull/663#discussion_r1814995429 + }, }; } (occurrences, expired_predicates) @@ -1107,7 +1110,7 @@ fn serialize_stacks_non_consensus_event( }; json!({ "payload": payload, - "received_at": event.received_at_ms, + "received_at_ms": event.received_at_ms, "received_at_block": event.received_at_block, }) } diff --git a/components/chainhook-sdk/src/indexer/stacks/mod.rs b/components/chainhook-sdk/src/indexer/stacks/mod.rs index 7478dddd8..7ebf90c16 100644 --- a/components/chainhook-sdk/src/indexer/stacks/mod.rs +++ b/components/chainhook-sdk/src/indexer/stacks/mod.rs @@ -714,10 +714,13 @@ pub fn standardize_stacks_stackerdb_chunks( }) } SignerMessage::BlockResponse(block_response) => match block_response { - BlockResponse::Accepted((block_hash, sig)) => StacksSignerMessage::BlockResponse( + BlockResponse::Accepted(block_accepted) => StacksSignerMessage::BlockResponse( BlockResponseData::Accepted(BlockAcceptedResponse { - signer_signature_hash: format!("0x{}", block_hash.to_hex()), - sig: format!("0x{}", sig.to_hex()), + signer_signature_hash: format!("0x{}", block_accepted.signer_signature_hash.to_hex()), + signature: format!("0x{}", block_accepted.signature.to_hex()), + metadata: SignerMessageMetadata { + server_version: block_accepted.metadata.server_version, + } }), ), BlockResponse::Rejected(block_rejection) => StacksSignerMessage::BlockResponse( @@ -725,8 +728,8 @@ pub fn standardize_stacks_stackerdb_chunks( reason: block_rejection.reason, reason_code: match block_rejection.reason_code { RejectCode::ValidationFailed(validate_reject_code) => { - BlockRejectReasonCode::ValidationFailed( - match validate_reject_code { + BlockRejectReasonCode::ValidationFailed { + validation_failed: match validate_reject_code { ValidateRejectCode::BadBlockHash => { BlockValidationFailedCode::BadBlockHash } @@ -749,7 +752,7 @@ pub fn standardize_stacks_stackerdb_chunks( BlockValidationFailedCode::NoSuchTenure } }, - ) + } } RejectCode::NoSortitionView => BlockRejectReasonCode::NoSortitionView, RejectCode::ConnectivityIssues => { @@ -769,6 +772,9 @@ pub fn standardize_stacks_stackerdb_chunks( ), chain_id: block_rejection.chain_id, signature: format!("0x{}", block_rejection.signature.to_hex()), + metadata: SignerMessageMetadata { + server_version: block_rejection.metadata.server_version, + }, }), ), }, diff --git a/components/chainhook-sdk/src/indexer/stacks/tests.rs b/components/chainhook-sdk/src/indexer/stacks/tests.rs index 493f6fd5e..f4a005c97 100644 --- a/components/chainhook-sdk/src/indexer/stacks/tests.rs +++ b/components/chainhook-sdk/src/indexer/stacks/tests.rs @@ -440,7 +440,7 @@ fn parses_block_response_signer_message() { match &message.message { StacksSignerMessage::BlockResponse(response) => match response { BlockResponseData::Accepted(accepted) => { - assert_eq!(accepted.sig, "0x00a1c66742e665e981d10f7a70a5df312c9cba729331129ff1b510e71133d79c0122b25266bf47e8c1c923b4fde0464756ced884030e9983f797c902961fc9b0b1"); + assert_eq!(accepted.signature, "0x00a1c66742e665e981d10f7a70a5df312c9cba729331129ff1b510e71133d79c0122b25266bf47e8c1c923b4fde0464756ced884030e9983f797c902961fc9b0b1"); assert_eq!( accepted.signer_signature_hash, "0x8f913dd2bcc2cfbd1c82166e0ad99230f76de098a5ba6ee1b15b042c8f67c6f0" diff --git a/components/chainhook-types-rs/src/signers.rs b/components/chainhook-types-rs/src/signers.rs index 131a32b67..c0754dd28 100644 --- a/components/chainhook-types-rs/src/signers.rs +++ b/components/chainhook-types-rs/src/signers.rs @@ -33,10 +33,17 @@ pub struct BlockProposalData { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct BlockAcceptedResponse { pub signer_signature_hash: String, - pub sig: String, + pub signature: String, + pub metadata: SignerMessageMetadata, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub struct SignerMessageMetadata { + pub server_version: String, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum BlockValidationFailedCode { BadBlockHash, BadTransaction, @@ -50,7 +57,11 @@ pub enum BlockValidationFailedCode { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum BlockRejectReasonCode { - ValidationFailed(BlockValidationFailedCode), + #[serde(rename_all = "SCREAMING_SNAKE_CASE")] + ValidationFailed { + #[serde(rename = "VALIDATION_FAILED")] + validation_failed: BlockValidationFailedCode, + }, ConnectivityIssues, RejectedInPriorRound, NoSortitionView, @@ -65,6 +76,7 @@ pub struct BlockRejectedResponse { pub signer_signature_hash: String, pub chain_id: u32, pub signature: String, + pub metadata: SignerMessageMetadata, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] diff --git a/components/client/typescript/src/schemas/stacks/signers.ts b/components/client/typescript/src/schemas/stacks/signers.ts index caa0e2e3a..7b9ceeb8f 100644 --- a/components/client/typescript/src/schemas/stacks/signers.ts +++ b/components/client/typescript/src/schemas/stacks/signers.ts @@ -40,7 +40,10 @@ export const StacksSignerMessageBlockResponseAcceptedSchema = Type.Object({ type: Type.Literal('Accepted'), data: Type.Object({ signer_signature_hash: Type.String(), - sig: Type.String(), + signature: Type.String(), + metadata: Type.Object({ + server_version: Type.String(), + }), }), }); export type StacksSignerMessageBlockResponseAccepted = Static< @@ -52,7 +55,17 @@ export const StacksSignerMessageBlockResponseRejectedSchema = Type.Object({ data: Type.Object({ reason: Type.String(), reason_code: Type.Union([ - Type.Literal('VALIDATION_FAILED'), + Type.Object({ + VALIDATION_FAILED: Type.Union([ + Type.Literal('BAD_BLOCK_HASH'), + Type.Literal('BAD_TRANSACTION'), + Type.Literal('INVALID_BLOCK'), + Type.Literal('CHAINSTATE_ERROR'), + Type.Literal('UNKNOWN_PARENT'), + Type.Literal('NON_CANONICAL_TENURE'), + Type.Literal('NO_SUCH_TENURE'), + ]), + }), Type.Literal('CONNECTIVITY_ISSUES'), Type.Literal('REJECTED_IN_PRIOR_ROUND'), Type.Literal('NO_SORTITION_VIEW'), @@ -62,6 +75,9 @@ export const StacksSignerMessageBlockResponseRejectedSchema = Type.Object({ signer_signature_hash: Type.String(), chain_id: Type.Integer(), signature: Type.String(), + metadata: Type.Object({ + server_version: Type.String(), + }), }), }); export type StacksSignerMessageBlockResponseRejected = Static<