From a1c97a765945827ac6de419c541d19cedb3684aa Mon Sep 17 00:00:00 2001 From: Aryaman Jain Date: Wed, 1 May 2024 16:36:15 +0000 Subject: [PATCH 1/2] flamenco: add txn generation --- src/flamenco/txn/Local.mk | 4 + src/flamenco/txn/fd_txn_generate.c | 144 +++++++++++++++++++++++++++++ src/flamenco/txn/fd_txn_generate.h | 49 ++++++++++ 3 files changed, 197 insertions(+) create mode 100644 src/flamenco/txn/Local.mk create mode 100644 src/flamenco/txn/fd_txn_generate.c create mode 100644 src/flamenco/txn/fd_txn_generate.h diff --git a/src/flamenco/txn/Local.mk b/src/flamenco/txn/Local.mk new file mode 100644 index 0000000000..e91f704c4e --- /dev/null +++ b/src/flamenco/txn/Local.mk @@ -0,0 +1,4 @@ +ifdef FD_HAS_INT128 +$(call add-hdrs,fd_txn_generate.h) +$(call add-objs,fd_txn_generate,fd_flamenco) +endif diff --git a/src/flamenco/txn/fd_txn_generate.c b/src/flamenco/txn/fd_txn_generate.c new file mode 100644 index 0000000000..39816e51c0 --- /dev/null +++ b/src/flamenco/txn/fd_txn_generate.c @@ -0,0 +1,144 @@ +#include "fd_txn_generate.h" + +/* 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; + +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 ) { + + FD_TEST(num_signatures <= 128); + *out_txn_payload = (uchar)num_signatures; + /* Fill out txn metadata */ + fd_txn_t * txn_meta = (fd_txn_t *) out_txn_meta; + txn_meta->acct_addr_cnt = accounts->acct_cnt; + txn_meta->readonly_signed_cnt = accounts->readonly_signed_cnt; + txn_meta->readonly_unsigned_cnt = accounts->readonly_unsigned_cnt; + /* Number of signatures is encoded as a compact u16 but + can always be encoded using 1 byte here. */ + txn_meta->message_off = (ushort)(num_signatures * FD_TXN_SIGNATURE_SZ + 1); + txn_meta->signature_off = (ushort)1UL; + txn_meta->instr_cnt = 0; + + FD_TEST(txn_meta->acct_addr_cnt <= 128); + txn_meta->acct_addr_off = (ushort)(txn_meta->message_off + (sizeof(fd_txn_message_hdr_t)) + 1); + txn_meta->recent_blockhash_off = (ushort)(txn_meta->acct_addr_off + (txn_meta->acct_addr_cnt * FD_TXN_ACCT_ADDR_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 = accounts->signature_cnt, + .num_readonly_signatures = accounts->readonly_signed_cnt, + .num_readonly_unsigned = accounts->readonly_unsigned_cnt }; + memcpy( write_ptr, &msg_header, sizeof(fd_txn_message_hdr_t) ) ; + write_ptr += sizeof(fd_txn_message_hdr_t); + + /* Write number of accounts */ + *write_ptr = (uchar)txn_meta->acct_addr_cnt; + write_ptr += 1; + + /* Write accounts list to txn payload */ + ulong signers_write_sz = FD_TXN_ACCT_ADDR_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, FD_TXN_ACCT_ADDR_SZ * accounts->readonly_signed_cnt ); + write_ptr += FD_TXN_ACCT_ADDR_SZ * accounts->readonly_signed_cnt; + + ulong non_signers_write_sz = FD_TXN_ACCT_ADDR_SZ * (ulong)(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, FD_TXN_ACCT_ADDR_SZ * accounts->readonly_unsigned_cnt ); + write_ptr += FD_TXN_ACCT_ADDR_SZ * accounts->readonly_unsigned_cnt; + FD_TEST( (ushort)((ulong)write_ptr - (ulong)out_txn_payload) == txn_meta->recent_blockhash_off ); + + /* Write recent blockhash */ + if( FD_LIKELY( opt_recent_blockhash ) ) { + memcpy( write_ptr, opt_recent_blockhash, FD_TXN_BLOCKHASH_SZ ); + } else { + memset( write_ptr, 0UL, FD_TXN_BLOCKHASH_SZ ); + } + write_ptr += FD_TXN_BLOCKHASH_SZ; + + 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 const * accounts, + ulong accounts_sz, + uchar const * instr_buf, + ulong instr_buf_sz ) { + + 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 + FD_TXN_BLOCKHASH_SZ; + txn_meta->instr_cnt++; + uchar * write_ptr = instr_start; + + uchar compact_instr_cnt_sz = (uchar) fd_cu16_enc( (ushort)txn_meta->instr_cnt, write_ptr ); + FD_TEST( compact_instr_cnt_sz == 1 ); + + write_ptr += compact_instr_cnt_sz; + + /* Calculate offset of next instruction. */ + 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; + + *write_ptr = program_id; + write_ptr += sizeof(uchar); + + uchar compact_accts_len_sz = (uchar) fd_cu16_enc( (ushort)accounts_sz, write_ptr ); + write_ptr += compact_accts_len_sz; + + fd_memcpy( write_ptr, accounts, accounts_sz ); + write_ptr += accounts_sz; + + ushort data_sz = (ushort)instr_buf_sz; + uint compact_data_len_sz = fd_cu16_enc( data_sz, write_ptr ); + write_ptr += compact_data_len_sz; + + /* Copy data buffer over */ + 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_sz, + data_sz, acct_off, data_off ); + return (ulong)(write_ptr - out_txn_payload); +} diff --git a/src/flamenco/txn/fd_txn_generate.h b/src/flamenco/txn/fd_txn_generate.h new file mode 100644 index 0000000000..d7a107c90c --- /dev/null +++ b/src/flamenco/txn/fd_txn_generate.h @@ -0,0 +1,49 @@ +/* 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" + +/* 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 { + /* signature cnt <= 256 */ + uchar signature_cnt; + /* readonly signed/unsigned <= 256 */ + uchar readonly_signed_cnt; + uchar readonly_unsigned_cnt; + ushort 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; + +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. + The accounts param is a list of indices to the accounts in the txn. + The instruction buffer contains the data for the instruction to + be added. */ +ulong +fd_txn_add_instr( uchar * txn_meta_ptr, + uchar out_txn_payload[ static FD_TXN_MTU ], + uchar program_id, + uchar const * accounts, + ulong accounts_sz, + uchar const * instr_buf, + ulong instr_buf_sz ); +FD_PROTOTYPES_END From b602c52d39bd7a4adae9f1317a26c21808eebd38 Mon Sep 17 00:00:00 2001 From: Aryaman Jain Date: Thu, 16 May 2024 21:05:08 +0000 Subject: [PATCH 2/2] flamenco: add instrs reset to txn generation --- src/flamenco/txn/fd_txn_generate.c | 30 +++++++++++++++++++++++------- src/flamenco/txn/fd_txn_generate.h | 17 +++++++++++++---- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/flamenco/txn/fd_txn_generate.c b/src/flamenco/txn/fd_txn_generate.c index 39816e51c0..cda0e49468 100644 --- a/src/flamenco/txn/fd_txn_generate.c +++ b/src/flamenco/txn/fd_txn_generate.c @@ -32,8 +32,10 @@ fd_txn_base_generate( uchar out_txn_meta[ static FD_TXN_MAX_SZ ], fd_txn_accounts_t * accounts, uchar * opt_recent_blockhash ) { - FD_TEST(num_signatures <= 128); + /* Number of signatures cannot exceed 127. */ + FD_TEST(num_signatures <= FD_TXN_SIG_MAX); *out_txn_payload = (uchar)num_signatures; + /* Fill out txn metadata */ fd_txn_t * txn_meta = (fd_txn_t *) out_txn_meta; txn_meta->acct_addr_cnt = accounts->acct_cnt; @@ -45,7 +47,7 @@ fd_txn_base_generate( uchar out_txn_meta[ static FD_TXN_MAX_SZ ], txn_meta->signature_off = (ushort)1UL; txn_meta->instr_cnt = 0; - FD_TEST(txn_meta->acct_addr_cnt <= 128); + FD_TEST(txn_meta->acct_addr_cnt < FD_TXN_ACCT_ADDR_MAX); txn_meta->acct_addr_off = (ushort)(txn_meta->message_off + (sizeof(fd_txn_message_hdr_t)) + 1); txn_meta->recent_blockhash_off = (ushort)(txn_meta->acct_addr_off + (txn_meta->acct_addr_cnt * FD_TXN_ACCT_ADDR_SZ)); @@ -105,13 +107,13 @@ fd_txn_add_instr( uchar * txn_meta_ptr, txn_meta->instr_cnt++; uchar * write_ptr = instr_start; - uchar compact_instr_cnt_sz = (uchar) fd_cu16_enc( (ushort)txn_meta->instr_cnt, write_ptr ); + uint compact_instr_cnt_sz = fd_cu16_enc( (ushort)txn_meta->instr_cnt, write_ptr ); FD_TEST( compact_instr_cnt_sz == 1 ); write_ptr += compact_instr_cnt_sz; /* Calculate offset of next instruction. */ - if ( txn_meta->instr_cnt > 1 ) { + if ( FD_UNLIKELY( 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; } @@ -120,9 +122,10 @@ fd_txn_add_instr( uchar * txn_meta_ptr, *write_ptr = program_id; write_ptr += sizeof(uchar); - uchar compact_accts_len_sz = (uchar) fd_cu16_enc( (ushort)accounts_sz, write_ptr ); + uint compact_accts_len_sz = fd_cu16_enc( (ushort)accounts_sz, write_ptr ); write_ptr += compact_accts_len_sz; + ushort acct_off = (ushort) (write_ptr - out_txn_payload); fd_memcpy( write_ptr, accounts, accounts_sz ); write_ptr += accounts_sz; @@ -134,11 +137,24 @@ fd_txn_add_instr( uchar * txn_meta_ptr, 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_sz, data_sz, acct_off, data_off ); - return (ulong)(write_ptr - out_txn_payload); + return (ulong)(instr_start - out_txn_payload); +} + +void +fd_txn_reset_instrs( uchar * txn_meta_ptr, + uchar out_txn_payload[ static FD_TXN_MTU ] ) { + fd_txn_t * txn_meta = (fd_txn_t *)txn_meta_ptr; + if( FD_UNLIKELY( txn_meta->instr_cnt == 0 ) ) { + return; + } + + ulong instr_start = txn_meta->recent_blockhash_off + FD_TXN_BLOCKHASH_SZ; + + *(out_txn_payload + instr_start) = 0; + txn_meta->instr_cnt = 0; } diff --git a/src/flamenco/txn/fd_txn_generate.h b/src/flamenco/txn/fd_txn_generate.h index d7a107c90c..fef679b4f2 100644 --- a/src/flamenco/txn/fd_txn_generate.h +++ b/src/flamenco/txn/fd_txn_generate.h @@ -10,9 +10,9 @@ /* 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 { - /* signature cnt <= 256 */ + /* signature cnt <= 128 */ uchar signature_cnt; - /* readonly signed/unsigned <= 256 */ + /* readonly signed/unsigned <= 128 */ uchar readonly_signed_cnt; uchar readonly_unsigned_cnt; ushort acct_cnt; @@ -26,7 +26,9 @@ typedef struct fd_txn_accounts fd_txn_accounts_t; FD_PROTOTYPES_BEGIN -/* Method used to create a template for a txn (useful for pre-staging and re-use) */ +/* Method used to create a template for a txn (useful for pre-staging and re-use) + Num signatures is restricted to < 128. Returns the offset to the start of the + insructions. */ ulong fd_txn_base_generate( uchar out_txn_meta[ static FD_TXN_MAX_SZ ], uchar out_txn_payload[ static FD_TXN_MTU ], @@ -37,7 +39,8 @@ fd_txn_base_generate( uchar out_txn_meta[ static FD_TXN_MAX_SZ ], /* Method used for adding an instruction to a txn being generated. The accounts param is a list of indices to the accounts in the txn. The instruction buffer contains the data for the instruction to - be added. */ + be added. Returns the offset to the start of the instruction added + in the transaction buffer. */ ulong fd_txn_add_instr( uchar * txn_meta_ptr, uchar out_txn_payload[ static FD_TXN_MTU ], @@ -46,4 +49,10 @@ fd_txn_add_instr( uchar * txn_meta_ptr, ulong accounts_sz, uchar const * instr_buf, ulong instr_buf_sz ); + +/* Helper method to reset the list of instrs in the metadata and + remove all instrs from the txn payload. */ +void +fd_txn_reset_instrs( uchar * txn_meta_ptr, + uchar out_txn_payload[ static FD_TXN_MTU ] ); FD_PROTOTYPES_END