Skip to content

Commit

Permalink
fix: serialize brc-20 data (#585)
Browse files Browse the repository at this point in the history
Co-authored-by: Ludo Galabru <[email protected]>
Co-authored-by: Micaiah Reid <[email protected]>
  • Loading branch information
3 people committed May 14, 2024
1 parent adda6ec commit 9011a0c
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 37 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion components/chainhook-sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "chainhook-sdk"
version = "0.12.7"
version = "0.12.10"
description = "Stateless Transaction Indexing Engine for Stacks and Bitcoin"
license = "GPL-3.0"
edition = "2021"
Expand Down
4 changes: 4 additions & 0 deletions components/chainhook-sdk/src/chainhooks/bitcoin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,10 @@ pub fn serialize_bitcoin_transactions_to_json<'a>(
};
metadata.insert("ordinal_operations".into(), json!(ordinals_ops));

if let Some(ref brc20) = transaction.metadata.brc20_operation {
metadata.insert("brc20_operation".into(), json!(brc20));
}

metadata.insert(
"proof".into(),
json!(proofs.get(&transaction.transaction_identifier)),
Expand Down
80 changes: 79 additions & 1 deletion components/chainhook-sdk/src/chainhooks/bitcoin/tests.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use std::collections::HashSet;

use super::super::types::MatchingRule;
use super::*;
use crate::chainhooks::types::InscriptionFeedData;
use crate::indexer::tests::helpers::accounts;
use crate::indexer::tests::helpers::bitcoin_blocks::generate_test_bitcoin_block;
use crate::indexer::tests::helpers::transactions::generate_test_tx_bitcoin_p2pkh_transfer;
use crate::types::BitcoinTransactionMetadata;
use chainhook_types::bitcoin::TxOut;

use chainhook_types::BitcoinNetwork;
use chainhook_types::{BitcoinNetwork, Brc20Operation, Brc20TokenDeployData};
use test_case::test_case;

#[test_case(
Expand Down Expand Up @@ -193,3 +196,78 @@ fn it_serdes_occurrence_payload(

let _: BitcoinChainhookOccurrencePayload = serde_json::from_slice(&payload[..]).unwrap();
}

#[test_case(
"pepe".to_string();
"including brc20 data"
)]
fn it_serdes_brc20_payload(tick: String) {
let transaction = BitcoinTransactionData {
transaction_identifier: TransactionIdentifier {
hash: "0xc6191000459e4c58611103216e44547e512c01ee04119462644ee09ce9d8e8bb".to_string(),
},
operations: vec![],
metadata: BitcoinTransactionMetadata {
inputs: vec![],
outputs: vec![],
ordinal_operations: vec![],
stacks_operations: vec![],
brc20_operation: Some(Brc20Operation::Deploy(Brc20TokenDeployData {
tick,
max: "21000000.000000".to_string(),
lim: "1000.000000".to_string(),
dec: "6".to_string(),
address: "3P4WqXDbSLRhzo2H6MT6YFbvBKBDPLbVtQ".to_string(),
inscription_id:
"c6191000459e4c58611103216e44547e512c01ee04119462644ee09ce9d8e8bbi0".to_string(),
self_mint: false,
})),
proof: None,
fee: 0,
index: 0,
},
};
let block = generate_test_bitcoin_block(0, 0, vec![transaction.clone()], None);
let mut meta_protocols = HashSet::<OrdinalsMetaProtocol>::new();
meta_protocols.insert(OrdinalsMetaProtocol::Brc20);
let chainhook = &BitcoinChainhookSpecification {
uuid: "uuid".into(),
owner_uuid: None,
name: "name".into(),
network: BitcoinNetwork::Mainnet,
version: 0,
blocks: None,
start_block: None,
end_block: None,
expire_after_occurrence: None,
predicate: BitcoinPredicateType::OrdinalsProtocol(OrdinalOperations::InscriptionFeed(
InscriptionFeedData {
meta_protocols: Some(meta_protocols),
},
)),
action: HookAction::Noop,
include_proof: false,
include_inputs: false,
include_outputs: false,
include_witness: false,
enabled: true,
expired_at: None,
};
let trigger = BitcoinTriggerChainhook {
chainhook,
apply: vec![(vec![&transaction], &block)],
rollback: vec![],
};
let payload = serde_json::to_vec(&serialize_bitcoin_payload_to_json(
&trigger,
&HashMap::new(),
))
.unwrap();

let deserialized: BitcoinChainhookOccurrencePayload =
serde_json::from_slice(&payload[..]).unwrap();
assert!(deserialized.apply[0].block.transactions[0]
.metadata
.brc20_operation
.is_some());
}
4 changes: 2 additions & 2 deletions components/client/typescript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion components/client/typescript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hirosystems/chainhook-client",
"version": "1.9.0",
"version": "1.10.0",
"description": "Chainhook TypeScript client",
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
Expand Down
58 changes: 27 additions & 31 deletions components/client/typescript/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const ServerOptionsSchema = Type.Object({
wait_for_chainhook_node: Type.Optional(Type.Boolean({ default: true })),
/** Validate the JSON schema of received chainhook payloads and report errors when invalid */
validate_chainhook_payloads: Type.Optional(Type.Boolean({ default: false })),
/** Validate the authorization token sent by the server is correct. */
validate_token_authorization: Type.Optional(Type.Boolean({ default: true })),
/** Size limit for received chainhook payloads (default 40MB) */
body_limit: Type.Optional(Type.Number({ default: 41943040 })),
/** Node type: `chainhook` or `ordhook` */
Expand Down Expand Up @@ -186,6 +188,7 @@ export async function buildServer(
}

async function isEventAuthorized(request: FastifyRequest, reply: FastifyReply) {
if (!(serverOpts.validate_token_authorization ?? true)) return;
const authHeader = request.headers.authorization;
if (authHeader && authHeader === `Bearer ${serverOpts.auth_token}`) {
return;
Expand All @@ -200,39 +203,32 @@ export async function buildServer(
> = (fastify, options, done) => {
const compiledPayloadSchema = TypeCompiler.Compile(PayloadSchema);
fastify.addHook('preHandler', isEventAuthorized);
fastify.post(
'/payload',
{
schema: {
body: PayloadSchema,
},
},
async (request, reply) => {
if (
(serverOpts.validate_chainhook_payloads ?? false) &&
!compiledPayloadSchema.Check(request.body)
) {
logger.error(
[...compiledPayloadSchema.Errors(request.body)],
`ChainhookEventObserver received an invalid payload`
);
await reply.code(422).send();
return;
}
try {
await callback(request.body.chainhook.uuid, request.body);
await reply.code(200).send();
} catch (error) {
if (error instanceof BadPayloadRequestError) {
logger.error(error, `ChainhookEventObserver bad payload`);
await reply.code(400).send();
} else {
logger.error(error, `ChainhookEventObserver error processing payload`);
await reply.code(500).send();
}
fastify.post('/payload', async (request, reply) => {
if (
(serverOpts.validate_chainhook_payloads ?? false) &&
!compiledPayloadSchema.Check(request.body)
) {
logger.error(
[...compiledPayloadSchema.Errors(request.body)],
`ChainhookEventObserver received an invalid payload`
);
await reply.code(422).send();
return;
}
const body = request.body as Payload;
try {
await callback(body.chainhook.uuid, body);
await reply.code(200).send();
} catch (error) {
if (error instanceof BadPayloadRequestError) {
logger.error(error, `ChainhookEventObserver bad payload`);
await reply.code(400).send();
} else {
logger.error(error, `ChainhookEventObserver error processing payload`);
await reply.code(500).send();
}
}
);
});
done();
};

Expand Down

0 comments on commit 9011a0c

Please sign in to comment.