diff --git a/crates/beacon_state/tile/src/tile.rs b/crates/beacon_state/tile/src/tile.rs index 9e2fe719..67b2082b 100644 --- a/crates/beacon_state/tile/src/tile.rs +++ b/crates/beacon_state/tile/src/tile.rs @@ -673,10 +673,6 @@ impl BeaconStateTile { // column validation/availability), not here; see the TODO at its // column-request path. EngineResp::GetBlobs(_) => {} - // Payload-body reconstruction is unneeded: the store persists and - // serves full SignedBeaconBlocks, so there is nothing to rebuild - // from EL bodies. - EngineResp::GetPayloadBodies(_) => {} } } diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 8a026e8b..5776ce49 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -26,13 +26,11 @@ pub use crate::{ BeaconApiResponse, BeaconStateEvent, BlockLookup, BlockSource, BlockStage, ClusterIn, ClusterMsgIn, ClusterMsgOut, ColumnOrigin, Consumer as TConsumer, DataColumnsEvent, ELSyncStatus, EngineFcuReq, EngineFcuResp, EngineGetBlobsReq, EngineGetBlobsResp, - EngineGetPayloadBodiesByHashReq, EngineGetPayloadBodiesByRangeReq, - EngineGetPayloadBodiesResp, EngineGetPayloadReq, EngineGetPayloadResp, EngineHealthEvent, - EngineNewPayloadEnvelopeReq, EngineNewPayloadReq, EngineNewPayloadResp, - EnginePreparePayloadReq, EngineReq, EngineResp, Error as TCacheError, GossipMsgIn, - GossipMsgOut, HeadChange, HeadRoots, IpBytes, LOCAL_GOSSIP_STREAM_ID, - LocalAttestationFailure, LocalAttestationResult, MAX_BLOBS_PER_BLOCK, - MAX_PAYLOAD_BODIES_PER_REQ, MULTISTREAM_V1, MultiProducer as TMultiProducer, NewGossipMsg, + EngineGetPayloadReq, EngineGetPayloadResp, EngineHealthEvent, EngineNewPayloadEnvelopeReq, + EngineNewPayloadReq, EngineNewPayloadResp, EnginePreparePayloadReq, EngineReq, EngineResp, + Error as TCacheError, GossipMsgIn, GossipMsgOut, HeadChange, HeadRoots, IpBytes, + LOCAL_GOSSIP_STREAM_ID, LocalAttestationFailure, LocalAttestationResult, + MAX_BLOBS_PER_BLOCK, MULTISTREAM_V1, MultiProducer as TMultiProducer, NewGossipMsg, P2pConnectionStats, P2pSend, P2pStreamId, PREFILL_SLOTS, PayloadResolution, PayloadValidationStatus, PeerControl, PeerEvent, PeerScores, PeerStats, PeerStatus, PeerTopicScores, PendingSubReservation, Prefill, Producer as TProducer, REJECT_RESPONSE, diff --git a/crates/common/src/spine.rs b/crates/common/src/spine.rs index d34352be..5f280629 100644 --- a/crates/common/src/spine.rs +++ b/crates/common/src/spine.rs @@ -5,16 +5,15 @@ pub use messages::{ AgentString, BeaconApiRequest, BeaconApiResponse, BeaconStateEvent, BlockLookup, BlockSource, BlockStage, ClusterIn, ClusterMsgIn, ClusterMsgOut, ColumnOrigin, DataColumnsEvent, ELSyncStatus, EngineFcuReq, EngineFcuResp, EngineGetBlobsReq, EngineGetBlobsResp, - EngineGetPayloadBodiesByHashReq, EngineGetPayloadBodiesByRangeReq, EngineGetPayloadBodiesResp, EngineGetPayloadReq, EngineGetPayloadResp, EngineHealthEvent, EngineNewPayloadEnvelopeReq, EngineNewPayloadReq, EngineNewPayloadResp, EnginePreparePayloadReq, EngineReq, EngineResp, GossipMsgIn, GossipMsgOut, HeadChange, HeadRoots, IpBytes, LocalAttestationFailure, - LocalAttestationResult, MAX_BLOBS_PER_BLOCK, MAX_PAYLOAD_BODIES_PER_REQ, NewGossipMsg, - P2pConnectionStats, P2pSend, PREFILL_SLOTS, PayloadResolution, PayloadValidationStatus, - PeerControl, PeerEvent, PeerScores, PeerStats, PeerStatus, PeerTopicScores, Prefill, - ReplayBlock, RpcInbound, RpcOutbound, RpcRequest, RpcRequestInbound, RpcRequestOutbound, - RpcResponse, RpcResponseInbound, RpcResponseOutbound, RpcSeverity, SelfBuiltGossip, - ServedBlock, SszCache, SyncNeed, SyncUpdate, SyncingStrategy, WithdrawalInline, + LocalAttestationResult, MAX_BLOBS_PER_BLOCK, NewGossipMsg, P2pConnectionStats, P2pSend, + PREFILL_SLOTS, PayloadResolution, PayloadValidationStatus, PeerControl, PeerEvent, PeerScores, + PeerStats, PeerStatus, PeerTopicScores, Prefill, ReplayBlock, RpcInbound, RpcOutbound, + RpcRequest, RpcRequestInbound, RpcRequestOutbound, RpcResponse, RpcResponseInbound, + RpcResponseOutbound, RpcSeverity, SelfBuiltGossip, ServedBlock, SszCache, SyncNeed, SyncUpdate, + SyncingStrategy, WithdrawalInline, }; pub use stream_id::{LOCAL_GOSSIP_STREAM_ID, P2pStreamId}; pub use stream_protocol::{ diff --git a/crates/common/src/spine/messages.rs b/crates/common/src/spine/messages.rs index 4fc43b46..3e7769d3 100644 --- a/crates/common/src/spine/messages.rs +++ b/crates/common/src/spine/messages.rs @@ -1082,9 +1082,6 @@ pub enum SyncingStrategy { /// evolves). pub const MAX_BLOBS_PER_BLOCK: usize = 21; -/// Maximum number of block hashes in a single `getPayloadBodiesByHash` request. -pub const MAX_PAYLOAD_BODIES_PER_REQ: usize = 128; - /// Execution-payload validation result returned by the EL. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] @@ -1254,38 +1251,6 @@ impl EngineGetBlobsResp { } } -/// `engine_getPayloadBodiesByHashV1` request. -/// `hashes[..hash_count]` are the execution block hashes to fetch bodies for. -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct EngineGetPayloadBodiesByHashReq { - pub id: u64, - pub hash_count: u8, - pub hashes: [[u8; 32]; MAX_PAYLOAD_BODIES_PER_REQ], -} - -/// `engine_getPayloadBodiesByRangeV1` request. Fully inline. -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct EngineGetPayloadBodiesByRangeReq { - pub id: u64, - pub start: u64, - pub count: u64, -} - -/// Response to either `getPayloadBodiesByHash` or `getPayloadBodiesByRange`. -/// When `ok` is true, `data` is a TCache slot with binary-encoded bodies: -/// `[u32 count] ([u8 present] [u32 tx_count] ([u32 tx_len][tx bytes])* [u32 -/// withdrawal_count] ([u32 index][u32 validator_index][20B address][u64 -/// amount])*)*` `present == 0` means the entry is null (block missing). -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct EngineGetPayloadBodiesResp { - pub id: u64, - pub ok: bool, - pub data: TCacheRead, -} - /// Multiplexed engine request. A single spine queue carries FCU, /// new-payload, and raw passthrough requests, preserving strict FIFO ordering. #[derive(Clone, Copy, Debug)] @@ -1298,8 +1263,6 @@ pub enum EngineReq { PreparePayload(EnginePreparePayloadReq), GetPayload(EngineGetPayloadReq), GetBlobs(EngineGetBlobsReq), - GetPayloadBodiesByHash(EngineGetPayloadBodiesByHashReq), - GetPayloadBodiesByRange(EngineGetPayloadBodiesByRangeReq), } /// Multiplexed engine response. @@ -1311,7 +1274,6 @@ pub enum EngineResp { NewPayload(EngineNewPayloadResp), GetPayload(EngineGetPayloadResp), GetBlobs(EngineGetBlobsResp), - GetPayloadBodies(EngineGetPayloadBodiesResp), } /// Sync status of the attached execution layer. diff --git a/crates/config/src/engine_config.rs b/crates/config/src/engine_config.rs index f22680f4..dcfa5c73 100644 --- a/crates/config/src/engine_config.rs +++ b/crates/config/src/engine_config.rs @@ -8,9 +8,7 @@ fn default_max_connections() -> usize { 32 } -// Clears every engine-api per-method minimum-wait floor (the highest is -// getPayloadBodiesBy* at 10 s) with margin: this deadline breaks wedged -// connections, it is not a latency target. +// The deadline breaks wedged connections; it is not a latency target. fn default_request_timeout_secs() -> u64 { 12 } diff --git a/crates/engine_api/src/api.rs b/crates/engine_api/src/api.rs index 59e4b38b..107639de 100644 --- a/crates/engine_api/src/api.rs +++ b/crates/engine_api/src/api.rs @@ -154,10 +154,6 @@ impl EngineApi { ReqKind::NewPayload(block_root) => out.new_payload(block_root, response), ReqKind::GetPayloadFetch(spine_id) => out.get_payload(spine_id, response), ReqKind::GetBlobs { block_root, slot } => out.get_blobs(block_root, slot, response), - ReqKind::GetPayloadBodiesByHash(spine_id) | - ReqKind::GetPayloadBodiesByRange(spine_id) => { - out.payload_bodies(spine_id, response) - } }); } diff --git a/crates/engine_api/src/client.rs b/crates/engine_api/src/client.rs index 9c6822b4..23dc4d9e 100644 --- a/crates/engine_api/src/client.rs +++ b/crates/engine_api/src/client.rs @@ -25,8 +25,6 @@ const OUR_CAPABILITIES: &[&str] = &[ "engine_getPayloadV3", "engine_getPayloadV4", "engine_getBlobsV2", - "engine_getPayloadBodiesByHashV1", - "engine_getPayloadBodiesByRangeV1", "engine_getClientVersionV1", ]; @@ -39,8 +37,6 @@ pub enum ReqKind { NewPayload(B256), // block root GetPayloadFetch(u64), GetBlobs { block_root: B256, slot: u64 }, - GetPayloadBodiesByHash(u64), - GetPayloadBodiesByRange(u64), } pub struct EngineClient { @@ -254,26 +250,6 @@ pub fn get_blobs(c: &mut EngineClient, params: simd_json::OwnedValue, block_root c.pending_requests.insert(id, ReqKind::GetBlobs { block_root, slot }); } -pub fn get_payload_bodies_by_hash( - c: &mut EngineClient, - params: simd_json::OwnedValue, - req_id: u64, -) { - let (id, body) = make_rpc_body(&mut c.id, "engine_getPayloadBodiesByHashV1", params); - enqueue(c, id, &body); - c.pending_requests.insert(id, ReqKind::GetPayloadBodiesByHash(req_id)); -} - -pub fn get_payload_bodies_by_range( - c: &mut EngineClient, - params: simd_json::OwnedValue, - req_id: u64, -) { - let (id, body) = make_rpc_body(&mut c.id, "engine_getPayloadBodiesByRangeV1", params); - enqueue(c, id, &body); - c.pending_requests.insert(id, ReqKind::GetPayloadBodiesByRange(req_id)); -} - pub fn get_sync_status(c: &mut EngineClient) { let (id, body) = make_rpc_body(&mut c.id, "eth_syncing", simd_json::json!([])); enqueue(c, id, &body); diff --git a/crates/engine_api/src/req_handlers.rs b/crates/engine_api/src/req_handlers.rs index 9206dbac..723fbac5 100644 --- a/crates/engine_api/src/req_handlers.rs +++ b/crates/engine_api/src/req_handlers.rs @@ -1,7 +1,6 @@ use flux::spine::FluxSpine; use silver_common::{ BlockSource, EngineFcuReq, EngineFcuResp, EngineGetBlobsReq, EngineGetBlobsResp, - EngineGetPayloadBodiesByHashReq, EngineGetPayloadBodiesByRangeReq, EngineGetPayloadBodiesResp, EngineGetPayloadReq, EngineGetPayloadResp, EngineNewPayloadEnvelopeReq, EngineNewPayloadReq, EngineNewPayloadResp, EnginePreparePayloadReq, EngineReq, EngineResp, PayloadValidationStatus, SilverSpine, TCacheRead, TProducer, TRandomAccess, @@ -9,10 +8,7 @@ use silver_common::{ use crate::{ EngineClient, EngineError, - client::{ - get_blobs, get_payload, get_payload_bodies_by_hash, get_payload_bodies_by_range, send_fcu, - send_new_payload, send_new_payload_envelope, - }, + client::{get_blobs, get_payload, send_fcu, send_new_payload, send_new_payload_envelope}, resp_handlers::write_tcache, types::{ForkchoiceState, PayloadAttributesV3, Withdrawal}, }; @@ -36,14 +32,12 @@ pub(crate) fn handle_request( EngineReq::PreparePayload(r) => handle_prepare_payload(client, *r), EngineReq::GetPayload(r) => handle_get_payload(client, *r), EngineReq::GetBlobs(r) => handle_get_blobs(client, r), - EngineReq::GetPayloadBodiesByHash(r) => handle_get_payload_bodies_by_hash(client, r), - EngineReq::GetPayloadBodiesByRange(r) => handle_get_payload_bodies_by_range(client, r), } } /// Unsafe no-EL testing mode: answer each request with a synthetic VALID -/// response without contacting an execution client. Built payloads and payload -/// bodies can't be fabricated, so those return `ok: false`; blob fetches answer +/// response without contacting an execution client. Built payloads can't be +/// fabricated, so those return `ok: false`; blob fetches answer /// as a healthy EL that simply holds none of the requested blobs. #[inline] pub(crate) fn handle_request_no_el( @@ -93,20 +87,6 @@ pub(crate) fn handle_request_no_el( }), None => EngineResp::GetBlobs(EngineGetBlobsResp::failed(r.block_root, r.slot)), }, - EngineReq::GetPayloadBodiesByHash(r) => { - EngineResp::GetPayloadBodies(EngineGetPayloadBodiesResp { - id: r.id, - ok: false, - data: unsafe { std::mem::zeroed() }, - }) - } - EngineReq::GetPayloadBodiesByRange(r) => { - EngineResp::GetPayloadBodies(EngineGetPayloadBodiesResp { - id: r.id, - ok: false, - data: unsafe { std::mem::zeroed() }, - }) - } }; producers.engine_resps.produce(&resp.into()); } @@ -211,27 +191,6 @@ fn handle_get_blobs(client: &mut EngineClient, r: &EngineGetBlobsReq) { get_blobs(client, simd_json::json!([hashes]), r.block_root, r.slot); } -#[inline] -fn handle_get_payload_bodies_by_hash( - client: &mut EngineClient, - r: &EngineGetPayloadBodiesByHashReq, -) { - let n = r.hash_count as usize; - let hashes: Vec = - r.hashes[..n].iter().map(|h| format!("0x{}", hex::encode(h))).collect(); - get_payload_bodies_by_hash(client, simd_json::json!([hashes]), r.id); -} - -#[inline] -fn handle_get_payload_bodies_by_range( - client: &mut EngineClient, - r: &EngineGetPayloadBodiesByRangeReq, -) { - let start_hex = format!("0x{:x}", r.start); - let count_hex = format!("0x{:x}", r.count); - get_payload_bodies_by_range(client, simd_json::json!([start_hex, count_hex]), r.id); -} - #[inline] fn handle_prepare_payload(client: &mut EngineClient, r: EnginePreparePayloadReq) { tracing::info!(head = %hex::encode(&r.head_block_hash[..4]), id = r.id, "preparePayload ← spine"); diff --git a/crates/engine_api/src/resp_handlers.rs b/crates/engine_api/src/resp_handlers.rs index d444f8a6..2d6e5f14 100644 --- a/crates/engine_api/src/resp_handlers.rs +++ b/crates/engine_api/src/resp_handlers.rs @@ -1,9 +1,9 @@ use flux::spine::SpineAdapter; use serde::Deserialize; use silver_common::{ - ELSyncStatus, EngineFcuResp, EngineGetBlobsResp, EngineGetPayloadBodiesResp, - EngineGetPayloadResp, EngineHealthEvent, EngineNewPayloadResp, EngineResp, - PayloadValidationStatus, SilverSpine, TCacheProducer, TCacheRead, TProducer, merkle::B256, + ELSyncStatus, EngineFcuResp, EngineGetBlobsResp, EngineGetPayloadResp, EngineHealthEvent, + EngineNewPayloadResp, EngineResp, PayloadValidationStatus, SilverSpine, TCacheProducer, + TCacheRead, TProducer, merkle::B256, }; use simd_json::prelude::{ValueAsArray, ValueAsScalar, ValueObjectAccess}; @@ -11,7 +11,7 @@ use crate::{ EngineError, types::{ ForkchoiceUpdatedResult, PayloadStatus, json_get_blobs_to_tcache, - json_get_payload_bodies_to_tcache, json_get_payload_to_tcache, + json_get_payload_to_tcache, }, }; @@ -65,12 +65,6 @@ pub(crate) fn handle_capabilities_response( if !has("engine_newPayloadV4") { tracing::warn!("EL does not support engine_newPayloadV4"); } - if !has("engine_getPayloadBodiesByHashV1") { - tracing::warn!("EL does not support engine_getPayloadBodiesByHashV1"); - } - if !has("engine_getPayloadBodiesByRangeV1") { - tracing::warn!("EL does not support engine_getPayloadBodiesByRangeV1"); - } let method = if has("engine_getPayloadV4") { "engine_getPayloadV4" } else { FALLBACK }; tracing::info!("capabilities negotiated, using {method}"); method @@ -321,35 +315,6 @@ impl<'a> Responses<'a> { }; self.adapter.produce(EngineResp::GetBlobs(resp)); } - - #[inline] - pub(crate) fn payload_bodies( - &mut self, - spine_id: u64, - response: Result<&mut [u8], EngineError>, - ) { - let resp = match response { - Ok(raw) => match self.encode(raw, json_get_payload_bodies_to_tcache) { - Ok(Some(((), data))) => { - tracing::info!(id = spine_id, "getPayloadBodies ok"); - EngineGetPayloadBodiesResp { id: spine_id, ok: true, data } - } - Ok(None) => { - tracing::warn!("getPayloadBodies TCache full"); - get_payload_bodies_error(spine_id) - } - Err(e) => { - tracing::warn!("getPayloadBodies parse error: {e}"); - get_payload_bodies_error(spine_id) - } - }, - Err(e) => { - tracing::warn!("getPayloadBodies error: {e}"); - get_payload_bodies_error(spine_id) - } - }; - self.adapter.produce(EngineResp::GetPayloadBodies(resp)); - } } #[inline] @@ -390,11 +355,6 @@ fn get_payload_error(id: u64) -> EngineGetPayloadResp { EngineGetPayloadResp { id, ok: false, data: unsafe { std::mem::zeroed() } } } -#[inline] -fn get_payload_bodies_error(id: u64) -> EngineGetPayloadBodiesResp { - EngineGetPayloadBodiesResp { id, ok: false, data: unsafe { std::mem::zeroed() } } -} - #[inline] fn fcu_error(block_root: [u8; 32]) -> EngineFcuResp { EngineFcuResp { diff --git a/crates/engine_api/src/types.rs b/crates/engine_api/src/types.rs index bbe42542..d529f8d4 100644 --- a/crates/engine_api/src/types.rs +++ b/crates/engine_api/src/types.rs @@ -795,67 +795,6 @@ pub(crate) fn json_get_blobs_to_tcache( Ok(blobs_present) } -// --------------------------------------------------------------------------- -// Zero-alloc JSON → TCache frame converter for -// engine_getPayloadBodiesByHashV1 / ByRangeV1 -// -// Wire layout: [u32 count] [u8 present] [u32 tx_count] [u32 len][tx bytes]... -// [u32 withdrawal_count] [44B each] -// --------------------------------------------------------------------------- - -pub(crate) fn json_get_payload_bodies_to_tcache( - raw: &mut [u8], - out: &mut Vec, -) -> Result<(), crate::EngineError> { - use simd_json::prelude::{TypedScalarValue, ValueAsArray, ValueAsScalar, ValueObjectAccess}; - - let root = simd_json::to_borrowed_value(raw).map_err(crate::EngineError::Json)?; - let items = - root.get("result").and_then(|v| v.as_array()).ok_or(crate::EngineError::MissingResult)?; - - out.extend_from_slice(&(items.len() as u32).to_le_bytes()); - - for item in items { - if item.is_null() { - out.push(0); - continue; - } - out.push(1); - - let txs = item - .get("transactions") - .and_then(|v| v.as_array()) - .ok_or_else(|| crate::EngineError::Ssz("missing transactions".into()))?; - out.extend_from_slice(&(txs.len() as u32).to_le_bytes()); - for tx in txs { - let s = tx.as_str().ok_or_else(|| crate::EngineError::Ssz("tx not a string".into()))?; - let s = s.strip_prefix("0x").unwrap_or(s); - let len = s.len() / 2; - out.extend_from_slice(&(len as u32).to_le_bytes()); - let base = out.len(); - out.resize(base + len, 0); - hex::decode_to_slice(s, &mut out[base..]) - .map_err(|e| crate::EngineError::Ssz(e.to_string()))?; - } - - let withdrawals: &[simd_json::BorrowedValue<'_>] = - item.get("withdrawals").and_then(|v| v.as_array()).map(|a| a.as_slice()).unwrap_or(&[]); - out.extend_from_slice(&(withdrawals.len() as u32).to_le_bytes()); - for w in withdrawals { - let index = parse_quantity(fstr(w, "index")?)?; - let validator_index = parse_quantity(fstr(w, "validatorIndex")?)?; - let address = hex_to_fixed::<20>(fstr(w, "address")?)?; - let amount = parse_quantity(fstr(w, "amount")?)?; - out.extend_from_slice(&index.to_le_bytes()); - out.extend_from_slice(&validator_index.to_le_bytes()); - out.extend_from_slice(&address); - out.extend_from_slice(&amount.to_le_bytes()); - } - } - - Ok(()) -} - // --------------------------------------------------------------------------- #[cfg(test)] @@ -1562,57 +1501,4 @@ mod tests { let mut json = br#"{"jsonrpc":"2.0","id":1}"#.to_vec(); assert!(json_get_blobs_to_tcache(&mut json, &mut Vec::new()).is_err()); } - - // --------------------------------------------------------------------------- - // json_get_payload_bodies_to_tcache - // --------------------------------------------------------------------------- - - #[test] - fn json_get_payload_bodies_with_items() { - let mut json = br#"{"result":[{"transactions":["0xdeadbeef","0xcafe"],"withdrawals":[{"index":"0x1","validatorIndex":"0x2","address":"0x1234567890123456789012345678901234567890","amount":"0xa"}]},null]}"#.to_vec(); - let mut out = Vec::new(); - json_get_payload_bodies_to_tcache(&mut json, &mut out).unwrap(); - - let mut pos = 0usize; - assert_eq!(u32::from_le_bytes(out[pos..pos + 4].try_into().unwrap()), 2); - pos += 4; - - // item 0 - assert_eq!(out[pos], 1); - pos += 1; - assert_eq!(u32::from_le_bytes(out[pos..pos + 4].try_into().unwrap()), 2); // tx_count - pos += 4; - let tx0_len = u32::from_le_bytes(out[pos..pos + 4].try_into().unwrap()) as usize; - pos += 4; - assert_eq!(tx0_len, 4); - assert_eq!(&out[pos..pos + 4], &[0xde, 0xad, 0xbe, 0xef]); - pos += tx0_len; - let tx1_len = u32::from_le_bytes(out[pos..pos + 4].try_into().unwrap()) as usize; - pos += 4; - assert_eq!(tx1_len, 2); - assert_eq!(&out[pos..pos + 2], &[0xca, 0xfe]); - pos += tx1_len; - assert_eq!(u32::from_le_bytes(out[pos..pos + 4].try_into().unwrap()), 1); // wd_count - pos += 4; - assert_eq!(u64::from_le_bytes(out[pos..pos + 8].try_into().unwrap()), 1); // index - pos += 8; - assert_eq!(u64::from_le_bytes(out[pos..pos + 8].try_into().unwrap()), 2); // validator_index - pos += 8; - assert_eq!( - &out[pos..pos + 20], - hex::decode("1234567890123456789012345678901234567890").unwrap().as_slice() - ); - pos += 20; - assert_eq!(u64::from_le_bytes(out[pos..pos + 8].try_into().unwrap()), 10); // amount - pos += 8; - - // item 1: null - assert_eq!(out[pos], 0); - } - - #[test] - fn json_get_payload_bodies_missing_result() { - let mut json = br#"{"jsonrpc":"2.0","id":1}"#.to_vec(); - assert!(json_get_payload_bodies_to_tcache(&mut json, &mut Vec::new()).is_err()); - } }