Skip to content

Commit

Permalink
Merge pull request #27 from helius-labs/nick-upstream
Browse files Browse the repository at this point in the history
Update blockbuster parsing for creator/collection hash fixes
  • Loading branch information
danenbm authored Aug 24, 2023
2 parents 0504d12 + 80f2b11 commit 7803e82
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 51 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions blockbuster/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ borsh = "~0.10.3"
thiserror = "1.0.32"
solana-sdk = "~1.16.5"
anchor-lang = { version = "0.28.0"}
log = "0.4.17"

[dev-dependencies]
rand = "0.8.5"
Expand Down
128 changes: 77 additions & 51 deletions blockbuster/src/programs/bubblegum/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use log::warn;

use crate::{
error::BlockbusterError,
instruction::InstructionBundle,
Expand All @@ -21,6 +23,7 @@ pub use spl_account_compression::events::{
AccountCompressionEvent::{self, ApplicationData, ChangeLog},
ApplicationDataEvent, ChangeLogEvent, ChangeLogEventV1,
};

use spl_noop;

#[derive(Eq, PartialEq)]
Expand All @@ -29,9 +32,8 @@ pub enum Payload {
MintV1 { args: MetadataArgs },
Decompress { args: MetadataArgs },
CancelRedeem { root: [u8; 32] },
VerifyCreator { creator: Pubkey },
UnverifyCreator { creator: Pubkey },
SetAndVerifyCollection { collection: Pubkey }
CreatorVerification { creator: Pubkey, verify: bool },
CollectionVerification { collection: Pubkey, verify: bool },
}
//TODO add more of the parsing here to minimize program transformer code
pub struct BubblegumInstruction {
Expand Down Expand Up @@ -93,6 +95,7 @@ impl ProgramParser for BubblegumParser {
bundle: &InstructionBundle,
) -> Result<Box<(dyn ParseResult + 'static)>, BlockbusterError> {
let InstructionBundle {
txn_id,
instruction,
inner_ix,
keys,
Expand All @@ -116,32 +119,41 @@ impl ProgramParser for BubblegumParser {
if let Some(inner_ix_data) = cix.data() {
let inner_ix_data = inner_ix_data.iter().collect::<Vec<_>>();
if !inner_ix_data.is_empty() {
match AccountCompressionEvent::try_from_slice(&inner_ix_data)? {
ChangeLog(changelog_event) => {
let ChangeLogEvent::V1(changelog_event) = changelog_event;
b_inst.tree_update = Some(changelog_event);
}
ApplicationData(app_data) => {
let ApplicationDataEvent::V1(app_data) = app_data;
let app_data = app_data.application_data;

let event_type_byte = if !app_data.is_empty() {
&app_data[0..1]
} else {
return Err(BlockbusterError::DeserializationError);
};

match BubblegumEventType::try_from_slice(event_type_byte)? {
BubblegumEventType::Uninitialized => {
return Err(
BlockbusterError::MissingBubblegumEventData,
);
}
BubblegumEventType::LeafSchemaEvent => {
b_inst.leaf_update =
Some(LeafSchemaEvent::try_from_slice(&app_data)?);
match AccountCompressionEvent::try_from_slice(&inner_ix_data) {
Ok(result) => match result {
ChangeLog(changelog_event) => {
let ChangeLogEvent::V1(changelog_event) = changelog_event;
b_inst.tree_update = Some(changelog_event);
}
ApplicationData(app_data) => {
let ApplicationDataEvent::V1(app_data) = app_data;
let app_data = app_data.application_data;

let event_type_byte = if !app_data.is_empty() {
&app_data[0..1]
} else {
return Err(BlockbusterError::DeserializationError);
};

match BubblegumEventType::try_from_slice(event_type_byte)? {
BubblegumEventType::Uninitialized => {
return Err(
BlockbusterError::MissingBubblegumEventData,
);
}
BubblegumEventType::LeafSchemaEvent => {
b_inst.leaf_update = Some(
LeafSchemaEvent::try_from_slice(&app_data)?,
);
}
}
}
},
Err(e) => {
warn!(
"Error while deserializing txn {:?} with noop data: {:?}",
txn_id, e
);
}
}
}
Expand Down Expand Up @@ -178,32 +190,16 @@ impl ProgramParser for BubblegumParser {
b_inst.payload = Some(Payload::CancelRedeem { root: slice });
}
InstructionName::VerifyCreator => {
let creator = keys
.get(5)
.ok_or(BlockbusterError::InstructionParsingError)?
.0;
b_inst.payload = Some(Payload::VerifyCreator {
creator: Pubkey::new_from_array(creator),
});
b_inst.payload = Some(build_creator_verification_payload(keys, true)?);
}
InstructionName::UnverifyCreator => {
let creator = keys
.get(5)
.ok_or(BlockbusterError::InstructionParsingError)?
.0;
b_inst.payload = Some(Payload::UnverifyCreator {
creator: Pubkey::new_from_array(creator),
});
b_inst.payload = Some(build_creator_verification_payload(keys, false)?);
}
InstructionName::VerifyCollection | InstructionName::SetAndVerifyCollection => {
b_inst.payload = Some(build_collection_verification_payload(keys, true)?);
}
// We don't extract any additional info w.r.t. verify and unverify
// collection ops for now.
InstructionName::SetAndVerifyCollection => {
// Deserializing this to get to the second argument encoded in the slice,
// which is the collection address. Is there a (safe) way to get to that
// directly?
let _args: MetadataArgs = MetadataArgs::try_from_slice(ix_data)?;
let collection: Pubkey = Pubkey::try_from_slice(ix_data)?;
b_inst.payload = Some(Payload::SetAndVerifyCollection { collection });
InstructionName::UnverifyCollection => {
b_inst.payload = Some(build_collection_verification_payload(keys, false)?);
}
InstructionName::Unknown => {}
_ => {}
Expand All @@ -214,4 +210,34 @@ impl ProgramParser for BubblegumParser {
Ok(Box::new(b_inst))
}
}


// See Bubblegum documentation for offsets and positions:
// https://github.com/metaplex-foundation/mpl-bubblegum/blob/main/programs/bubblegum/README.md#-verify_creator-and-unverify_creator
fn build_creator_verification_payload(
keys: &[plerkle_serialization::Pubkey],
verify: bool,
) -> Result<Payload, BlockbusterError> {
let creator = keys
.get(5)
.ok_or(BlockbusterError::InstructionParsingError)?
.0;
Ok(Payload::CreatorVerification {
creator: Pubkey::new_from_array(creator),
verify,
})
}

// See Bubblegum for offsets and positions:
// https://github.com/metaplex-foundation/mpl-bubblegum/blob/main/programs/bubblegum/README.md#-verify_collection-unverify_collection-and-set_and_verify_collection
// NOTE: Unverfication does not include collection. This needs to be fixed in the README.
fn build_collection_verification_payload(
keys: &[plerkle_serialization::Pubkey],
verify: bool,
) -> Result<Payload, BlockbusterError> {
let collection_raw = keys
.get(8)
.ok_or(BlockbusterError::InstructionParsingError)?
.0;
let collection: Pubkey = Pubkey::try_from_slice(&collection_raw)?;
Ok(Payload::CollectionVerification { collection, verify })
}

0 comments on commit 7803e82

Please sign in to comment.