Skip to content

Commit

Permalink
Merge pull request #266 from Virus-Axel/bugfix/transaction_from_bytes
Browse files Browse the repository at this point in the history
Fix mint CandyGuard crash on some guards
  • Loading branch information
Virus-Axel authored Aug 25, 2024
2 parents 8be5065 + dfe3b9f commit 7f33a5f
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 2 deletions.
41 changes: 40 additions & 1 deletion example/Transactions/transaction_example.gd
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extends VBoxContainer
@onready var payer: Keypair = Keypair.new_from_file("res://payer.json")
const LAMPORTS_PER_SOL = 1000000000

const TOTAL_CASES := 11
const TOTAL_CASES := 12
var passed_test_mask := 0


Expand Down Expand Up @@ -185,6 +185,44 @@ func blockhash_before_instruction():
PASS(10)


func transaction_from_bytes():
var receiver: Pubkey = Pubkey.new_from_string("78GVwUb8ojcJVrEVkwCU5tfUKTfJuiazRrysGwgjqsif")
var tx = Transaction.new()

add_child(tx)

# A transaction can be sent after three steps:
# Set the payer.
# Add instruction(s).
# Set latest blockhash.

tx.set_payer(payer)

var ix: Instruction = SystemProgram.transfer(payer, receiver, LAMPORTS_PER_SOL / 10)
tx.add_instruction(ix)

tx.update_latest_blockhash()

tx.sign()
var original = tx.serialize()
var reconstructed = Transaction.new_from_bytes(tx.serialize()).serialize()

assert(original == reconstructed)

# Signers are not stored in the bytes so need to set them.
tx.set_signers([payer])
tx.send()
# On success transaction response signal will contain results.
# Connect it to avoid errors in you application.
var response = await tx.transaction_response_received

assert(response.has("result"))
var signature = response["result"]
assert(typeof(signature) == TYPE_STRING)

PASS(11)


func _ready():
# Use a local cluster for unlimited Solana airdrops.
# SolanaClient defaults to devnet cluster URL if not specified.
Expand All @@ -199,6 +237,7 @@ func _ready():
transaction_with_confirmation_1()
transaction_with_confirmation_2()
blockhash_before_instruction()
transaction_from_bytes()


func _on_timeout_timeout():
Expand Down
7 changes: 7 additions & 0 deletions src/instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,21 @@ CompiledInstruction::CompiledInstruction(){
}

int CompiledInstruction::create_from_bytes(const PackedByteArray& bytes){
const unsigned int MINIMUM_COMPILED_INSTRUCTION_SIZE = 3;
ERR_FAIL_COND_V_EDMSG(bytes.size() < MINIMUM_COMPILED_INSTRUCTION_SIZE, 0, "Invalid compiled instruction.");

int cursor = 0;
program_id_index = bytes[cursor++];

const unsigned int account_size = bytes[cursor++];

ERR_FAIL_COND_V_EDMSG(bytes.size() < MINIMUM_COMPILED_INSTRUCTION_SIZE + account_size, 0, "Invalid compiled instruction.");
accounts = bytes.slice(cursor, cursor + account_size);
cursor += account_size;

const unsigned int data_size = bytes[cursor++];

ERR_FAIL_COND_V_EDMSG(bytes.size() < MINIMUM_COMPILED_INSTRUCTION_SIZE + account_size + data_size, 0, "Invalid compiled instruction.");
data = bytes.slice(cursor, cursor + data_size);

return cursor + data_size;
Expand Down
3 changes: 2 additions & 1 deletion src/instructions/mpl_candy_machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1660,6 +1660,7 @@ Variant MplCandyGuard::mint(
Instruction *result = memnew(Instruction);

PackedByteArray data = MplCandyMachine::mint2_discriminator();

data.append_array(Object::cast_to<CandyGuardAccessList>(candy_guard_acl)->get_group(label).serialize_mint_settings());
data.append_array(serialize_label(label));

Expand Down Expand Up @@ -1721,7 +1722,7 @@ Variant MplCandyGuard::mint(
TypedArray<AccountMeta> mint_arg_accounts = Object::cast_to<CandyGuardAccessList>(candy_guard_acl)->get_group(label).get_mint_arg_accounts(receiver);

for(unsigned int i = 0; i < mint_arg_accounts.size(); i++){
result->append_meta(mint_arg_accounts[i]);
result->append_meta(*memnew(AccountMeta(mint_arg_accounts[i])));
}

return result;
Expand Down
12 changes: 12 additions & 0 deletions src/message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,22 @@ Message::Message(TypedArray<Instruction> instructions, Variant &payer, uint32_t

Message::Message(const PackedByteArray& bytes){
int cursor = 0;

// blockhash + number of accounts + compiled instruction size
unsigned int minimum_remaining_size = 32 + 1 + 1;
ERR_FAIL_COND_EDMSG(bytes.size() < minimum_remaining_size, "Invalid accounts size");

num_required_signatures = bytes[cursor++];

ERR_FAIL_COND_EDMSG(num_required_signatures > 127, "V0 transactions are not yet supported.");

num_readonly_signed_accounts = bytes[cursor++];
num_readonly_unsigned_accounts = bytes[cursor++];

uint8_t account_size = bytes[cursor++];

ERR_FAIL_COND_EDMSG(bytes.size() < minimum_remaining_size + account_size * 32, "Invalid accounts size");

for(unsigned int i = 0; i < account_size; i++){
account_keys.append(Pubkey::new_from_bytes(bytes.slice(cursor, cursor + 32)));
cursor += 32;
Expand All @@ -177,6 +187,8 @@ Message::Message(const PackedByteArray& bytes){
for(int i = 0; i < compiled_instructions_size; i++){
CompiledInstruction *new_instruction = memnew(CompiledInstruction);
int consumed_bytes = new_instruction->create_from_bytes(bytes.slice(cursor));
ERR_FAIL_COND(consumed_bytes == 0);

compiled_instructions.append(new_instruction);
cursor += consumed_bytes;
}
Expand Down
7 changes: 7 additions & 0 deletions src/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ Transaction::Transaction() {
}

Transaction::Transaction(const PackedByteArray& bytes){
const unsigned int MINIMUM_MESSAGE_SIZE = 32 + 4 + 1;
const unsigned int MINIMUM_TRANSACTION_SIZE = MINIMUM_MESSAGE_SIZE + 1;
ERR_FAIL_COND_EDMSG(bytes.size() < MINIMUM_TRANSACTION_SIZE, "Invalid transaction size");

send_client = memnew(SolanaClient);
blockhash_client = memnew(SolanaClient);
subscribe_client = memnew(SolanaClient);
Expand All @@ -320,13 +324,16 @@ Transaction::Transaction(const PackedByteArray& bytes){
subscribe_client->set_async_override(true);

int cursor = 0;

const unsigned int signer_size = bytes[cursor++];
ERR_FAIL_COND_EDMSG(bytes.size() < MINIMUM_MESSAGE_SIZE + 1 + signer_size * 64, "Invalid message size.");
for(unsigned int i = 0; i < signer_size; i++){
const PackedByteArray signature_bytes = bytes.slice(cursor, cursor + 64);
if(! are_all_bytes_zeroes(signature_bytes)){
ready_signature_amount += 1;
}
signatures.append(bytes.slice(cursor, cursor + 64));

cursor += 64;
}

Expand Down

0 comments on commit 7f33a5f

Please sign in to comment.