diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index bfda8f2ffa..2b39a7204b 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -60,7 +60,7 @@ jobs: with: submodules: recursive - - uses: dtolnay/rust-toolchain@1.73.0 + - uses: dtolnay/rust-toolchain@1.81.0 - name: Build command line args run: | @@ -74,11 +74,11 @@ jobs: ARGS="$ARGS --exit-on-err" fi # machine - if [ "${{ inputs.machine }}" != "all" ]; then + if [ ! -z "${{ inputs.machine }}" ] && [ "${{ inputs.machine }}" != "all" ]; then ARGS="$ARGS --machines ${{ inputs.machine }}" fi # gcc - if [ "${{ inputs.gcc }}" != "all" ]; then + if [ ! -z "${{ inputs.gcc }}" ] && [ "${{ inputs.gcc }}" != "all" ]; then ARGS="$ARGS --gcc-versions ${{ inputs.gcc }}" fi echo "BUILD_ARGS=$ARGS" >> $GITHUB_ENV @@ -95,7 +95,7 @@ jobs: with: submodules: recursive - - uses: dtolnay/rust-toolchain@1.73.0 + - uses: dtolnay/rust-toolchain@1.81.0 - name: Build command line args run: | @@ -109,11 +109,11 @@ jobs: ARGS="$ARGS --exit-on-err" fi # machine - if [ "${{ inputs.machine }}" != "all" ]; then + if [ ! -z "${{ inputs.machine }}" ] && [ "${{ inputs.machine }}" != "all" ]; then ARGS="$ARGS --machines ${{ inputs.machine }}" fi # clang - if [ "${{ inputs.clang }}" != "all" ]; then + if [ ! -z "${{ inputs.clang }}" ] && [ "${{ inputs.clang }}" != "all" ]; then ARGS="$ARGS --clang-versions ${{ inputs.clang }}" fi echo "BUILD_ARGS=$ARGS" >> $GITHUB_ENV diff --git a/.github/workflows/on_pull_request.yml b/.github/workflows/on_pull_request.yml index 46ec0bc679..c295bbdef6 100644 --- a/.github/workflows/on_pull_request.yml +++ b/.github/workflows/on_pull_request.yml @@ -15,7 +15,7 @@ jobs: if: github.event.pull_request.draft == false uses: ./.github/workflows/ledgers.yml secrets: inherit - firedancer: - if: github.event.pull_request.draft == false - uses: ./.github/workflows/test_firedancer_localnet.yml - secrets: inherit + # firedancer: + # if: github.event.pull_request.draft == false + # uses: ./.github/workflows/test_firedancer_localnet.yml + # secrets: inherit diff --git a/contrib/test/test-vectors-fixtures/txn-fixtures/program-tests.list b/contrib/test/test-vectors-fixtures/txn-fixtures/program-tests.list index afabbd54a3..d6da04fd7f 100644 --- a/contrib/test/test-vectors-fixtures/txn-fixtures/program-tests.list +++ b/contrib/test/test-vectors-fixtures/txn-fixtures/program-tests.list @@ -1391,7 +1391,6 @@ dump/test-vectors/txn/fixtures/programs/8134209f43c8d3dbbb861b30ee71f1f649babc5f dump/test-vectors/txn/fixtures/programs/8162e28c300791cf553a31198cd11bad10e0b7bc_265678.fix dump/test-vectors/txn/fixtures/programs/81696f4a9ca9e9e12c70b71407b73249d8c00575_265678.fix dump/test-vectors/txn/fixtures/programs/8173a59ae2048da0ab0756ff088897ed73f7be15_265678.fix -dump/test-vectors/txn/fixtures/programs/81e2a423431d64b80c68120e7e1e04d5829b7bbc_2924287.fix dump/test-vectors/txn/fixtures/programs/81e2fb1dce2a047cb9e6ed334b72e57d1759ef06_265678.fix dump/test-vectors/txn/fixtures/programs/81fd385209eb3ab9586a2f98a96a3ac9db99f260_265678.fix dump/test-vectors/txn/fixtures/programs/8203019e63adfb8f0ddaed3fbc558786a2f45042_265678.fix @@ -2222,7 +2221,6 @@ dump/test-vectors/txn/fixtures/programs/cfbba9574fbeafc94c36fd42b41f93b6cf77fa47 dump/test-vectors/txn/fixtures/programs/cfedcab73fbc13b206765d606fac26c7044e028f_265678.fix dump/test-vectors/txn/fixtures/programs/crash-0ae873089c62df6e15d3fc0a271485da355874c8.fix dump/test-vectors/txn/fixtures/programs/crash-10c291c5d8f688fa03b3007887e577e636d2ea79.fix -dump/test-vectors/txn/fixtures/programs/crash-266f593b1f2314b84cfde121192e747bb57b4e6e.fix dump/test-vectors/txn/fixtures/programs/crash-2a071a64139bf15f205d9284c323d2ed75b40a00.fix dump/test-vectors/txn/fixtures/programs/crash-3354ca5c9ba1c2ea78c33855ec5ced47dcf9f29e.fix dump/test-vectors/txn/fixtures/programs/crash-38b53303426b0cb17570b27f89be658a890b867d-add_new_reserved_account_keys.fix @@ -2336,7 +2334,6 @@ dump/test-vectors/txn/fixtures/programs/d907c1029bcdcfb8e2cd95e40111e8328bfd9986 dump/test-vectors/txn/fixtures/programs/d90c6b78ce363076a3d2138656c05b89788b220b_265678.fix dump/test-vectors/txn/fixtures/programs/d911d69395ca0da6d47669609a67078aa5d157a4_265678.fix dump/test-vectors/txn/fixtures/programs/d9120943dc2f4eb9449f6835fd7cfebb42027859_265678.fix -dump/test-vectors/txn/fixtures/programs/d95760218a1283e6e0ee79f3424c133b911b5e4b_265678.fix dump/test-vectors/txn/fixtures/programs/d95929160e4a7f7c87410cd379d1dcba23d9ccad_265678.fix dump/test-vectors/txn/fixtures/programs/d95f082d44ddd9116d6f554aa7f42857a806ba17_265678.fix dump/test-vectors/txn/fixtures/programs/d982991cdb8f20554b0863f33082bd8db6c4b52f_265678.fix diff --git a/src/app/fddev/quic_trace/fd_quic_trace.h b/src/app/fddev/quic_trace/fd_quic_trace.h index 14afa89293..74b91b45f1 100644 --- a/src/app/fddev/quic_trace/fd_quic_trace.h +++ b/src/app/fddev/quic_trace/fd_quic_trace.h @@ -1,7 +1,6 @@ #ifndef HEADER_fd_src_app_fddev_quic_trace_fd_quic_trace_h #define HEADER_fd_src_app_fddev_quic_trace_fd_quic_trace_h -#include "../../../disco/topo/fd_topo.h" #include "../../../disco/quic/fd_quic_tile.h" /* fd_quic_trace_ctx is the relocated fd_quic_ctx_t of the target quic @@ -23,6 +22,7 @@ struct fd_quic_trace_frame_ctx { ulong conn_id; uint src_ip; ushort src_port; + uchar pkt_type; ulong pkt_num; }; diff --git a/src/app/fddev/quic_trace/fd_quic_trace_frame.c b/src/app/fddev/quic_trace/fd_quic_trace_frame.c index 275a476de8..b06d7c08eb 100644 --- a/src/app/fddev/quic_trace/fd_quic_trace_frame.c +++ b/src/app/fddev/quic_trace/fd_quic_trace_frame.c @@ -155,20 +155,26 @@ FRAME_STUB( handshake_done ) static ulong fd_quic_trace_frame( fd_quic_trace_frame_ctx_t * context, - uchar const * data, - ulong data_sz ) { + uchar const * data, + ulong data_sz ) { if( FD_UNLIKELY( data_sz<1UL ) ) return FD_QUIC_PARSE_FAIL; - (void)context; /* Frame ID is technically a varint but it's sufficient to look at the first byte. */ uint id = data[0]; + if( !fd_quic_frame_type_allowed( context->pkt_type, id ) ) { + FD_LOG_HEXDUMP_NOTICE(( "Frame not allowed", data, data_sz )); + return FD_QUIC_PARSE_FAIL; + } + switch( id ) { # define F(T,MID,NAME,...) \ case T: return fd_quic_trace1_##NAME##_frame( context, data, data_sz ); FD_QUIC_FRAME_TYPES(F) # undef F - default: return FD_QUIC_PARSE_FAIL; + default: + FD_LOG_HEXDUMP_NOTICE(( "Failed to parse frame", data, data_sz )); + return FD_QUIC_PARSE_FAIL; } } diff --git a/src/app/fddev/quic_trace/fd_quic_trace_log_tile.c b/src/app/fddev/quic_trace/fd_quic_trace_log_tile.c index 04e5039ecc..b22b389bd2 100644 --- a/src/app/fddev/quic_trace/fd_quic_trace_log_tile.c +++ b/src/app/fddev/quic_trace/fd_quic_trace_log_tile.c @@ -30,9 +30,10 @@ after_frag( void * _ctx FD_FN_UNUSED, fd_stem_context_t * stem FD_FN_UNUSED ) { fd_quic_ctx_t * ctx = &fd_quic_trace_ctx; fd_quic_log_error_t const * error = fd_type_pun_const( ctx->buffer ); - printf( "event=conn_close_quic conn_id=%016lx src_ip=%08x pktnum=%8lu close_code=0x%lx loc=%.*s(%u)\n", + printf( "event=conn_close_quic conn_id=%016lx src_ip=%08x enc=%d pktnum=%8lu close_code=0x%lx loc=%.*s(%u)\n", error->hdr.conn_id, fd_uint_bswap( FD_LOAD( uint, error->hdr.ip4_saddr ) ), + error->hdr.enc_level, error->hdr.pkt_num, error->code[0], (int)sizeof(error->src_file), diff --git a/src/app/fddev/quic_trace/fd_quic_trace_rx_tile.c b/src/app/fddev/quic_trace/fd_quic_trace_rx_tile.c index 17a77921fe..bca49ea5b1 100644 --- a/src/app/fddev/quic_trace/fd_quic_trace_rx_tile.c +++ b/src/app/fddev/quic_trace/fd_quic_trace_rx_tile.c @@ -6,6 +6,7 @@ #include "fd_quic_trace.h" #include "../../../waltz/quic/fd_quic_private.h" #include "../../../waltz/quic/templ/fd_quic_parse_util.h" +#include "../../../waltz/quic/fd_quic_proto.c" #include "../../../util/net/fd_eth.h" #include "../../../util/net/fd_ip4.h" #include "../../../util/net/fd_udp.h" @@ -44,6 +45,97 @@ during_frag( void * _ctx FD_FN_UNUSED, fd_memcpy( ctx->buffer, (uchar const *)fd_chunk_to_laddr_const( ctx->in_mem, chunk ), sz ); } +static int +bounds_check_conn( fd_quic_t * quic, + fd_quic_conn_t * conn ) { + long conn_off = (long)((ulong)conn-(ulong)quic); + return conn_off >= (long)quic->layout.conn_map_off && conn_off < (long)quic->layout.hs_pool_off; +} + +static void +fd_quic_trace_initial( void * _ctx FD_FN_UNUSED, + uchar * data, + ulong data_sz, + uint ip4_saddr, + ushort udp_sport ) { + fd_quic_ctx_t * ctx = &fd_quic_trace_ctx; + fd_quic_t * quic = ctx->quic; + fd_quic_state_t * state = fd_quic_get_state( quic ); + fd_quic_conn_map_t * conn_map = translate_ptr( state->conn_map ); + + if( FD_UNLIKELY( data_sz < FD_QUIC_SHORTEST_PKT ) ) return; + + fd_quic_initial_t initial[1] = {0}; + ulong rc = fd_quic_decode_initial( initial, data, data_sz ); + if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) { + FD_LOG_DEBUG(( "fd_quic_decode_initial failed" )); + return; + } + ulong len = (ulong)( initial->pkt_num_pnoff + initial->len ); + if( FD_UNLIKELY( len > data_sz ) ) { + FD_LOG_DEBUG(( "Bogus initial packet length" )); + return; + } + + fd_quic_crypto_keys_t _keys[1]; + fd_quic_crypto_keys_t const * keys = NULL; + if( initial->dst_conn_id_len == FD_QUIC_CONN_ID_SZ ) { + ulong dst_conn_id = fd_ulong_load_8( initial->dst_conn_id ); + fd_quic_conn_map_t * conn_entry = fd_quic_conn_map_query( conn_map, dst_conn_id, NULL ); + if( conn_entry ) { + fd_quic_conn_t * conn = translate_ptr( conn_entry->conn ); + if( FD_LIKELY( bounds_check_conn( quic, conn ) ) ) { + keys = translate_ptr( &conn->keys[0][0] ); + } + } + } + if( !keys ) { + /* Set secrets->initial_secret */ + fd_quic_crypto_secrets_t secrets[1]; + fd_quic_gen_initial_secret( + secrets, + initial->dst_conn_id, initial->dst_conn_id_len ); + + /* Derive secrets->secret[0][0] */ + fd_tls_hkdf_expand_label( + secrets->secret[0][0], FD_QUIC_SECRET_SZ, + secrets->initial_secret, + FD_QUIC_CRYPTO_LABEL_CLIENT_IN, + FD_QUIC_CRYPTO_LABEL_CLIENT_IN_LEN, + NULL, 0UL ); + + /* Derive decryption key */ + fd_quic_gen_keys( _keys, secrets->secret[0][0] ); + keys = _keys; + } + + ulong pktnum_off = initial->pkt_num_pnoff; + int hdr_err = fd_quic_crypto_decrypt_hdr( data, data_sz, pktnum_off, keys ); + if( hdr_err!=FD_QUIC_SUCCESS ) return; + + ulong pktnum_sz = fd_quic_h0_pkt_num_len( data[0] )+1u; + ulong pktnum_comp = fd_quic_pktnum_decode( data+9UL, pktnum_sz ); + ulong pktnum = pktnum_comp; /* don't bother decompressing since initial pktnum is usually low */ + + int crypt_err = fd_quic_crypto_decrypt( data, data_sz, pktnum_off, pktnum, keys ); + if( crypt_err!=FD_QUIC_SUCCESS ) return; + + ulong hdr_sz = pktnum_off + pktnum_sz; + ulong wrap_sz = hdr_sz + FD_QUIC_CRYPTO_TAG_SZ; + if( FD_UNLIKELY( data_szdst_conn_id, initial->dst_conn_id_len ); + fd_quic_trace_frame_ctx_t frame_ctx = { + .conn_id = fd_ulong_load_8( conn_id_truncated ), + .pkt_num = pktnum, + .src_ip = ip4_saddr, + .src_port = udp_sport, + .pkt_type = FD_QUIC_PKT_TYPE_INITIAL + }; + fd_quic_trace_frames( &frame_ctx, data+hdr_sz, data_sz-wrap_sz ); +} + static void fd_quic_trace_1rtt( void * _ctx FD_FN_UNUSED, uchar * data, @@ -61,31 +153,33 @@ fd_quic_trace_1rtt( void * _ctx FD_FN_UNUSED, ulong dst_conn_id = fd_ulong_load_8( data+1 ); fd_quic_conn_map_t * conn_entry = fd_quic_conn_map_query( conn_map, dst_conn_id, NULL ); if( !conn_entry ) return; - fd_quic_conn_t * conn = translate_ptr( conn_entry->conn ); + fd_quic_conn_t * conn = translate_ptr( conn_entry->conn ); + if( FD_UNLIKELY( !bounds_check_conn( quic, conn ) ) ) return; + fd_quic_crypto_keys_t * keys = &conn->keys[ fd_quic_enc_level_appdata_id ][ 0 ]; - ulong pkt_number_off = 9UL; - int hdr_err = fd_quic_crypto_decrypt_hdr( data, data_sz, pkt_number_off, keys ); + ulong pktnum_off = 9UL; + int hdr_err = fd_quic_crypto_decrypt_hdr( data, data_sz, pktnum_off, keys ); if( hdr_err!=FD_QUIC_SUCCESS ) return; - ulong pkt_num_sz = fd_quic_h0_pkt_num_len( data[0] )+1u; - ulong pkt_number = fd_quic_pktnum_decode( data+9UL, pkt_num_sz ); - int crypt_err = fd_quic_crypto_decrypt( data, data_sz, pkt_number_off, pkt_number, keys ); + ulong pktnum_sz = fd_quic_h0_pkt_num_len( data[0] )+1u; + ulong pktnum_comp = fd_quic_pktnum_decode( data+9UL, pktnum_sz ); + ulong pktnum = fd_quic_reconstruct_pkt_num( pktnum_comp, pktnum_sz, conn->exp_pkt_number[2] ); + int crypt_err = fd_quic_crypto_decrypt( data, data_sz, pktnum_off, pktnum, keys ); if( crypt_err!=FD_QUIC_SUCCESS ) return; - ulong hdr_sz = pkt_number_off + pkt_num_sz; + ulong hdr_sz = pktnum_off + pktnum_sz; ulong wrap_sz = hdr_sz + FD_QUIC_CRYPTO_TAG_SZ; if( FD_UNLIKELY( data_szwksp, fd_blockstore_align(), fd_blockstore_footprint( args->shred_max, args->slot_history_max, 0, txn_max ), blockstore_tag ); + shmem = fd_wksp_alloc_laddr( args->wksp, fd_blockstore_align(), fd_blockstore_footprint( args->shred_max, args->slot_history_max, 16, txn_max ), blockstore_tag ); if( shmem == NULL ) { FD_LOG_ERR(( "failed to allocate a blockstore" )); } @@ -1094,12 +1094,12 @@ prune( fd_ledger_args_t * args ) { } /* Create blockstore */ fd_blockstore_t * pruned_blockstore; - void * shmem = fd_wksp_alloc_laddr( pruned_wksp, fd_blockstore_align(), fd_blockstore_footprint( args->shred_max, args->slot_history_max, 0, 1UL << 22UL ), FD_BLOCKSTORE_MAGIC ); + void * shmem = fd_wksp_alloc_laddr( pruned_wksp, fd_blockstore_align(), fd_blockstore_footprint( args->shred_max, args->slot_history_max, 16, 1UL << 22UL ), FD_BLOCKSTORE_MAGIC ); if( shmem == NULL ) { FD_LOG_ERR(( "failed to allocate a blockstore" )); } pruned_blockstore = fd_blockstore_join( fd_blockstore_new( shmem, 1, args->hashseed, args->shred_max, - args->slot_history_max, 0, 1UL << 22UL ) ); + args->slot_history_max, 16, 1UL << 22UL ) ); if( pruned_blockstore == NULL ) { fd_wksp_free_laddr( shmem ); FD_LOG_ERR(( "failed to allocate a blockstore" )); diff --git a/src/app/shredcap/main.c b/src/app/shredcap/main.c index f00eac66e2..08ec901821 100644 --- a/src/app/shredcap/main.c +++ b/src/app/shredcap/main.c @@ -79,12 +79,12 @@ main( int argc, char ** argv ) { FD_LOG_ERR(( "failed to join a blockstore" )); } } else { - shmem = fd_wksp_alloc_laddr( wksp, fd_blockstore_align(), fd_blockstore_footprint( shred_max, slot_history_max, 0, shred_max ), FD_BLOCKSTORE_MAGIC ); + shmem = fd_wksp_alloc_laddr( wksp, fd_blockstore_align(), fd_blockstore_footprint( shred_max, slot_history_max, 16, shred_max ), FD_BLOCKSTORE_MAGIC ); if ( shmem == NULL ) { FD_LOG_ERR(( "failed to allocate a blockstore" )); } - blockstore = fd_blockstore_join( fd_blockstore_new( shmem, 1, hashseed, shred_max, slot_history_max, 0, shred_max ) ); + blockstore = fd_blockstore_join( fd_blockstore_new( shmem, 1, hashseed, shred_max, slot_history_max, 16, shred_max ) ); if ( blockstore == NULL ) { fd_wksp_free_laddr( shmem ); FD_LOG_ERR(( "failed to allocate a blockstore" )); diff --git a/src/ballet/ed25519/fd_curve25519_secure.c b/src/ballet/ed25519/fd_curve25519_secure.c index 7b15945272..98261ca48d 100644 --- a/src/ballet/ed25519/fd_curve25519_secure.c +++ b/src/ballet/ed25519/fd_curve25519_secure.c @@ -17,7 +17,7 @@ */ FD_25519_INLINE void FD_FN_SENSITIVE -fd_ed25519_scalar_radix16( char secret_e[ 64 ], /* ouput: 64-entry in [-8;8] */ +fd_ed25519_scalar_radix16( char secret_e[ 64 ], /* output: 64-entry in [-8;8] */ uchar const secret_a[ 32 ], /* input: 32-byte, assumes valid scalar */ char * tmp_secret_carry ) { (*tmp_secret_carry) = 0; diff --git a/src/ballet/txn/fd_txn.h b/src/ballet/txn/fd_txn.h index dc79add67c..9eb75c46cf 100644 --- a/src/ballet/txn/fd_txn.h +++ b/src/ballet/txn/fd_txn.h @@ -440,6 +440,28 @@ fd_txn_get_instr_data( fd_txn_instr_t const * instr, return (uchar const *)((ulong)payload + (ulong)instr->data_off); } +/* + * 1. has 1 or 2 signatures + * 2. is legacy message + * 3. has only one instruction + * 4. which must be a Vote instruction + */ +static inline int +fd_txn_is_simple_vote_transaction( fd_txn_t const * txn, + void const * payload, + uchar const vote_program_id[ static 32 ] ) { + fd_acct_addr_t const * addr_base = fd_txn_get_acct_addrs( txn, payload ); + + ulong instr_cnt = txn->instr_cnt; + ulong vote_instr_cnt = 0UL; + for( ulong i=0UL; iinstr_cnt; i++ ) { + ulong prog_id_idx = (ulong)txn->instr[i].program_id; + fd_acct_addr_t const * prog_id = addr_base + prog_id_idx; + vote_instr_cnt += (ulong)(0 == memcmp(prog_id->b, vote_program_id, 32UL) ); + } + return (vote_instr_cnt==1UL) && (instr_cnt==1UL) && (txn->transaction_version==FD_TXN_VLEGACY) && (txn->signature_cnt<3UL); +} + /* fd_txn_align returns the alignment in bytes required of a region of memory to be used as a fd_txn_t. It is the same as alignof(fd_txn_t). */ diff --git a/src/disco/topo/fd_topo.c b/src/disco/topo/fd_topo.c index 716d90aa98..c35f052f86 100644 --- a/src/disco/topo/fd_topo.c +++ b/src/disco/topo/fd_topo.c @@ -369,15 +369,17 @@ fd_topo_print_log( int stdout, PRINT(" %23s (NUMA node %lu): %lu\n", "Required Huge Pages", i, fd_topo_huge_page_cnt( topo, i, 0 ) ); } - char agave_affinity[ 4096 ]; - ulong offset = 0UL; - for( ulong i=0UL; iagave_affinity_cnt; i++ ) { - ulong sz; - if( FD_LIKELY( i!=0UL ) ) FD_TEST( fd_cstr_printf_check( agave_affinity+offset, 4096-offset, &sz, ", %lu", topo->agave_affinity_cpu_idx[ i ] ) ); - else FD_TEST( fd_cstr_printf_check( agave_affinity+offset, 4096-offset, &sz, "%lu", topo->agave_affinity_cpu_idx[ i ] ) ); - offset += sz; + if( topo->agave_affinity_cnt > 0 ) { + char agave_affinity[4096]; + ulong offset = 0UL; + for ( ulong i = 0UL; i < topo->agave_affinity_cnt; i++ ) { + ulong sz; + if( FD_LIKELY( i != 0UL )) FD_TEST( fd_cstr_printf_check( agave_affinity+offset, 4096-offset, &sz, ", %lu", topo->agave_affinity_cpu_idx[ i ] ) ); + else FD_TEST( fd_cstr_printf_check( agave_affinity+offset, 4096-offset, &sz, "%lu", topo->agave_affinity_cpu_idx[ i ] ) ); + offset += sz; + } + PRINT( " %23s: %s\n", "Agave Affinity", agave_affinity ); } - PRINT(" %23s: %s\n", "Agave Affinity", agave_affinity ); PRINT( "\nWORKSPACES\n"); for( ulong i=0UL; iwksp_cnt; i++ ) { diff --git a/src/flamenco/runtime/fd_runtime.c b/src/flamenco/runtime/fd_runtime.c index 7c7e540810..19d81e9410 100644 --- a/src/flamenco/runtime/fd_runtime.c +++ b/src/flamenco/runtime/fd_runtime.c @@ -1759,10 +1759,7 @@ fd_runtime_finalize_txns_tpool( fd_exec_slot_ctx_t * slot_ctx, slot_ctx->signature_cnt += txn_ctx->txn_descriptor->signature_cnt; - fd_txn_instr_t const * txn_instr = &txn_ctx->txn_descriptor->instr[0]; - fd_instr_info_t instr; - fd_convert_txn_instr_to_instr( txn_ctx, txn_instr, txn_ctx->borrowed_accounts, &instr ); - int is_vote = (0 == memcmp(instr.program_id_pubkey.key, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t))); + int is_vote = fd_txn_is_simple_vote_transaction( txn_ctx->txn_descriptor, txn_ctx->_txn_raw->raw, fd_solana_vote_program_id.key ); if( is_vote ) { if( FD_UNLIKELY( exec_txn_err ) ) { failed_txn_count++; diff --git a/src/flamenco/runtime/tests/run_ledger_tests_all.txt b/src/flamenco/runtime/tests/run_ledger_tests_all.txt index b517099ae1..4ac8e1b8cb 100644 --- a/src/flamenco/runtime/tests/run_ledger_tests_all.txt +++ b/src/flamenco/runtime/tests/run_ledger_tests_all.txt @@ -77,4 +77,5 @@ src/flamenco/runtime/tests/run_ledger_test.sh -l migrate-config-program -s snaps src/flamenco/runtime/tests/run_ledger_test.sh -l per-stake-history-new-rate-activation-epoch -s snapshot-50-47DbuHpi6gXmZKQ1FUSAAfkgnuWrkfTAtx3Hk5Gqsu5W.tar.zst -p 50 -y 16 -m 5000000 -e 675 -c 2.1.0 src/flamenco/runtime/tests/run_ledger_test.sh -l enable-get-epoch-stake-syscall -s snapshot-50-FfJQ3ePJx2s5BDymDoiLVRHDHj5doYNQBzyrqus8LocL.tar.zst -p 50 -y 16 -m 5000000 -e 686 -c 2.1.0 -o 7mScTYkJXsbdrcwTQRs7oeCSXoJm4WjzBsRyf8bCU3Np src/flamenco/runtime/tests/run_ledger_test.sh -l mainnet-257181032 -s snapshot-257181030-BiZi1ye6MeW8iq2xUBDrYYPSNuBVrXvzsHjPWvQ8ZytE.tar.zst -p 50 -y 16 -m 5000000 -e 257181035 -c 1.19.0 -src/flamenco/runtime/tests/run_ledger_test.sh -l mainnet-257047660 -s snapshot-257047658-Cp4XkpXueDWep78NCTQHCWmG5VszMBjKbGdAoXYLHBfh.tar.zst -p 50 -y 16 -m 5000000 -e 257047662 -c 1.19.0 \ No newline at end of file +src/flamenco/runtime/tests/run_ledger_test.sh -l mainnet-257047660 -s snapshot-257047658-Cp4XkpXueDWep78NCTQHCWmG5VszMBjKbGdAoXYLHBfh.tar.zst -p 50 -y 16 -m 5000000 -e 257047662 -c 1.19.0 +src/flamenco/runtime/tests/run_ledger_test.sh -l mainnet-257047659 -s snapshot-257047655-5r7etGHT6fJ5edRijJMtYr8xYJpE98ECWF8n7CCZUhDN.tar.zst -p 50 -y 16 -m 5000000 -e 257047660 -c 1.19.0 \ No newline at end of file diff --git a/src/funk/fd_funk_rec.c b/src/funk/fd_funk_rec.c index 6ad6c8cef4..5582f6e34f 100644 --- a/src/funk/fd_funk_rec.c +++ b/src/funk/fd_funk_rec.c @@ -543,7 +543,7 @@ fd_funk_rec_write_prepare( fd_funk_t * funk, rec_con = irec; /* We are able to handle tombstones in this case because we treat an erased - record as not exisitng. */ + record as not existing. */ if ( FD_UNLIKELY( rec_con && !(rec_con->flags & FD_FUNK_REC_FLAG_ERASE) ) ) { /* We have an incarnation of the record */ diff --git a/src/funk/fd_funk_rec.h b/src/funk/fd_funk_rec.h index 7e61cf9fab..250f86d896 100644 --- a/src/funk/fd_funk_rec.h +++ b/src/funk/fd_funk_rec.h @@ -14,7 +14,7 @@ #define FD_FUNK_REC_ALIGN (32UL) /* FD_FUNK_REC_FLAG_* are flags that can be bit-ored together to specify - how records are to be interpreted. The 5 most signifcant bytes of a + how records are to be interpreted. The 5 most significant bytes of a rec's flag are reserved to be used in conjunction with the ERASE flag. - ERASE indicates a record in an in-preparation transaction should be diff --git a/src/util/fd_util_base.h b/src/util/fd_util_base.h index 4450b974a6..7fec7e4409 100644 --- a/src/util/fd_util_base.h +++ b/src/util/fd_util_base.h @@ -742,7 +742,11 @@ fd_type_pun_const( void const * p ) { the other logical cores sharing the same underlying physical core for a few clocks without yielding it to the operating system scheduler. Typically useful for shared memory spin polling loops, especially if - hyperthreading is in use. */ + hyperthreading is in use. IMPORTANT SAFETY TIP! This might act as a + FD_COMPILER_MFENCE on some combinations of toolchains and targets + (e.g. gcc documents that __builtin_ia32_pause also does a compiler + memory) but this should not be relied upon for portable code + (consider making this a compiler memory fence on all platforms?) */ #if FD_HAS_X86 #define FD_SPIN_PAUSE() __builtin_ia32_pause() diff --git a/src/util/tpool/fd_tpool.cxx b/src/util/tpool/fd_tpool.cxx index 117fc16182..036dac84ee 100644 --- a/src/util/tpool/fd_tpool.cxx +++ b/src/util/tpool/fd_tpool.cxx @@ -25,8 +25,9 @@ fd_tpool_private_worker( int argc, ulong scratch_sz = cfg->scratch_sz; fd_tpool_private_worker_t worker[1]; + FD_COMPILER_MFENCE(); - FD_VOLATILE( worker->state ) = FD_TPOOL_WORKER_STATE_BOOT; + worker->state = FD_TPOOL_WORKER_STATE_BOOT; FD_COMPILER_MFENCE(); worker->tile_idx = (uint)tile_idx; @@ -36,16 +37,19 @@ fd_tpool_private_worker( int argc, if( scratch_sz ) fd_scratch_attach( scratch, fd_tpool_private_scratch_frame, scratch_sz, FD_TPOOL_WORKER_SCRATCH_DEPTH ); FD_COMPILER_MFENCE(); - FD_VOLATILE( worker->state ) = FD_TPOOL_WORKER_STATE_IDLE; - FD_COMPILER_MFENCE(); - FD_VOLATILE( fd_tpool_private_worker( tpool )[ worker_idx ] ) = worker; + worker->state = FD_TPOOL_WORKER_STATE_IDLE; FD_COMPILER_MFENCE(); + fd_tpool_private_worker( tpool )[ worker_idx ] = worker; + for(;;) { /* We are IDLE ... see what we should do next */ - int state = FD_VOLATILE_CONST( worker->state ); + FD_COMPILER_MFENCE(); + int state = worker->state; + FD_COMPILER_MFENCE(); + if( FD_UNLIKELY( state!=FD_TPOOL_WORKER_STATE_EXEC ) ) { if( FD_UNLIKELY( state!=FD_TPOOL_WORKER_STATE_IDLE ) ) break; FD_SPIN_PAUSE(); @@ -54,15 +58,17 @@ fd_tpool_private_worker( int argc, /* We are EXEC ... do the task and then transition to IDLE */ - fd_tpool_task_t task = FD_VOLATILE_CONST( worker->task ); + fd_tpool_task_t task = worker->task; - void * task_tpool = FD_VOLATILE_CONST( worker->task_tpool ); - ulong task_t0 = FD_VOLATILE_CONST( worker->task_t0 ); ulong task_t1 = FD_VOLATILE_CONST( worker->task_t1 ); - void * task_args = FD_VOLATILE_CONST( worker->task_args ); - void * task_reduce = FD_VOLATILE_CONST( worker->task_reduce ); ulong task_stride = FD_VOLATILE_CONST( worker->task_stride ); - ulong task_l0 = FD_VOLATILE_CONST( worker->task_l0 ); ulong task_l1 = FD_VOLATILE_CONST( worker->task_l1 ); - ulong task_m0 = FD_VOLATILE_CONST( worker->task_m0 ); ulong task_m1 = FD_VOLATILE_CONST( worker->task_m1 ); - ulong task_n0 = FD_VOLATILE_CONST( worker->task_n0 ); ulong task_n1 = FD_VOLATILE_CONST( worker->task_n1 ); + void * task_tpool = worker->task_tpool; + ulong task_t0 = worker->task_t0; ulong task_t1 = worker->task_t1; + void * task_args = worker->task_args; + void * task_reduce = worker->task_reduce; ulong task_stride = worker->task_stride; + ulong task_l0 = worker->task_l0; ulong task_l1 = worker->task_l1; + ulong task_m0 = worker->task_m0; ulong task_m1 = worker->task_m1; + ulong task_n0 = worker->task_n0; ulong task_n1 = worker->task_n1; + + FD_COMPILER_MFENCE(); try { task( task_tpool,task_t0,task_t1, task_args, task_reduce,task_stride, task_l0,task_l1, task_m0,task_m1, task_n0,task_n1 ); @@ -71,8 +77,7 @@ fd_tpool_private_worker( int argc, } FD_COMPILER_MFENCE(); - FD_VOLATILE( worker->state ) = FD_TPOOL_WORKER_STATE_IDLE; - FD_COMPILER_MFENCE(); + worker->state = FD_TPOOL_WORKER_STATE_IDLE; } /* state is HALT, clean up and then reset back to BOOT */ @@ -80,8 +85,9 @@ fd_tpool_private_worker( int argc, if( scratch_sz ) fd_scratch_detach( NULL ); FD_COMPILER_MFENCE(); - FD_VOLATILE( worker->state ) = FD_TPOOL_WORKER_STATE_BOOT; + worker->state = FD_TPOOL_WORKER_STATE_BOOT; FD_COMPILER_MFENCE(); + return 0; } @@ -101,6 +107,8 @@ fd_tpool_t * fd_tpool_init( void * mem, ulong worker_max ) { + FD_COMPILER_MFENCE(); + if( FD_UNLIKELY( !mem ) ) { FD_LOG_WARNING(( "NULL mem" )); return NULL; @@ -120,8 +128,9 @@ fd_tpool_init( void * mem, fd_memset( mem, 0, footprint ); fd_tpool_private_worker_t * worker0 = (fd_tpool_private_worker_t *)mem; + FD_COMPILER_MFENCE(); - FD_VOLATILE( worker0->state ) = FD_TPOOL_WORKER_STATE_EXEC; + worker0->state = FD_TPOOL_WORKER_STATE_EXEC; FD_COMPILER_MFENCE(); fd_tpool_t * tpool = (fd_tpool_t *)(worker0+1); @@ -129,7 +138,7 @@ fd_tpool_init( void * mem, tpool->worker_cnt = 1UL; FD_COMPILER_MFENCE(); - FD_VOLATILE( fd_tpool_private_worker( tpool )[0] ) = worker0; + fd_tpool_private_worker( tpool )[0] = worker0; FD_COMPILER_MFENCE(); return tpool; @@ -138,16 +147,19 @@ fd_tpool_init( void * mem, void * fd_tpool_fini( fd_tpool_t * tpool ) { + FD_COMPILER_MFENCE(); + if( FD_UNLIKELY( !tpool ) ) { FD_LOG_WARNING(( "NULL tpool" )); return NULL; } - while( fd_tpool_worker_cnt( tpool )>1UL ) + while( fd_tpool_worker_cnt( tpool )>1UL ) { if( FD_UNLIKELY( !fd_tpool_worker_pop( tpool ) ) ) { FD_LOG_WARNING(( "fd_tpool_worker_pop failed" )); return NULL; } + } return (void *)fd_tpool_private_worker0( tpool ); } @@ -158,6 +170,8 @@ fd_tpool_worker_push( fd_tpool_t * tpool, void * scratch, ulong scratch_sz ) { + FD_COMPILER_MFENCE(); + if( FD_UNLIKELY( !tpool ) ) { FD_LOG_WARNING(( "NULL tpool" )); return NULL; @@ -215,7 +229,7 @@ fd_tpool_worker_push( fd_tpool_t * tpool, char ** argv = (char **)fd_type_pun( cfg ); FD_COMPILER_MFENCE(); - FD_VOLATILE( worker[ worker_cnt ] ) = NULL; + worker[ worker_cnt ] = NULL; FD_COMPILER_MFENCE(); if( FD_UNLIKELY( !fd_tile_exec_new( tile_idx, fd_tpool_private_worker, argc, argv ) ) ) { @@ -232,6 +246,8 @@ fd_tpool_worker_push( fd_tpool_t * tpool, fd_tpool_t * fd_tpool_worker_pop( fd_tpool_t * tpool ) { + FD_COMPILER_MFENCE(); + if( FD_UNLIKELY( !tpool ) ) { FD_LOG_WARNING(( "NULL tpool" )); return NULL; @@ -248,16 +264,17 @@ fd_tpool_worker_pop( fd_tpool_t * tpool ) { int volatile * vstate = (int volatile *)&(worker->state); int state; - /* Testing for IDLE isn't strictly necessary given requirements - to use this but can help catch user errors. Likewise, - FD_ATOMIC_CAS isn't strictly necessary given correct operation but - can more robustly catch such errors. */ + /* Testing for IDLE isn't strictly necessary given requirements to use + this but can help catch user errors. Likewise, FD_ATOMIC_CAS isn't + strictly necessary given correct operation but can more robustly + catch such errors. */ # if FD_HAS_ATOMIC FD_COMPILER_MFENCE(); state = FD_ATOMIC_CAS( vstate, FD_TPOOL_WORKER_STATE_IDLE, FD_TPOOL_WORKER_STATE_HALT ); FD_COMPILER_MFENCE(); + if( FD_UNLIKELY( state!=FD_TPOOL_WORKER_STATE_IDLE ) ) { FD_LOG_WARNING(( "worker to pop is not idle (%i-%s)", state, fd_tpool_worker_state_cstr( state ) )); return NULL; @@ -268,10 +285,12 @@ fd_tpool_worker_pop( fd_tpool_t * tpool ) { FD_COMPILER_MFENCE(); state = *vstate; FD_COMPILER_MFENCE(); + if( FD_UNLIKELY( state!=FD_TPOOL_WORKER_STATE_IDLE ) ) { FD_LOG_WARNING(( "worker to pop is not idle (%i-%s)", state, fd_tpool_worker_state_cstr( state ) )); return NULL; } + FD_COMPILER_MFENCE(); *vstate = FD_TPOOL_WORKER_STATE_HALT; FD_COMPILER_MFENCE(); @@ -333,9 +352,7 @@ FD_TPOOL_EXEC_ALL_IMPL_FTR #if FD_HAS_ATOMIC FD_TPOOL_EXEC_ALL_IMPL_HDR(taskq) ulong * l_next = (ulong *)_tpool; - FD_COMPILER_MFENCE(); - void * tpool = (void *)FD_VOLATILE_CONST( l_next[1] ); - FD_COMPILER_MFENCE(); + void * tpool = (void *)l_next[1]; for(;;) { /* Note that we use an ATOMIC_CAS here instead of an @@ -345,8 +362,9 @@ FD_TPOOL_EXEC_ALL_IMPL_HDR(taskq) not overflow. */ FD_COMPILER_MFENCE(); - ulong m0 = FD_VOLATILE_CONST( *l_next ); + ulong m0 = *l_next; FD_COMPILER_MFENCE(); + if( FD_UNLIKELY( m0>=l1 ) ) break; ulong m1 = m0+1UL; if( FD_UNLIKELY( FD_ATOMIC_CAS( l_next, m0, m1 )!=m0 ) ) { diff --git a/src/util/tpool/fd_tpool.h b/src/util/tpool/fd_tpool.h index 68e677f321..e2919c61b7 100644 --- a/src/util/tpool/fd_tpool.h +++ b/src/util/tpool/fd_tpool.h @@ -646,7 +646,8 @@ FD_FN_CONST ulong fd_tpool_footprint( ulong worker_max ); operation and can also flexibly participates in the tpool in bulk operations. This uses init/fini semantics instead of new/join/leave/delete semantics because thread pools aren't - meaningfully sharable between processes / thread groups. */ + meaningfully sharable between processes / thread groups. This + function acts as a compiler memory fence. */ fd_tpool_t * fd_tpool_init( void * mem, @@ -658,7 +659,8 @@ fd_tpool_init( void * mem, "worker 0" thread) and no other operations on the tpool should be in progress when this is called or done after this is called. Returns the memory region used by the tpool on success (this is not a simple - cast of mem) and NULL on failure (logs details). */ + cast of mem) and NULL on failure (logs details). This function acts + as a compiler memory fence. */ void * fd_tpool_fini( fd_tpool_t * tpool ); @@ -692,7 +694,8 @@ fd_tpool_fini( fd_tpool_t * tpool ); scratch region specified, etc). No other operations on tpool should be in process when this is called - or started while this is running. */ + or started while this is running. This function acts as a compiler + memory fence. */ fd_tpool_t * fd_tpool_worker_push( fd_tpool_t * tpool, @@ -710,7 +713,8 @@ fd_tpool_worker_push( fd_tpool_t * tpool, tpool, bad tile_idx, tile was not idle, bad scratch region, etc). No other operations on the tpool should be in process when this is - called or started while this is running. */ + called or started while this is running. This function acts as a + compiler memory fence. */ fd_tpool_t * fd_tpool_worker_pop( fd_tpool_t * tpool ); @@ -748,12 +752,17 @@ fd_tpool_worker_scratch_sz( fd_tpool_t const * tpool, no input argument checking. Specifically, assumes tpool is valid and worker_idx is in [0,worker_cnt). Return value will be a FD_TPOOL_WORKER_STATE value (and, in correct usage, either IDLE or - EXEC). worker 0 is special. The state here will always be EXEC. */ + EXEC). worker 0 is special. The state here will always be EXEC. + This function acts as a compiler memory fence. */ static inline int fd_tpool_worker_state( fd_tpool_t const * tpool, ulong worker_idx ) { - return FD_VOLATILE_CONST( fd_tpool_private_worker( tpool )[ worker_idx ]->state ); + int * _state = &fd_tpool_private_worker( tpool )[ worker_idx ]->state; + FD_COMPILER_MFENCE(); + int state = *_state; + FD_COMPILER_MFENCE(); + return state; } /* fd_tpool_exec calls @@ -775,7 +784,8 @@ fd_tpool_worker_state( fd_tpool_t const * tpool, assumes tpool is valid, worker_idx in (0,worker_cnt) (yes, open on both ends), caller is not tpool thread worker_idx, task is valid. worker_idx 0 is special and is considered to always be in the EXEC - state so we cannot call exec on it. */ + state so we cannot call exec on it. This function acts as a compiler + memory fence. */ static inline void fd_tpool_exec( fd_tpool_t * tpool, ulong worker_idx, @@ -788,17 +798,16 @@ fd_tpool_exec( fd_tpool_t * tpool, ulong worker_idx, ulong task_m0, ulong task_m1, ulong task_n0, ulong task_n1 ) { fd_tpool_private_worker_t * worker = fd_tpool_private_worker( tpool )[ worker_idx ]; + worker->task = task; + worker->task_tpool = task_tpool; + worker->task_t0 = task_t0; worker->task_t1 = task_t1; + worker->task_args = task_args; + worker->task_reduce = task_reduce; worker->task_stride = task_stride; + worker->task_l0 = task_l0; worker->task_l1 = task_l1; + worker->task_m0 = task_m0; worker->task_m1 = task_m1; + worker->task_n0 = task_n0; worker->task_n1 = task_n1; FD_COMPILER_MFENCE(); - FD_VOLATILE( worker->task ) = task; - FD_VOLATILE( worker->task_tpool ) = task_tpool; - FD_VOLATILE( worker->task_t0 ) = task_t0; FD_VOLATILE( worker->task_t1 ) = task_t1; - FD_VOLATILE( worker->task_args ) = task_args; - FD_VOLATILE( worker->task_reduce ) = task_reduce; FD_VOLATILE( worker->task_stride ) = task_stride; - FD_VOLATILE( worker->task_l0 ) = task_l0; FD_VOLATILE( worker->task_l1 ) = task_l1; - FD_VOLATILE( worker->task_m0 ) = task_m0; FD_VOLATILE( worker->task_m1 ) = task_m1; - FD_VOLATILE( worker->task_n0 ) = task_n0; FD_VOLATILE( worker->task_n1 ) = task_n1; - FD_COMPILER_MFENCE(); - FD_VOLATILE( worker->state ) = FD_TPOOL_WORKER_STATE_EXEC; + worker->state = FD_TPOOL_WORKER_STATE_EXEC; FD_COMPILER_MFENCE(); } @@ -807,15 +816,19 @@ fd_tpool_exec( fd_tpool_t * tpool, ulong worker_idx, input argument checking. Specifically, assumes tpool is valid, worker_idx in (0,worker_cnt) (yes, open on both ends) and caller is not tpool thread worker_idx. worker_idx 0 is considered to always be - in an exec state so we cannot call wait on it. */ + in an exec state so we cannot call wait on it. On return, the caller + atomically observed worker_idx was not in the EXEC state at some + point between when this was called and when this returned. This + function acts as a compiler memory fence. */ static inline void fd_tpool_wait( fd_tpool_t const * tpool, ulong worker_idx ) { - int volatile * vstate = (int volatile *)&(fd_tpool_private_worker( tpool )[ worker_idx ]->state); - int state; + int * _state = &fd_tpool_private_worker( tpool )[ worker_idx ]->state; for(;;) { - state = *vstate; + FD_COMPILER_MFENCE(); + int state = *_state; + FD_COMPILER_MFENCE(); if( FD_LIKELY( state!=FD_TPOOL_WORKER_STATE_EXEC ) ) break; FD_SPIN_PAUSE(); } @@ -884,7 +897,8 @@ fd_tpool_wait( fd_tpool_t const * tpool, As this is used in high performance contexts, this does no input argument checking. Specifically, it assumes tpool is valid, task is - valid, 0<=t01L ) { tpool = fd_tpool_init( tpool_mem, 2UL ); FD_TEST( tpool ); fd_tile_exec_delete( fd_tile_exec_new( 1UL, tile_self_push_main, 0, (char **)tpool ), NULL ); - FD_COMPILER_MFENCE(); - FD_VOLATILE( spin_done ) = 0; - FD_COMPILER_MFENCE(); - fd_tile_exec_t * exec = fd_tile_exec_new( 1UL, tile_spin_main, 0, (char **)fd_type_pun( &spin_done ) ); /* self push */ + char * spin_done = (char *)0UL; + FD_COMPILER_MFENCE(); /* FIXME: give fd_tile_exec_new stronger fencing semantics */ + fd_tile_exec_t * exec = fd_tile_exec_new( 1UL, tile_spin_main, 0, &spin_done ); FD_TEST( exec ); FD_TEST( !fd_tpool_worker_push( tpool, 1UL, NULL, 0UL ) ); /* Already in use */ - FD_COMPILER_MFENCE(); - FD_VOLATILE( spin_done ) = 1; - FD_COMPILER_MFENCE(); + spin_done = (char *)1UL; fd_tile_exec_delete( exec, NULL ); FD_TEST( fd_tpool_fini( tpool )==(void *)tpool_mem ); @@ -457,15 +446,11 @@ main( int argc, if( tile_cnt>1L ) { ulong worker_idx = tile_cnt-1UL; - FD_COMPILER_MFENCE(); - FD_VOLATILE( spin_done ) = 0; - FD_COMPILER_MFENCE(); + ulong spin_done = 0UL; fd_tpool_exec( tpool,worker_idx, worker_spin, &spin_done, 1UL,2UL,(void *)3UL,(void *)4UL,5UL,6UL,7UL,8UL,9UL,10UL,11UL ); FD_TEST( fd_tpool_worker_state( tpool, worker_idx )==FD_TPOOL_WORKER_STATE_EXEC ); FD_TEST( !fd_tpool_worker_pop( tpool ) ); /* already in use */ - FD_COMPILER_MFENCE(); - FD_VOLATILE( spin_done ) = 1; - FD_COMPILER_MFENCE(); + spin_done = 1UL; fd_tpool_wait( tpool,worker_idx ); FD_TEST( fd_tpool_worker_state( tpool, worker_idx )==FD_TPOOL_WORKER_STATE_IDLE ); } @@ -646,14 +631,12 @@ main( int argc, FD_LOG_NOTICE(( "Testing FD_FOR_ALL" )); for( ulong rem=100000UL; rem; rem-- ) { - FD_COMPILER_MFENCE(); test_t0 = fd_rng_ulong_roll( rng, tile_cnt ); test_t1 = fd_rng_ulong_roll( rng, tile_cnt ); fd_swap_if( test_t1> 1ul; ulong pn_mask = pn_win - 1ul; @@ -578,20 +577,18 @@ fd_quic_reconstruct_pkt_num( ulong * pkt_number, // The following code calculates a candidate value and // makes sure it's within the packet number window. // Note the extra checks to prevent overflow and underflow. - ulong candidate_pn = ( exp_pkt_number & ~pn_mask ) | truncated_pn; + ulong candidate_pn = ( exp_pkt_number & ~pn_mask ) | pktnum_comp; if( candidate_pn + pn_hwin <= exp_pkt_number && candidate_pn + pn_win < ( 1ul << 62ul ) ) { - *pkt_number = candidate_pn + pn_win; - return; + return candidate_pn + pn_win; } if( candidate_pn > exp_pkt_number + pn_hwin && candidate_pn >= pn_win ) { - *pkt_number = candidate_pn - pn_win; - return; + return candidate_pn - pn_win; } - *pkt_number = candidate_pn; + return candidate_pn; } static void @@ -1370,8 +1367,6 @@ fd_quic_handle_v1_initial( fd_quic_t * quic, RFC 9001 specifies use of the TLS_AES_128_GCM_SHA256_ID suite for initial secrets and keys. */ - uint const enc_level = fd_quic_enc_level_initial_id; - /* Parse initial packet */ fd_quic_initial_t initial[1] = {0}; @@ -1385,8 +1380,10 @@ fd_quic_handle_v1_initial( fd_quic_t * quic, /* len indicated the number of bytes after the packet number offset so verify this value is within the packet */ - ulong len = (ulong)( initial->pkt_num_pnoff + initial->len ); - if( FD_UNLIKELY( len > cur_sz ) ) { + ulong pn_offset = initial->pkt_num_pnoff; + ulong body_sz = initial->len; /* length of packet number, frames, and auth tag */ + ulong tot_sz = pn_offset + body_sz; + if( FD_UNLIKELY( tot_sz > cur_sz ) ) { FD_DEBUG( FD_LOG_DEBUG(( "Bogus initial packet length" )) ); return FD_QUIC_PARSE_FAIL; } @@ -1630,76 +1627,57 @@ fd_quic_handle_v1_initial( fd_quic_t * quic, } } - if( FD_UNLIKELY( !fd_uint_extract_bit( conn->keys_avail, (int)enc_level ) ) ) { + if( FD_UNLIKELY( !fd_uint_extract_bit( conn->keys_avail, fd_quic_enc_level_initial_id ) ) ) { return FD_QUIC_PARSE_FAIL; } /* Decrypt incoming packet */ /* header protection needs the offset to the packet number */ - ulong pn_offset = initial->pkt_num_pnoff; - - ulong body_sz = initial->len; /* not a protected field */ - /* length of payload + num packet bytes */ - - ulong pkt_number = ULONG_MAX; - ulong pkt_number_sz = ULONG_MAX; - ulong tot_sz = ULONG_MAX; # if !FD_QUIC_DISABLE_CRYPTO - /* this decrypts the header */ - int server = conn->server; + /* this decrypts the header */ + int server = conn->server; - if( FD_UNLIKELY( - fd_quic_crypto_decrypt_hdr( cur_ptr, cur_sz, - pn_offset, - &conn->keys[enc_level][!server] ) != FD_QUIC_SUCCESS ) ) { - /* As this is an INITIAL packet, change the status to DEAD, and allow - it to be reaped */ - FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt_hdr failed" )) ); - conn->state = FD_QUIC_CONN_STATE_DEAD; - fd_quic_svc_schedule( state, conn, FD_QUIC_SVC_INSTANT ); - quic->metrics.conn_aborted_cnt++; - quic->metrics.pkt_decrypt_fail_cnt++; - return FD_QUIC_PARSE_FAIL; - } + if( FD_UNLIKELY( + fd_quic_crypto_decrypt_hdr( cur_ptr, cur_sz, + pn_offset, + &conn->keys[0][!server] ) != FD_QUIC_SUCCESS ) ) { + /* As this is an INITIAL packet, change the status to DEAD, and allow + it to be reaped */ + FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt_hdr failed" )) ); + conn->state = FD_QUIC_CONN_STATE_DEAD; + fd_quic_svc_schedule( state, conn, FD_QUIC_SVC_INSTANT ); + quic->metrics.conn_aborted_cnt++; + quic->metrics.pkt_decrypt_fail_cnt++; + return FD_QUIC_PARSE_FAIL; + } # endif /* !FD_QUIC_DISABLE_CRYPTO */ - /* TODO should we avoid looking at the packet number here - since the packet integrity is checked in fd_quic_crypto_decrypt? */ - - /* number of bytes in the packet header */ - pkt_number_sz = fd_quic_h0_pkt_num_len( cur_ptr[0] ) + 1u; - tot_sz = pn_offset + body_sz; /* total including header and payload */ - - /* now we have decrypted packet number */ - pkt_number = fd_quic_pktnum_decode( cur_ptr+pn_offset, pkt_number_sz ); - FD_DEBUG( FD_LOG_DEBUG(( "initial pkt_number: %lu", (ulong)pkt_number )) ); - - /* packet number space */ - uint pn_space = fd_quic_enc_level_to_pn_space( enc_level ); + ulong pkt_number_sz = fd_quic_h0_pkt_num_len( cur_ptr[0] ) + 1u; + ulong pktnum_comp = fd_quic_pktnum_decode( cur_ptr+pn_offset, pkt_number_sz ); - /* reconstruct packet number */ - fd_quic_reconstruct_pkt_num( &pkt_number, pkt_number_sz, conn->exp_pkt_number[pn_space] ); - - /* set packet number on the context */ - pkt->pkt_number = pkt_number; + /* reconstruct packet number */ + ulong pkt_number = fd_quic_reconstruct_pkt_num( pktnum_comp, pkt_number_sz, conn->exp_pkt_number[0] ); # if !FD_QUIC_DISABLE_CRYPTO - /* NOTE from rfc9002 s3 - It is permitted for some packet numbers to never be used, leaving intentional gaps. */ - /* this decrypts the header and payload */ - if( FD_UNLIKELY( - fd_quic_crypto_decrypt( cur_ptr, tot_sz, - pn_offset, - pkt_number, - &conn->keys[enc_level][!server] ) != FD_QUIC_SUCCESS ) ) { - FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt failed" )) ); - quic->metrics.pkt_decrypt_fail_cnt++; - return FD_QUIC_PARSE_FAIL; - } + /* NOTE from rfc9002 s3 + It is permitted for some packet numbers to never be used, leaving intentional gaps. */ + /* this decrypts the header and payload */ + if( FD_UNLIKELY( + fd_quic_crypto_decrypt( cur_ptr, tot_sz, + pn_offset, + pkt_number, + &conn->keys[0][!server] ) != FD_QUIC_SUCCESS ) ) { + FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt failed" )) ); + quic->metrics.pkt_decrypt_fail_cnt++; + return FD_QUIC_PARSE_FAIL; + } # endif /* FD_QUIC_DISABLE_CRYPTO */ + /* set packet number on the context */ + pkt->pkt_number = pkt_number; + if( FD_UNLIKELY( body_sz < pkt_number_sz + FD_QUIC_CRYPTO_TAG_SZ ) ) { return FD_QUIC_PARSE_FAIL; } @@ -1748,11 +1726,7 @@ fd_quic_handle_v1_initial( fd_quic_t * quic, conn->flags &= ~( FD_QUIC_CONN_FLAGS_PING_SENT | FD_QUIC_CONN_FLAGS_PING ); /* update expected packet number */ - do { - uint pn_space = fd_quic_enc_level_to_pn_space( enc_level ); - ulong next_pkt_number = pkt_number + 1UL; - conn->exp_pkt_number[pn_space] = fd_ulong_max( conn->exp_pkt_number[pn_space], next_pkt_number ); - } while(0); + conn->exp_pkt_number[0] = fd_ulong_max( conn->exp_pkt_number[0], pkt_number+1UL ); FD_DEBUG( FD_LOG_DEBUG(( "new connection success" )) ); @@ -1771,14 +1745,12 @@ fd_quic_handle_v1_handshake( uchar * cur_ptr, ulong cur_sz ) { - uint const enc_level = fd_quic_enc_level_handshake_id; - if( FD_UNLIKELY( !conn ) ) { quic->metrics.pkt_no_conn_cnt++; return FD_QUIC_PARSE_FAIL; } - if( FD_UNLIKELY( !fd_uint_extract_bit( conn->keys_avail, (int)enc_level ) ) ) { + if( FD_UNLIKELY( !fd_uint_extract_bit( conn->keys_avail, fd_quic_enc_level_handshake_id ) ) ) { quic->metrics.pkt_decrypt_fail_cnt++; return FD_QUIC_PARSE_FAIL; } @@ -1815,10 +1787,6 @@ fd_quic_handle_v1_handshake( ulong body_sz = handshake->len; /* not a protected field */ /* length of payload + num packet bytes */ - ulong pkt_number = ULONG_MAX; - ulong pkt_number_sz = ULONG_MAX; - ulong tot_sz = ULONG_MAX; - # if !FD_QUIC_DISABLE_CRYPTO /* this decrypts the header */ int server = conn->server; @@ -1826,27 +1794,21 @@ fd_quic_handle_v1_handshake( if( FD_UNLIKELY( fd_quic_crypto_decrypt_hdr( cur_ptr, cur_sz, pn_offset, - &conn->keys[enc_level][!server] ) != FD_QUIC_SUCCESS ) ) { + &conn->keys[2][!server] ) != FD_QUIC_SUCCESS ) ) { quic->metrics.pkt_decrypt_fail_cnt++; return FD_QUIC_PARSE_FAIL; } # endif /* !FD_QUIC_DISABLE_CRYPTO */ /* number of bytes in the packet header */ - pkt_number_sz = fd_quic_h0_pkt_num_len( cur_ptr[0] ) + 1u; - tot_sz = pn_offset + body_sz; /* total including header and payload */ + ulong pkt_number_sz = fd_quic_h0_pkt_num_len( cur_ptr[0] ) + 1u; + ulong tot_sz = pn_offset + body_sz; /* total including header and payload */ /* now we have decrypted packet number */ - pkt_number = fd_quic_pktnum_decode( cur_ptr+pn_offset, pkt_number_sz ); - - /* packet number space */ - uint pn_space = fd_quic_enc_level_to_pn_space( enc_level ); + ulong pktnum_comp = fd_quic_pktnum_decode( cur_ptr+pn_offset, pkt_number_sz ); /* reconstruct packet number */ - fd_quic_reconstruct_pkt_num( &pkt_number, pkt_number_sz, conn->exp_pkt_number[pn_space] ); - - /* set packet number on the context */ - pkt->pkt_number = pkt_number; + ulong pkt_number = fd_quic_reconstruct_pkt_num( pktnum_comp, pkt_number_sz, conn->exp_pkt_number[1] ); /* NOTE from rfc9002 s3 It is permitted for some packet numbers to never be used, leaving intentional gaps. */ @@ -1857,7 +1819,7 @@ fd_quic_handle_v1_handshake( fd_quic_crypto_decrypt( cur_ptr, tot_sz, pn_offset, pkt_number, - &conn->keys[enc_level][!server] ) != FD_QUIC_SUCCESS ) ) { + &conn->keys[2][!server] ) != FD_QUIC_SUCCESS ) ) { /* remove connection from map, and insert into free list */ FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt failed" )) ); quic->metrics.pkt_decrypt_fail_cnt++; @@ -1865,6 +1827,9 @@ fd_quic_handle_v1_handshake( } # endif /* FD_QUIC_DISABLE_CRYPTO */ + /* set packet number on the context */ + pkt->pkt_number = pkt_number; + /* check body size large enough for required elements */ if( FD_UNLIKELY( body_sz < pkt_number_sz + FD_QUIC_CRYPTO_TAG_SZ ) ) { return FD_QUIC_PARSE_FAIL; @@ -1874,9 +1839,7 @@ fd_quic_handle_v1_handshake( > A server stops sending and processing Initial packets when it > receives its first Handshake packet. */ fd_quic_abandon_enc_level( conn, fd_quic_enc_level_initial_id ); - if( FD_UNLIKELY( enc_level > conn->peer_enc_level ) ) { - conn->peer_enc_level = (uchar) enc_level; - } + conn->peer_enc_level = (uchar)fd_uchar_max( conn->peer_enc_level, fd_quic_enc_level_handshake_id ); /* handle frames */ ulong payload_off = pn_offset + pkt_number_sz; @@ -1910,11 +1873,7 @@ fd_quic_handle_v1_handshake( conn->flags &= ~( FD_QUIC_CONN_FLAGS_PING_SENT | FD_QUIC_CONN_FLAGS_PING ); /* update expected packet number */ - do { - uint pn_space = fd_quic_enc_level_to_pn_space( enc_level ); - ulong next_pkt_number = pkt_number + 1UL; - conn->exp_pkt_number[pn_space] = fd_ulong_max( conn->exp_pkt_number[pn_space], next_pkt_number ); - } while(0); + conn->exp_pkt_number[1] = fd_ulong_max( conn->exp_pkt_number[1], pkt_number+1UL ); /* return number of bytes consumed */ return tot_sz; @@ -2078,7 +2037,7 @@ fd_quic_handle_v1_one_rtt( fd_quic_t * quic, } ulong pn_offset = 1UL + FD_QUIC_CONN_ID_SZ; - uint enc_level = pkt->enc_level = fd_quic_enc_level_appdata_id; + pkt->enc_level = fd_quic_enc_level_appdata_id; if( FD_UNLIKELY( !fd_uint_extract_bit( conn->keys_avail, fd_quic_enc_level_appdata_id ) ) ) { quic->metrics.pkt_decrypt_fail_cnt++; return FD_QUIC_PARSE_FAIL; @@ -2086,52 +2045,49 @@ fd_quic_handle_v1_one_rtt( fd_quic_t * quic, int server = conn->server; # if !FD_QUIC_DISABLE_CRYPTO - if( FD_UNLIKELY( - fd_quic_crypto_decrypt_hdr( cur_ptr, tot_sz, - pn_offset, - &conn->keys[enc_level][!server] ) != FD_QUIC_SUCCESS ) ) { - FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt_hdr failed" )) ); - quic->metrics.pkt_decrypt_fail_cnt++; - return FD_QUIC_PARSE_FAIL; - } + if( FD_UNLIKELY( + fd_quic_crypto_decrypt_hdr( cur_ptr, tot_sz, + pn_offset, + &conn->keys[3][!server] ) != FD_QUIC_SUCCESS ) ) { + FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt_hdr failed" )) ); + quic->metrics.pkt_decrypt_fail_cnt++; + return FD_QUIC_PARSE_FAIL; + } # endif /* !FD_QUIC_DISABLE_CRYPTO */ - uint first = (uint)cur_ptr[0]; - uint pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u; - uint key_phase = fd_quic_one_rtt_key_phase( first ); - - /* reconstruct packet number */ - ulong pkt_number = fd_quic_pktnum_decode( cur_ptr+pn_offset, pkt_number_sz ); - uint pn_space = fd_quic_enc_level_to_pn_space( enc_level ); - fd_quic_reconstruct_pkt_num( &pkt_number, pkt_number_sz, conn->exp_pkt_number[pn_space] ); + uint pkt_number_sz = fd_quic_h0_pkt_num_len( cur_ptr[0] ) + 1u; + uint key_phase = fd_quic_one_rtt_key_phase( cur_ptr[0] ); - /* set packet number on the context */ - pkt->pkt_number = pkt_number; + /* reconstruct packet number */ + ulong pktnum_comp = fd_quic_pktnum_decode( cur_ptr+pn_offset, pkt_number_sz ); + ulong pkt_number = fd_quic_reconstruct_pkt_num( pktnum_comp, pkt_number_sz, conn->exp_pkt_number[2] ); - /* NOTE from rfc9002 s3 - It is permitted for some packet numbers to never be used, leaving intentional gaps. */ + /* NOTE from rfc9002 s3 + It is permitted for some packet numbers to never be used, leaving intentional gaps. */ - /* is current packet in the current key phase? */ - int current_key_phase = conn->key_phase == key_phase; + /* is current packet in the current key phase? */ + int current_key_phase = conn->key_phase == key_phase; # if !FD_QUIC_DISABLE_CRYPTO - /* If the key phase bit flips, decrypt with the new pair of keys - instead. Note that the key phase bit is untrusted at this point. */ - fd_quic_crypto_keys_t * keys = current_key_phase ? &conn->keys[enc_level][!server] - : &conn->new_keys[!server]; - - /* this decrypts the header and payload */ - if( FD_UNLIKELY( - fd_quic_crypto_decrypt( cur_ptr, tot_sz, - pn_offset, - pkt_number, - keys ) != FD_QUIC_SUCCESS ) ) { - /* remove connection from map, and insert into free list */ - quic->metrics.pkt_decrypt_fail_cnt++; - return FD_QUIC_PARSE_FAIL; - } + /* If the key phase bit flips, decrypt with the new pair of keys + instead. Note that the key phase bit is untrusted at this point. */ + fd_quic_crypto_keys_t * keys = current_key_phase ? &conn->keys[3][!server] : &conn->new_keys[!server]; + + /* this decrypts the header and payload */ + if( FD_UNLIKELY( + fd_quic_crypto_decrypt( cur_ptr, tot_sz, + pn_offset, + pkt_number, + keys ) != FD_QUIC_SUCCESS ) ) { + /* remove connection from map, and insert into free list */ + quic->metrics.pkt_decrypt_fail_cnt++; + return FD_QUIC_PARSE_FAIL; + } # endif /* !FD_QUIC_DISABLE_CRYPTO */ + /* set packet number on the context */ + pkt->pkt_number = pkt_number; + if( !current_key_phase ) { /* Decryption succeeded. Commit the key phase update and throw away the old keys. (May cause a few decryption failures if old @@ -2139,9 +2095,7 @@ fd_quic_handle_v1_one_rtt( fd_quic_t * quic, fd_quic_key_update_complete( conn ); } - if( FD_UNLIKELY( enc_level > conn->peer_enc_level ) ) { - conn->peer_enc_level = (uchar)enc_level; - } + conn->peer_enc_level = (uchar)fd_uchar_max( conn->peer_enc_level, fd_quic_enc_level_appdata_id ); /* handle frames */ ulong payload_off = pn_offset + pkt_number_sz; @@ -2174,11 +2128,7 @@ fd_quic_handle_v1_one_rtt( fd_quic_t * quic, conn->flags &= ~( FD_QUIC_CONN_FLAGS_PING_SENT | FD_QUIC_CONN_FLAGS_PING ); /* update expected packet number */ - do { - uint pn_space = fd_quic_enc_level_to_pn_space( enc_level ); - ulong next_pkt_number = pkt_number + 1UL; - conn->exp_pkt_number[pn_space] = fd_ulong_max( conn->exp_pkt_number[pn_space], next_pkt_number ); - } while(0); + conn->exp_pkt_number[2] = fd_ulong_max( conn->exp_pkt_number[2], pkt_number+1UL ); return tot_sz; } @@ -2732,8 +2682,7 @@ void fd_quic_tls_cb_handshake_complete( fd_quic_tls_hs_t * hs, void * context ) { (void)hs; - fd_quic_conn_t * conn = (fd_quic_conn_t*)context; - fd_quic_conn_stream_rx_t * srx = conn->srx; + fd_quic_conn_t * conn = (fd_quic_conn_t *)context; /* need to send quic handshake completion */ switch( conn->state ) { @@ -2751,13 +2700,6 @@ fd_quic_tls_cb_handshake_complete( fd_quic_tls_hs_t * hs, } conn->handshake_complete = 1; conn->state = FD_QUIC_CONN_STATE_HANDSHAKE_COMPLETE; - - if( conn->server ) { - /* Remove flow control limits */ - srx->rx_sup_stream_id = (1UL<<60) + FD_QUIC_STREAM_TYPE_UNI_CLIENT; - srx->rx_max_data = (1UL<<62UL) - 1UL; - } - return; default: @@ -4361,6 +4303,13 @@ fd_quic_conn_create( fd_quic_t * quic, srx->rx_tot_data = 0; srx->rx_streams_active = 0L; + if( state->transport_params.initial_max_streams_uni_present ) { + srx->rx_sup_stream_id = (state->transport_params.initial_max_streams_uni<<2) + FD_QUIC_STREAM_TYPE_UNI_CLIENT; + } + if( state->transport_params.initial_max_data ) { + srx->rx_max_data = state->transport_params.initial_max_data; + } + /* points to free tx space */ conn->tx_ptr = conn->tx_buf; conn->tx_sz = sizeof( conn->tx_buf ); diff --git a/src/waltz/quic/fd_quic_pkt_meta.h b/src/waltz/quic/fd_quic_pkt_meta.h index 2b03602ec6..a532061426 100644 --- a/src/waltz/quic/fd_quic_pkt_meta.h +++ b/src/waltz/quic/fd_quic_pkt_meta.h @@ -64,12 +64,10 @@ typedef struct fd_quic_pkt_meta_var fd_quic_pkt_meta_var_t; used when acks arrive to determine what is being acked specifically */ struct fd_quic_pkt_meta { /* stores metadata about what was sent in the identified packet */ - ulong pkt_number; /* the packet number */ - uchar enc_level; /* every packet is sent at a specific - enc_level */ - uchar pn_space; /* packet number space (must be consistent - with enc_level) */ - uchar var_sz; /* number of populated entries in var */ + ulong pkt_number; /* packet number (in pn_space) */ + uchar enc_level; /* encryption level of packet */ + uchar pn_space; /* packet number space (derived from enc_level) */ + uchar var_sz; /* number of populated entries in var */ /* does/should the referenced packet contain: FD_QUIC_PKT_META_FLAGS_HS_DATA handshake data @@ -88,9 +86,7 @@ struct fd_quic_pkt_meta { # define FD_QUIC_PKT_META_FLAGS_MAX_DATA (1u<<3u) # define FD_QUIC_PKT_META_FLAGS_MAX_STREAMS_UNIDIR (1u<<5u) # define FD_QUIC_PKT_META_FLAGS_CLOSE (1u<<8u) - fd_quic_range_t range; /* range of bytes referred to by this meta */ - /* stream data or crypto data */ - /* we currently do not put both in the same packet */ + fd_quic_range_t range; /* CRYPTO data range; FIXME use pkt_meta var instead */ ulong stream_id; /* if this contains stream data, the stream id, else zero */ diff --git a/src/waltz/quic/fd_quic_private.h b/src/waltz/quic/fd_quic_private.h index 0638d6d99b..d9be0d4333 100644 --- a/src/waltz/quic/fd_quic_private.h +++ b/src/waltz/quic/fd_quic_private.h @@ -296,6 +296,11 @@ fd_quic_cb_stream_notify( fd_quic_t * quic, } +FD_FN_CONST ulong +fd_quic_reconstruct_pkt_num( ulong pktnum_comp, + ulong pktnum_sz, + ulong exp_pkt_number ); + void fd_quic_pkt_meta_retry( fd_quic_t * quic, fd_quic_conn_t * conn, diff --git a/src/waltz/quic/templ/fd_quic_frame.c b/src/waltz/quic/templ/fd_quic_frame.c index 22072abce6..e3cecd29b6 100644 --- a/src/waltz/quic/templ/fd_quic_frame.c +++ b/src/waltz/quic/templ/fd_quic_frame.c @@ -5,8 +5,6 @@ #include "fd_quic_frame.h" #include "../fd_quic_enum.h" -#include "../../../util/fd_util.h" - /* Lookup table for allowed frame types *******************************/ @@ -16,7 +14,7 @@ #define PKT_FLAG_0 (1u< FD_QUIC_PKT_TYPE_ONE_RTT ) ) return 0; - if( FD_UNLIKELY( frame_type >= FD_QUIC_FRAME_TYPE_CNT ) ) return 0; - return !!( fd_quic_frame_type_flags[frame_type] & (1u<4 ) ) return 0; + if( FD_UNLIKELY( frame_type>=FD_QUIC_FRAME_TYPE_CNT ) ) return 0; + return !!( fd_quic_frame_type_flags[frame_type] & (1u<quic )->transport_params; + params->initial_max_data = 987654321UL; + params->initial_max_streams_uni = 8192UL; + + /* Create a connection that's not yet established */ + ulong our_conn_id = 1234UL; + fd_quic_conn_id_t peer_conn_id = fd_quic_conn_id_new( &our_conn_id, 8UL ); + uint dst_ip_addr = FD_IP4_ADDR( 192, 168, 1, 1 ); + ushort dst_udp_port = 8080; + fd_quic_conn_t * conn = fd_quic_conn_create( sandbox->quic, our_conn_id, &peer_conn_id, dst_ip_addr, dst_udp_port, 1 ); + FD_TEST( conn ); + FD_TEST( conn->state == FD_QUIC_CONN_STATE_HANDSHAKE ); + + /* Ensure stream frames are accepted + (Rationale: The application encryption level becomes available before + the handshake is confirmed; Quota would have been granted already via + QUIC transport params) */ + uchar buf[ 1024 ]; + fd_quic_stream_frame_t stream_frame = + { .stream_id = FD_QUIC_STREAM_TYPE_UNI_CLIENT, + .length_opt = 1, + .length = 1UL }; + ulong sz = fd_quic_encode_stream_frame( buf, sizeof(buf), &stream_frame ); + FD_TEST( sz!=FD_QUIC_ENCODE_FAIL ); + buf[ sz++ ] = '0'; + fd_quic_sandbox_send_lone_frame( sandbox, conn, buf, sz ); + FD_TEST( conn->state == FD_QUIC_CONN_STATE_HANDSHAKE ); /* conn not killed */ + + /* Double check RX limits */ + FD_TEST( conn->srx->rx_sup_stream_id == 32770UL ); /* (8192<<2)+2 */ + FD_TEST( conn->srx->rx_max_data == 987654321UL ); +} + static __attribute__((noinline)) void test_quic_parse_path_challenge( void ) { fd_quic_path_challenge_frame_t path_challenge[1]; @@ -337,6 +376,7 @@ main( int argc, test_quic_ping_frame ( sandbox, rng ); test_quic_server_alpn_fail ( sandbox, rng ); test_quic_pktnum_skip ( sandbox, rng ); + test_quic_conn_initial_limits ( sandbox, rng ); test_quic_parse_path_challenge(); /* Wind down */