From 4f47b7051ac772e97527a373abafc801ed826d05 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Thu, 30 Jul 2026 08:14:44 -0400 Subject: [PATCH 01/10] fix(drand): cover epochs from `parent_epoch + 1` till current epoch on quicknet branch --- src/beacon/drand.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/beacon/drand.rs b/src/beacon/drand.rs index ad41db5ddfc..14ff7274f89 100644 --- a/src/beacon/drand.rs +++ b/src/beacon/drand.rs @@ -113,10 +113,13 @@ impl BeaconSchedule { prev.round() }; - // We only ever need one entry after drand quicknet upgrade (FIP-0063) if curr_beacon.network().is_unchained() { - let entry = curr_beacon.entry(max_round).await?; - Ok(vec![entry]) + let mut out = Vec::new(); + for covered_epoch in (parent_epoch + 1)..=epoch { + let round = curr_beacon.max_beacon_round_for_epoch(network_version, covered_epoch); + out.push(curr_beacon.entry(round).await?); + } + Ok(out) } else { let mut cur = max_round; let mut out = Vec::new(); From ea57e0cfac7fd4438d4ab2a48691a1264b465f54 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Thu, 30 Jul 2026 09:13:14 -0400 Subject: [PATCH 02/10] chore: include tests --- src/beacon/tests/drand.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/beacon/tests/drand.rs b/src/beacon/tests/drand.rs index 350b8d2399f..36c1c9eda9f 100644 --- a/src/beacon/tests/drand.rs +++ b/src/beacon/tests/drand.rs @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0, MIT use crate::{ - beacon::{Beacon, ChainInfo, DrandBeacon, DrandConfig, DrandNetwork}, + beacon::{ + Beacon, BeaconEntry, BeaconPoint, BeaconSchedule, ChainInfo, DrandBeacon, DrandConfig, + DrandNetwork, + }, shim::version::NetworkVersion, }; use std::borrow::Cow; @@ -144,3 +147,33 @@ fn test_max_beacon_round_for_epoch_quicknet() { ((1598306400 + 3547000 * 30) - 1692803367 - 30) / 3 + 1 ); } + +#[tokio::test] +async fn beacon_entries_for_block_covers_null_rounds_quicknet() { + // (parent epoch, its beacon round, block epoch, expected rounds) + let cases = [ + // Null round at 6216199: entries for both 6216199 and 6216200. + (6216198, 30662982, 6216200, vec![30662992, 30663002]), + // No null round in between: only 6216200's entry. + (6216199, 30662992, 6216200, vec![30663002]), + ]; + + let schedule = BeaconSchedule(vec![BeaconPoint::new(0, new_beacon_quicknet())]); + + for (prev_epoch, prev_epoch_round, epoch, expected_rounds) in cases { + let (_, prev_beacon) = schedule.beacon_for_epoch(prev_epoch).unwrap(); + let prev_beacon_entry = prev_beacon.entry(prev_epoch_round).await.unwrap(); + + let entries = schedule + .beacon_entries_for_block(NetworkVersion::V22, epoch, prev_epoch, &prev_beacon_entry) + .await + .unwrap(); + + let rounds: Vec = entries.iter().map(BeaconEntry::round).collect(); + + assert_eq!( + rounds, expected_rounds, + "epoch {epoch}, parent {prev_epoch}" + ); + } +} From f33f6cec1fe8f77de2389f3bfc134fdf11dc5b4f Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Thu, 30 Jul 2026 14:36:26 -0400 Subject: [PATCH 03/10] chore: apply nit comment --- src/beacon/drand.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/beacon/drand.rs b/src/beacon/drand.rs index 14ff7274f89..86d3464cd14 100644 --- a/src/beacon/drand.rs +++ b/src/beacon/drand.rs @@ -117,7 +117,14 @@ impl BeaconSchedule { let mut out = Vec::new(); for covered_epoch in (parent_epoch + 1)..=epoch { let round = curr_beacon.max_beacon_round_for_epoch(network_version, covered_epoch); - out.push(curr_beacon.entry(round).await?); + out.push( + curr_beacon + .entry(round) + .await + .context(format!( + "failed to fetch beacon entry for epoch {covered_epoch}, round {round}" + ))?, + ); } Ok(out) } else { From 407ab476cc88618b4b6482b9b66aa1e89d0cf503 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Mon, 3 Aug 2026 08:44:50 -0400 Subject: [PATCH 04/10] chore: use smallvec (addressing comment) --- src/beacon/drand.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/beacon/drand.rs b/src/beacon/drand.rs index 86d3464cd14..5b575dfef41 100644 --- a/src/beacon/drand.rs +++ b/src/beacon/drand.rs @@ -25,6 +25,7 @@ use backon::{ExponentialBuilder, Retryable}; use bls_signatures::Serialize as _; use nonzero_ext::nonzero; use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; +use smallvec::SmallVec; use tracing::debug; use url::Url; @@ -88,10 +89,11 @@ impl BeaconSchedule { if cb_epoch != pb_epoch { // Fork logic, take entries from the last two rounds of the new beacon. let round = curr_beacon.max_beacon_round_for_epoch(network_version, epoch); - let mut entries = Vec::with_capacity(2); - entries.push(curr_beacon.entry(round - 1).await?); - entries.push(curr_beacon.entry(round).await?); - return Ok(entries); + + let mut out: SmallVec::<[BeaconEntry; 2]> = SmallVec::new(); + out.push(curr_beacon.entry(round - 1).await?); + out.push(curr_beacon.entry(round).await?); + return Ok(out.to_vec()); } } @@ -113,8 +115,8 @@ impl BeaconSchedule { prev.round() }; + let mut out: SmallVec::<[BeaconEntry; 2]> = SmallVec::new(); if curr_beacon.network().is_unchained() { - let mut out = Vec::new(); for covered_epoch in (parent_epoch + 1)..=epoch { let round = curr_beacon.max_beacon_round_for_epoch(network_version, covered_epoch); out.push( @@ -126,10 +128,9 @@ impl BeaconSchedule { ))?, ); } - Ok(out) + Ok(out.to_vec()) } else { let mut cur = max_round; - let mut out = Vec::new(); while cur > prev_round { // Push all entries from rounds elapsed since the last chain epoch. let entry = curr_beacon.entry(cur).await?; @@ -137,7 +138,7 @@ impl BeaconSchedule { out.push(entry); } out.reverse(); - Ok(out) + Ok(out.to_vec()) } } From fd658b50fc2186189e8496b04a35f6cd2bf96024 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Mon, 3 Aug 2026 08:46:12 -0400 Subject: [PATCH 05/10] chore: use `collect_vec` at `tests/drand.rs` --- src/beacon/tests/drand.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/beacon/tests/drand.rs b/src/beacon/tests/drand.rs index 36c1c9eda9f..03d38638ecd 100644 --- a/src/beacon/tests/drand.rs +++ b/src/beacon/tests/drand.rs @@ -1,6 +1,8 @@ // Copyright 2019-2026 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use itertools::Itertools; + use crate::{ beacon::{ Beacon, BeaconEntry, BeaconPoint, BeaconSchedule, ChainInfo, DrandBeacon, DrandConfig, @@ -169,7 +171,7 @@ async fn beacon_entries_for_block_covers_null_rounds_quicknet() { .await .unwrap(); - let rounds: Vec = entries.iter().map(BeaconEntry::round).collect(); + let rounds: Vec = entries.iter().map(BeaconEntry::round).collect_vec(); assert_eq!( rounds, expected_rounds, From ded0580d4db54c9c99bfcfef3f17abbac9daaaed Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Mon, 3 Aug 2026 13:48:07 -0400 Subject: [PATCH 06/10] chore: use Vec instead of `SmallVec`, use `with_context` --- src/beacon/drand.rs | 8 ++++---- src/beacon/tests/drand.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/beacon/drand.rs b/src/beacon/drand.rs index 5b575dfef41..01fa241e736 100644 --- a/src/beacon/drand.rs +++ b/src/beacon/drand.rs @@ -115,7 +115,7 @@ impl BeaconSchedule { prev.round() }; - let mut out: SmallVec::<[BeaconEntry; 2]> = SmallVec::new(); + let mut out = Vec::with_capacity(2); if curr_beacon.network().is_unchained() { for covered_epoch in (parent_epoch + 1)..=epoch { let round = curr_beacon.max_beacon_round_for_epoch(network_version, covered_epoch); @@ -123,9 +123,9 @@ impl BeaconSchedule { curr_beacon .entry(round) .await - .context(format!( - "failed to fetch beacon entry for epoch {covered_epoch}, round {round}" - ))?, + .with_context(|| { + format!("failed to fetch beacon entry for epoch {covered_epoch}, round {round}") + })?, ); } Ok(out.to_vec()) diff --git a/src/beacon/tests/drand.rs b/src/beacon/tests/drand.rs index 03d38638ecd..1b0fbdfb250 100644 --- a/src/beacon/tests/drand.rs +++ b/src/beacon/tests/drand.rs @@ -171,7 +171,7 @@ async fn beacon_entries_for_block_covers_null_rounds_quicknet() { .await .unwrap(); - let rounds: Vec = entries.iter().map(BeaconEntry::round).collect_vec(); + let rounds = entries.iter().map(BeaconEntry::round).collect_vec(); assert_eq!( rounds, expected_rounds, From bbac1921a66098b960c143935717e33fb4911885 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Mon, 3 Aug 2026 14:47:29 -0400 Subject: [PATCH 07/10] chore: addressing small nits --- src/beacon/drand.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/beacon/drand.rs b/src/beacon/drand.rs index 01fa241e736..e9cdbb1cc00 100644 --- a/src/beacon/drand.rs +++ b/src/beacon/drand.rs @@ -25,7 +25,6 @@ use backon::{ExponentialBuilder, Retryable}; use bls_signatures::Serialize as _; use nonzero_ext::nonzero; use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; -use smallvec::SmallVec; use tracing::debug; use url::Url; @@ -89,11 +88,12 @@ impl BeaconSchedule { if cb_epoch != pb_epoch { // Fork logic, take entries from the last two rounds of the new beacon. let round = curr_beacon.max_beacon_round_for_epoch(network_version, epoch); - - let mut out: SmallVec::<[BeaconEntry; 2]> = SmallVec::new(); - out.push(curr_beacon.entry(round - 1).await?); - out.push(curr_beacon.entry(round).await?); - return Ok(out.to_vec()); + + let out = vec![ + curr_beacon.entry(round - 1).await?, + curr_beacon.entry(round).await?, + ]; + return Ok(out); } } @@ -119,14 +119,7 @@ impl BeaconSchedule { if curr_beacon.network().is_unchained() { for covered_epoch in (parent_epoch + 1)..=epoch { let round = curr_beacon.max_beacon_round_for_epoch(network_version, covered_epoch); - out.push( - curr_beacon - .entry(round) - .await - .with_context(|| { - format!("failed to fetch beacon entry for epoch {covered_epoch}, round {round}") - })?, - ); + out.push(curr_beacon.entry(round).await?); } Ok(out.to_vec()) } else { From c60e828ddbf0fd2be079a177855981323506ea58 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Tue, 4 Aug 2026 08:14:12 -0400 Subject: [PATCH 08/10] chore: include changelog entry --- CHANGELOG.md | 2 + interop-tests/src/tests/go_app/ffi_gen.go | 371 +++++++++++----------- 2 files changed, 181 insertions(+), 192 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d671c06af10..827dd9c8cf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ ### Changed +- [#7412](https://github.com/ChainSafe/forest/issues/7412): Quicknet "unchained" logic fetch the `max_beacon_round` for all covered epochs + ### Removed ### Fixed diff --git a/interop-tests/src/tests/go_app/ffi_gen.go b/interop-tests/src/tests/go_app/ffi_gen.go index c9bce1458ec..58bba02ebdd 100644 --- a/interop-tests/src/tests/go_app/ffi_gen.go +++ b/interop-tests/src/tests/go_app/ffi_gen.go @@ -20,218 +20,205 @@ typedef struct StringRef { */ import "C" import ( - "runtime" - "unsafe" +"unsafe" +"runtime" - "github.com/ihciah/rust2go/asmcall" +"github.com/ihciah/rust2go/asmcall" ) - var GoKadNodeImpl GoKadNode - type GoKadNode interface { - run() - connect(multiaddr *string) - get_n_connected() uint +run() +connect(multiaddr *string) +get_n_connected() uint } - //export CGoKadNode_run func CGoKadNode_run() { - GoKadNodeImpl.run() + GoKadNodeImpl.run() } - //export CGoKadNode_connect -func CGoKadNode_connect(multiaddr C.StringRef) { - _new_multiaddr := newString(multiaddr) - GoKadNodeImpl.connect(&_new_multiaddr) +func CGoKadNode_connect(multiaddr C.StringRef, ) { +_new_multiaddr := newString(multiaddr) + GoKadNodeImpl.connect(&_new_multiaddr) } - //export CGoKadNode_get_n_connected func CGoKadNode_get_n_connected(slot *C.void, cb *C.void) { - resp := GoKadNodeImpl.get_n_connected() - resp_ref, buffer := cvt_ref(cntC_uintptr_t, refC_uintptr_t)(&resp) - asmcall.CallFuncG0P2(unsafe.Pointer(cb), unsafe.Pointer(&resp_ref), unsafe.Pointer(slot)) - runtime.KeepAlive(resp_ref) - runtime.KeepAlive(resp) - runtime.KeepAlive(buffer) +resp := GoKadNodeImpl.get_n_connected() +resp_ref, buffer := cvt_ref(cntC_uintptr_t, refC_uintptr_t)(&resp) +asmcall.CallFuncG0P2(unsafe.Pointer(cb), unsafe.Pointer(&resp_ref), unsafe.Pointer(slot)) +runtime.KeepAlive(resp_ref) +runtime.KeepAlive(resp) +runtime.KeepAlive(buffer) } - var GoBitswapNodeImpl GoBitswapNode - type GoBitswapNode interface { - run() - connect(multiaddr *string) - get_block(cid *string) bool +run() +connect(multiaddr *string) +get_block(cid *string) bool } - //export CGoBitswapNode_run func CGoBitswapNode_run() { - GoBitswapNodeImpl.run() + GoBitswapNodeImpl.run() } - //export CGoBitswapNode_connect -func CGoBitswapNode_connect(multiaddr C.StringRef) { - _new_multiaddr := newString(multiaddr) - GoBitswapNodeImpl.connect(&_new_multiaddr) +func CGoBitswapNode_connect(multiaddr C.StringRef, ) { +_new_multiaddr := newString(multiaddr) + GoBitswapNodeImpl.connect(&_new_multiaddr) } - //export CGoBitswapNode_get_block func CGoBitswapNode_get_block(cid C.StringRef, slot *C.void, cb *C.void) { - _new_cid := newString(cid) - resp := GoBitswapNodeImpl.get_block(&_new_cid) - resp_ref, buffer := cvt_ref(cntC_bool, refC_bool)(&resp) - asmcall.CallFuncG0P2(unsafe.Pointer(cb), unsafe.Pointer(&resp_ref), unsafe.Pointer(slot)) - runtime.KeepAlive(resp_ref) - runtime.KeepAlive(resp) - runtime.KeepAlive(buffer) -} - -func newString(s_ref C.StringRef) string { - return unsafe.String((*byte)(unsafe.Pointer(s_ref.ptr)), s_ref.len) -} -func refString(s *string, _ *[]byte) C.StringRef { - return C.StringRef{ - ptr: (*C.uint8_t)(unsafe.StringData(*s)), - len: C.uintptr_t(len(*s)), - } -} - -func ownString(s_ref C.StringRef) string { - return string(unsafe.Slice((*byte)(unsafe.Pointer(s_ref.ptr)), int(s_ref.len))) -} -func cntString(_ *string, _ *uint) [0]C.StringRef { return [0]C.StringRef{} } -func new_list_mapper[T1, T2 any](f func(T1) T2) func(C.ListRef) []T2 { - return func(x C.ListRef) []T2 { - input := unsafe.Slice((*T1)(unsafe.Pointer(x.ptr)), x.len) - output := make([]T2, len(input)) - for i, v := range input { - output[i] = f(v) - } - return output - } -} -func new_list_mapper_primitive[T1, T2 any](_ func(T1) T2) func(C.ListRef) []T2 { - return func(x C.ListRef) []T2 { - return unsafe.Slice((*T2)(unsafe.Pointer(x.ptr)), x.len) - } -} - -// only handle non-primitive type T -func cnt_list_mapper[T, R any](f func(s *T, cnt *uint) [0]R) func(s *[]T, cnt *uint) [0]C.ListRef { - return func(s *[]T, cnt *uint) [0]C.ListRef { - for _, v := range *s { - f(&v, cnt) - } - *cnt += uint(len(*s)) * size_of[R]() - return [0]C.ListRef{} - } -} - -// only handle primitive type T -func cnt_list_mapper_primitive[T, R any](_ func(s *T, cnt *uint) [0]R) func(s *[]T, cnt *uint) [0]C.ListRef { - return func(s *[]T, cnt *uint) [0]C.ListRef { return [0]C.ListRef{} } -} - -// only handle non-primitive type T -func ref_list_mapper[T, R any](f func(s *T, buffer *[]byte) R) func(s *[]T, buffer *[]byte) C.ListRef { - return func(s *[]T, buffer *[]byte) C.ListRef { - if len(*buffer) == 0 { - return C.ListRef{ - ptr: unsafe.Pointer(nil), - len: C.uintptr_t(len(*s)), - } - } - ret := C.ListRef{ - ptr: unsafe.Pointer(&(*buffer)[0]), - len: C.uintptr_t(len(*s)), - } - children_bytes := int(size_of[R]()) * len(*s) - children := (*buffer)[:children_bytes] - *buffer = (*buffer)[children_bytes:] - for _, v := range *s { - child := f(&v, buffer) - len := unsafe.Sizeof(child) - copy(children, unsafe.Slice((*byte)(unsafe.Pointer(&child)), len)) - children = children[len:] - } - return ret - } -} - -// only handle primitive type T -func ref_list_mapper_primitive[T, R any](_ func(s *T, buffer *[]byte) R) func(s *[]T, buffer *[]byte) C.ListRef { - return func(s *[]T, buffer *[]byte) C.ListRef { - if len(*s) == 0 { - return C.ListRef{ - ptr: unsafe.Pointer(nil), - len: C.uintptr_t(0), - } - } - return C.ListRef{ - ptr: unsafe.Pointer(&(*s)[0]), - len: C.uintptr_t(len(*s)), - } - } -} -func size_of[T any]() uint { - var t T - return uint(unsafe.Sizeof(t)) -} -func cvt_ref[R, CR any](cnt_f func(s *R, cnt *uint) [0]CR, ref_f func(p *R, buffer *[]byte) CR) func(p *R) (CR, []byte) { - return func(p *R) (CR, []byte) { - var cnt uint - cnt_f(p, &cnt) - buffer := make([]byte, cnt) - return ref_f(p, &buffer), buffer - } -} -func cvt_ref_cap[R, CR any](cnt_f func(s *R, cnt *uint) [0]CR, ref_f func(p *R, buffer *[]byte) CR, add_cap uint) func(p *R) (CR, []byte) { - return func(p *R) (CR, []byte) { - var cnt uint - cnt_f(p, &cnt) - buffer := make([]byte, cnt, cnt+add_cap) - return ref_f(p, &buffer), buffer - } -} - -func newC_uint8_t(n C.uint8_t) uint8 { return uint8(n) } -func newC_uint16_t(n C.uint16_t) uint16 { return uint16(n) } -func newC_uint32_t(n C.uint32_t) uint32 { return uint32(n) } -func newC_uint64_t(n C.uint64_t) uint64 { return uint64(n) } -func newC_int8_t(n C.int8_t) int8 { return int8(n) } -func newC_int16_t(n C.int16_t) int16 { return int16(n) } -func newC_int32_t(n C.int32_t) int32 { return int32(n) } -func newC_int64_t(n C.int64_t) int64 { return int64(n) } -func newC_bool(n C.bool) bool { return bool(n) } -func newC_uintptr_t(n C.uintptr_t) uint { return uint(n) } -func newC_intptr_t(n C.intptr_t) int { return int(n) } -func newC_float(n C.float) float32 { return float32(n) } -func newC_double(n C.double) float64 { return float64(n) } - -func cntC_uint8_t(_ *uint8, _ *uint) [0]C.uint8_t { return [0]C.uint8_t{} } -func cntC_uint16_t(_ *uint16, _ *uint) [0]C.uint16_t { return [0]C.uint16_t{} } -func cntC_uint32_t(_ *uint32, _ *uint) [0]C.uint32_t { return [0]C.uint32_t{} } -func cntC_uint64_t(_ *uint64, _ *uint) [0]C.uint64_t { return [0]C.uint64_t{} } -func cntC_int8_t(_ *int8, _ *uint) [0]C.int8_t { return [0]C.int8_t{} } -func cntC_int16_t(_ *int16, _ *uint) [0]C.int16_t { return [0]C.int16_t{} } -func cntC_int32_t(_ *int32, _ *uint) [0]C.int32_t { return [0]C.int32_t{} } -func cntC_int64_t(_ *int64, _ *uint) [0]C.int64_t { return [0]C.int64_t{} } -func cntC_bool(_ *bool, _ *uint) [0]C.bool { return [0]C.bool{} } -func cntC_uintptr_t(_ *uint, _ *uint) [0]C.uintptr_t { return [0]C.uintptr_t{} } -func cntC_intptr_t(_ *int, _ *uint) [0]C.intptr_t { return [0]C.intptr_t{} } -func cntC_float(_ *float32, _ *uint) [0]C.float { return [0]C.float{} } -func cntC_double(_ *float64, _ *uint) [0]C.double { return [0]C.double{} } - -func refC_uint8_t(p *uint8, _ *[]byte) C.uint8_t { return C.uint8_t(*p) } -func refC_uint16_t(p *uint16, _ *[]byte) C.uint16_t { return C.uint16_t(*p) } -func refC_uint32_t(p *uint32, _ *[]byte) C.uint32_t { return C.uint32_t(*p) } -func refC_uint64_t(p *uint64, _ *[]byte) C.uint64_t { return C.uint64_t(*p) } -func refC_int8_t(p *int8, _ *[]byte) C.int8_t { return C.int8_t(*p) } -func refC_int16_t(p *int16, _ *[]byte) C.int16_t { return C.int16_t(*p) } -func refC_int32_t(p *int32, _ *[]byte) C.int32_t { return C.int32_t(*p) } -func refC_int64_t(p *int64, _ *[]byte) C.int64_t { return C.int64_t(*p) } -func refC_bool(p *bool, _ *[]byte) C.bool { return C.bool(*p) } -func refC_uintptr_t(p *uint, _ *[]byte) C.uintptr_t { return C.uintptr_t(*p) } -func refC_intptr_t(p *int, _ *[]byte) C.intptr_t { return C.intptr_t(*p) } -func refC_float(p *float32, _ *[]byte) C.float { return C.float(*p) } -func refC_double(p *float64, _ *[]byte) C.double { return C.double(*p) } -func main() {} +_new_cid := newString(cid) +resp := GoBitswapNodeImpl.get_block(&_new_cid) +resp_ref, buffer := cvt_ref(cntC_bool, refC_bool)(&resp) +asmcall.CallFuncG0P2(unsafe.Pointer(cb), unsafe.Pointer(&resp_ref), unsafe.Pointer(slot)) +runtime.KeepAlive(resp_ref) +runtime.KeepAlive(resp) +runtime.KeepAlive(buffer) +} + + func newString(s_ref C.StringRef) string { + return unsafe.String((*byte)(unsafe.Pointer(s_ref.ptr)), s_ref.len) + } + func refString(s *string, _ *[]byte) C.StringRef { + return C.StringRef{ + ptr: (*C.uint8_t)(unsafe.StringData(*s)), + len: C.uintptr_t(len(*s)), + } + } + + func ownString(s_ref C.StringRef) string { + return string(unsafe.Slice((*byte)(unsafe.Pointer(s_ref.ptr)), int(s_ref.len))) + } + func cntString(_ *string, _ *uint) [0]C.StringRef { return [0]C.StringRef{} } + func new_list_mapper[T1, T2 any](f func(T1) T2) func(C.ListRef) []T2 { + return func(x C.ListRef) []T2 { + input := unsafe.Slice((*T1)(unsafe.Pointer(x.ptr)), x.len) + output := make([]T2, len(input)) + for i, v := range input { + output[i] = f(v) + } + return output + } + } + func new_list_mapper_primitive[T1, T2 any](_ func(T1) T2) func(C.ListRef) []T2 { + return func(x C.ListRef) []T2 { + return unsafe.Slice((*T2)(unsafe.Pointer(x.ptr)), x.len) + } + } + // only handle non-primitive type T + func cnt_list_mapper[T, R any](f func(s *T, cnt *uint)[0]R) func(s *[]T, cnt *uint) [0]C.ListRef { + return func(s *[]T, cnt *uint) [0]C.ListRef { + for _, v := range *s { + f(&v, cnt) + } + *cnt += uint(len(*s)) * size_of[R]() + return [0]C.ListRef{} + } + } + + // only handle primitive type T + func cnt_list_mapper_primitive[T, R any](_ func(s *T, cnt *uint)[0]R) func(s *[]T, cnt *uint) [0]C.ListRef { + return func(s *[]T, cnt *uint) [0]C.ListRef {return [0]C.ListRef{}} + } + // only handle non-primitive type T + func ref_list_mapper[T, R any](f func(s *T, buffer *[]byte) R) func(s *[]T, buffer *[]byte) C.ListRef { + return func(s *[]T, buffer *[]byte) C.ListRef { + if len(*buffer) == 0 { + return C.ListRef{ + ptr: unsafe.Pointer(nil), + len: C.uintptr_t(len(*s)), + } + } + ret := C.ListRef{ + ptr: unsafe.Pointer(&(*buffer)[0]), + len: C.uintptr_t(len(*s)), + } + children_bytes := int(size_of[R]()) * len(*s) + children := (*buffer)[:children_bytes] + *buffer = (*buffer)[children_bytes:] + for _, v := range *s { + child := f(&v, buffer) + len := unsafe.Sizeof(child) + copy(children, unsafe.Slice((*byte)(unsafe.Pointer(&child)), len)) + children = children[len:] + } + return ret + } + } + // only handle primitive type T + func ref_list_mapper_primitive[T, R any](_ func(s *T, buffer *[]byte) R) func(s *[]T, buffer *[]byte) C.ListRef { + return func(s *[]T, buffer *[]byte) C.ListRef { + if len(*s) == 0 { + return C.ListRef{ + ptr: unsafe.Pointer(nil), + len: C.uintptr_t(0), + } + } + return C.ListRef{ + ptr: unsafe.Pointer(&(*s)[0]), + len: C.uintptr_t(len(*s)), + } + } + } + func size_of[T any]() uint { + var t T + return uint(unsafe.Sizeof(t)) + } + func cvt_ref[R, CR any](cnt_f func(s *R, cnt *uint) [0]CR, ref_f func(p *R, buffer *[]byte) CR) func(p *R) (CR, []byte) { + return func(p *R) (CR, []byte) { + var cnt uint + cnt_f(p, &cnt) + buffer := make([]byte, cnt) + return ref_f(p, &buffer), buffer + } + } + func cvt_ref_cap[R, CR any](cnt_f func(s *R, cnt *uint) [0]CR, ref_f func(p *R, buffer *[]byte) CR, add_cap uint) func(p *R) (CR, []byte) { + return func(p *R) (CR, []byte) { + var cnt uint + cnt_f(p, &cnt) + buffer := make([]byte, cnt, cnt + add_cap) + return ref_f(p, &buffer), buffer + } + } + + func newC_uint8_t(n C.uint8_t) uint8 { return uint8(n) } + func newC_uint16_t(n C.uint16_t) uint16 { return uint16(n) } + func newC_uint32_t(n C.uint32_t) uint32 { return uint32(n) } + func newC_uint64_t(n C.uint64_t) uint64 { return uint64(n) } + func newC_int8_t(n C.int8_t) int8 { return int8(n) } + func newC_int16_t(n C.int16_t) int16 { return int16(n) } + func newC_int32_t(n C.int32_t) int32 { return int32(n) } + func newC_int64_t(n C.int64_t) int64 { return int64(n) } + func newC_bool(n C.bool) bool { return bool(n) } + func newC_uintptr_t(n C.uintptr_t) uint { return uint(n) } + func newC_intptr_t(n C.intptr_t) int { return int(n) } + func newC_float(n C.float) float32 { return float32(n) } + func newC_double(n C.double) float64 { return float64(n) } + + func cntC_uint8_t(_ *uint8, _ *uint) [0]C.uint8_t { return [0]C.uint8_t{} } + func cntC_uint16_t(_ *uint16, _ *uint) [0]C.uint16_t { return [0]C.uint16_t{} } + func cntC_uint32_t(_ *uint32, _ *uint) [0]C.uint32_t { return [0]C.uint32_t{} } + func cntC_uint64_t(_ *uint64, _ *uint) [0]C.uint64_t { return [0]C.uint64_t{} } + func cntC_int8_t(_ *int8, _ *uint) [0]C.int8_t { return [0]C.int8_t{} } + func cntC_int16_t(_ *int16, _ *uint) [0]C.int16_t { return [0]C.int16_t{} } + func cntC_int32_t(_ *int32, _ *uint) [0]C.int32_t { return [0]C.int32_t{} } + func cntC_int64_t(_ *int64, _ *uint) [0]C.int64_t { return [0]C.int64_t{} } + func cntC_bool(_ *bool, _ *uint) [0]C.bool { return [0]C.bool{} } + func cntC_uintptr_t(_ *uint, _ *uint) [0]C.uintptr_t { return [0]C.uintptr_t{} } + func cntC_intptr_t(_ *int, _ *uint) [0]C.intptr_t { return [0]C.intptr_t{} } + func cntC_float(_ *float32, _ *uint) [0]C.float { return [0]C.float{} } + func cntC_double(_ *float64, _ *uint) [0]C.double { return [0]C.double{} } + + func refC_uint8_t(p *uint8, _ *[]byte) C.uint8_t { return C.uint8_t(*p) } + func refC_uint16_t(p *uint16, _ *[]byte) C.uint16_t { return C.uint16_t(*p) } + func refC_uint32_t(p *uint32, _ *[]byte) C.uint32_t { return C.uint32_t(*p) } + func refC_uint64_t(p *uint64, _ *[]byte) C.uint64_t { return C.uint64_t(*p) } + func refC_int8_t(p *int8, _ *[]byte) C.int8_t { return C.int8_t(*p) } + func refC_int16_t(p *int16, _ *[]byte) C.int16_t { return C.int16_t(*p) } + func refC_int32_t(p *int32, _ *[]byte) C.int32_t { return C.int32_t(*p) } + func refC_int64_t(p *int64, _ *[]byte) C.int64_t { return C.int64_t(*p) } + func refC_bool(p *bool, _ *[]byte) C.bool { return C.bool(*p) } + func refC_uintptr_t(p *uint, _ *[]byte) C.uintptr_t { return C.uintptr_t(*p) } + func refC_intptr_t(p *int, _ *[]byte) C.intptr_t { return C.intptr_t(*p) } + func refC_float(p *float32, _ *[]byte) C.float { return C.float(*p) } + func refC_double(p *float64, _ *[]byte) C.double { return C.double(*p) } + func main() {} From 0493d1b9806bf8b3bfd1b0851cbdeb422553bd83 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Tue, 4 Aug 2026 08:15:55 -0400 Subject: [PATCH 09/10] chore: include changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f41141b4dc1..8548bb88d74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ ### Changed +- [#7412](https://github.com/ChainSafe/forest/issues/7412): Changes quicknet "unchained" logic to fetch the `max_beacon_round` for all covered epochs + ### Removed ### Fixed From 18d36a1a89ddacc311aab2e8a7b703e47c1744e1 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Tue, 4 Aug 2026 09:09:21 -0400 Subject: [PATCH 10/10] chore: update CHANGELOG.md, remove uneeded `.to_vec()` --- CHANGELOG.md | 4 ++-- src/beacon/drand.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8548bb88d74..34840f558cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,12 +37,12 @@ ### Changed -- [#7412](https://github.com/ChainSafe/forest/issues/7412): Changes quicknet "unchained" logic to fetch the `max_beacon_round` for all covered epochs - ### Removed ### Fixed +- [#7412](https://github.com/ChainSafe/forest/issues/7412): Fixes quicknet "unchained" logic to fetch the `max_beacon_round` for all covered epochs + ## Forest v0.35.0 "Shravan" Non-mandatory release for all node operators. It includes some fixes and improvements, notably around state-related RPC. Note that this release contains breaking changes, so please read the changelog carefully before upgrading. diff --git a/src/beacon/drand.rs b/src/beacon/drand.rs index e9cdbb1cc00..61ebddbca41 100644 --- a/src/beacon/drand.rs +++ b/src/beacon/drand.rs @@ -121,7 +121,7 @@ impl BeaconSchedule { let round = curr_beacon.max_beacon_round_for_epoch(network_version, covered_epoch); out.push(curr_beacon.entry(round).await?); } - Ok(out.to_vec()) + Ok(out) } else { let mut cur = max_round; while cur > prev_round { @@ -131,7 +131,7 @@ impl BeaconSchedule { out.push(entry); } out.reverse(); - Ok(out.to_vec()) + Ok(out) } }