Skip to content

Commit

Permalink
flamenco: add txn generation
Browse files Browse the repository at this point in the history
  • Loading branch information
arjain4 committed May 1, 2024
1 parent 9362ac5 commit 210ce8d
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/flamenco/txn/Local.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
$(call add-hdrs,fd_txn_generate.h)
$(call add-objs,fd_txn_generate,fd_txn_generate)
.PHONY: fd_txn_generate

fd_txn_generate: $(OBJDIR)/bin/fd_txn_generate
136 changes: 136 additions & 0 deletions src/flamenco/txn/fd_txn_generate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#include "fd_txn_generate.h"

static fd_txn_instr_t * fd_txn_instr_meta_generate( uchar * out_buf,
uchar program_id,
ushort acct_cnt,
ushort data_sz,
ushort acct_off,
ushort data_off ) {
fd_txn_instr_t * out_instr = (fd_txn_instr_t *) out_buf;
out_instr->program_id = program_id;
out_instr->acct_cnt = acct_cnt;
out_instr->data_sz = data_sz;
out_instr->acct_off = acct_off;
out_instr->data_off = data_off;
return out_instr;
}

ulong fd_txn_base_generate( uchar out_txn_meta[ static FD_TXN_MAX_SZ ],
uchar out_txn_payload[ static FD_TXN_MTU ],
ulong num_signatures,
fd_txn_accounts_t * accounts,
uchar * opt_recent_blockhash
) {

uchar compact_sig_cnt[3];
uchar compact_sig_cnt_sz = (uchar) fd_cu16_enc( (ushort)num_signatures, compact_sig_cnt );
// Fill out txn metadata
fd_txn_t * txn_meta = (fd_txn_t *) out_txn_meta;
txn_meta->acct_addr_cnt = (ushort)accounts->acct_cnt;
txn_meta->readonly_signed_cnt = (uchar)accounts->readonly_signed_cnt;
txn_meta->readonly_unsigned_cnt = (uchar)accounts->readonly_unsigned_cnt;
txn_meta->message_off = (ushort)(num_signatures * SIGNATURE_SZ + compact_sig_cnt_sz);
txn_meta->signature_off = 0;
txn_meta->instr_cnt = 0;

uchar compact_acct_cnt[3];
uchar compact_acct_cnt_sz = (uchar) fd_cu16_enc( (ushort)txn_meta->acct_addr_cnt, compact_acct_cnt );
txn_meta->acct_addr_off = (ushort)(txn_meta->message_off + (sizeof(fd_txn_message_hdr_t)) + compact_acct_cnt_sz);
txn_meta->recent_blockhash_off = (ushort)(txn_meta->acct_addr_off + (txn_meta->acct_addr_cnt * ACCOUNT_SZ));

// Fill message header in txn payload
uchar * write_ptr = out_txn_payload + txn_meta->message_off;
fd_txn_message_hdr_t msg_header = { .num_signatures = (uchar)accounts->signature_cnt,
.num_readonly_signatures = (uchar)accounts->readonly_signed_cnt,
.num_readonly_unsigned = (uchar)accounts->readonly_unsigned_cnt };
fd_memcpy( write_ptr, &msg_header, sizeof(fd_txn_message_hdr_t));
write_ptr = out_txn_payload + txn_meta->acct_addr_off - compact_acct_cnt_sz;

// Write number of accounts (compact-u16)
fd_memcpy( write_ptr, compact_acct_cnt, compact_acct_cnt_sz );
write_ptr += compact_acct_cnt_sz;

// Write accounts list to txn payload
ulong signers_write_sz = ACCOUNT_SZ * (accounts->signature_cnt - accounts->readonly_signed_cnt);
fd_memcpy( write_ptr, accounts->signers_w, signers_write_sz );
write_ptr += signers_write_sz;

fd_memcpy( write_ptr, accounts->signers_r, ACCOUNT_SZ * accounts->readonly_signed_cnt );
write_ptr += ACCOUNT_SZ * accounts->readonly_signed_cnt;

ulong non_signers_write_sz = ACCOUNT_SZ * (accounts->acct_cnt - accounts->readonly_unsigned_cnt - accounts->signature_cnt);
fd_memcpy( write_ptr, accounts->non_signers_w, non_signers_write_sz);
write_ptr += non_signers_write_sz;

fd_memcpy( write_ptr, accounts->non_signers_r, ACCOUNT_SZ * accounts->readonly_unsigned_cnt );
write_ptr += ACCOUNT_SZ * accounts->readonly_unsigned_cnt;
FD_TEST( (ushort)((ulong)write_ptr - (ulong)out_txn_payload) == txn_meta->recent_blockhash_off );

// Write recent blockhash
if ( opt_recent_blockhash ) {
fd_memcpy( write_ptr, opt_recent_blockhash, 32 );
write_ptr += 32;
}

return (ulong)(write_ptr - out_txn_payload);
}

ulong fd_txn_add_instr( uchar * txn_meta_ptr,
uchar out_txn_payload[ static FD_TXN_MTU ],
uchar program_id,
uchar * accounts,
ulong accounts_len,
fd_build_instr_fun instr_fun,
uchar * opt_build_args,
ulong opt_args_len ) {

fd_txn_t * txn_meta = (fd_txn_t *) txn_meta_ptr;
FD_TEST( txn_meta->instr_cnt < FD_TXN_INSTR_MAX );
FD_TEST( txn_meta->recent_blockhash_off != 0 );

uchar * instr_start = out_txn_payload + txn_meta->recent_blockhash_off + BLOCKHASH_SZ;
txn_meta->instr_cnt++;
uchar compact_instr_cnt[3];
uchar compact_instr_cnt_sz = (uchar) fd_cu16_enc( (ushort)txn_meta->instr_cnt, compact_instr_cnt );
FD_TEST( compact_instr_cnt_sz == 1 );
uchar * write_ptr = instr_start;
fd_memcpy( write_ptr, compact_instr_cnt, compact_instr_cnt_sz );
write_ptr += compact_instr_cnt_sz;

if ( txn_meta->instr_cnt > 1 ) {
write_ptr = out_txn_payload + txn_meta->instr[txn_meta->instr_cnt-2].data_off + txn_meta->instr[txn_meta->instr_cnt-2].data_sz;
}

instr_start = write_ptr;

fd_memcpy( write_ptr, &program_id, sizeof(uchar) );
write_ptr += sizeof(uchar);

uchar compact_accts_len[3];
uchar compact_accts_len_sz = (uchar) fd_cu16_enc( (ushort)accounts_len, compact_accts_len );
fd_memcpy( write_ptr, compact_accts_len, compact_accts_len_sz );
write_ptr += compact_accts_len_sz;

fd_memcpy( write_ptr, accounts, accounts_len );
write_ptr += accounts_len;

// Build instruction data
uchar instr_buf[FD_TXN_MTU];
ushort data_sz = (*instr_fun)( instr_buf, opt_build_args, opt_args_len );
uchar compact_data_len[3];
uint compact_data_len_sz = fd_cu16_enc( data_sz, compact_data_len );

// Copy data array over
fd_memcpy( write_ptr, compact_data_len, compact_data_len_sz );
write_ptr += compact_data_len_sz;
ushort data_off = (ushort) (write_ptr - out_txn_payload);
fd_memcpy( write_ptr, instr_buf, data_sz );
write_ptr += data_sz;
ushort acct_off = (ushort) (instr_start + sizeof(uchar) + compact_accts_len_sz - out_txn_payload);

(void) fd_txn_instr_meta_generate( (uchar*)&txn_meta->instr[txn_meta->instr_cnt-1],
program_id,
(ushort)accounts_len,
data_sz, acct_off, data_off );
return (ulong)(write_ptr - out_txn_payload);
}
87 changes: 87 additions & 0 deletions src/flamenco/txn/fd_txn_generate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* Provides utility methods to create txn templates for
* pre-staging, as well as a mechanism to build out an
* entire transaction with instructions.
*/

#include "../../ballet/txn/fd_txn.h"
#include "../../ballet/txn/fd_compact_u16.h"
#include "../../flamenco/types/fd_types_custom.h"
#include "../../flamenco/types/fd_types.h"

#define SIGNATURE_SZ 64
#define ACCOUNT_SZ 32
#define BLOCKHASH_SZ 32

/**
* Struct used to define a list of accounts supplied in a txn.
* Also provides information on number of signers/writeable accounts.
*/
struct fd_txn_accounts {
ulong signature_cnt;
ulong readonly_signed_cnt;
ulong readonly_unsigned_cnt;
ulong acct_cnt;
fd_pubkey_t * signers_w;
fd_pubkey_t * signers_r;
fd_pubkey_t * non_signers_w;
fd_pubkey_t * non_signers_r;
};

typedef struct fd_txn_accounts fd_txn_accounts_t;

// Message header type
struct __attribute__((packed)) fd_txn_message_hdr {
uchar num_signatures;
uchar num_readonly_signatures;
uchar num_readonly_unsigned;
};

typedef struct fd_txn_message_hdr fd_txn_message_hdr_t;

/**
* Instruction builder function signature. Accepts an output buffer and optional argument.
* Returns the size of the instruction.
* An example of such a function used for testing:
*
ushort build_vote_state_update_instr( uchar * out_buf, uchar * FD_PARAM_UNUSED opt_args, ulong FD_PARAM_UNUSED opt_args_len ) {
fd_vote_instruction_t vote_instr;
vote_instr.discriminant = fd_vote_instruction_enum_update_vote_state;
fd_vote_state_update_t update;
memset(&update, 0, sizeof(fd_vote_state_update_t));
getrandom( update.hash.key, 32UL, 0 );
ulong ts = (ulong)fd_log_wallclock();
update.timestamp = &ts;
vote_instr.inner.update_vote_state = update;
fd_bincode_encode_ctx_t encode = {.data = out_buf, .dataend = (out_buf + FD_TXN_MTU)};
fd_vote_instruction_encode( &vote_instr, &encode );
(void) opt_args;
(void) opt_args_len;
return (ushort)fd_vote_instruction_size( &vote_instr );
}
**/
typedef ushort (*fd_build_instr_fun)( uchar * buf_out, uchar * opt_arg, ulong arg_sz );

FD_PROTOTYPES_BEGIN

/**
* Method used to create a template for a txn (useful for pre-staging and re-use)
*/
ulong fd_txn_base_generate( uchar out_txn_meta[ static FD_TXN_MAX_SZ ],
uchar out_txn_payload[ static FD_TXN_MTU ],
ulong num_signatures,
fd_txn_accounts_t * accounts,
uchar * opt_recent_blockhash );

/**
* Method used for adding an instruction to a txn being generated.
*/
ulong fd_txn_add_instr( uchar * txn_meta_ptr,
uchar out_txn_payload[ static FD_TXN_MTU ],
uchar program_id,
uchar * accounts,
ulong accounts_len,
fd_build_instr_fun instr_fun,
uchar * opt_build_args,
ulong opt_args_len );
FD_PROTOTYPES_END

0 comments on commit 210ce8d

Please sign in to comment.