Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(f3): manifest retrieval from smart contract #5321

Merged
merged 24 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f27db8d
feat(f3): manifest retrieval from smart contract
hanabi1224 Feb 21, 2025
e6587af
refactor
hanabi1224 Feb 24, 2025
c27d3e0
Merge remote-tracking branch 'origin/main' into hm/f3-contract-manife…
hanabi1224 Feb 24, 2025
92155c6
ContractManifestProvider
hanabi1224 Feb 24, 2025
4bcf84d
Merge remote-tracking branch 'origin/main' into hm/f3-contract-manife…
hanabi1224 Feb 24, 2025
3425568
fix
hanabi1224 Feb 24, 2025
1de2414
Merge branch 'main' into hm/f3-contract-manifest-integration
hanabi1224 Feb 24, 2025
711b988
Merge remote-tracking branch 'origin/main' into hm/f3-contract-manife…
hanabi1224 Feb 25, 2025
d8c055c
Merge branch 'main' into hm/f3-contract-manifest-integration
hanabi1224 Feb 25, 2025
b81c4b3
Update src/rpc/methods/f3.rs
hanabi1224 Feb 25, 2025
edb93e4
FOREST_F3_CONTRACT_ADDRESS env var
hanabi1224 Feb 25, 2025
b4b9b45
fix spellcheck errors
hanabi1224 Feb 26, 2025
cc8e25e
link to solidity contract
hanabi1224 Feb 26, 2025
627b492
Merge remote-tracking branch 'origin/main' into hm/f3-contract-manife…
hanabi1224 Feb 26, 2025
7de05ce
link to activationInformation method ID
hanabi1224 Feb 26, 2025
76edefb
Update src/rpc/methods/f3/types.rs
hanabi1224 Feb 26, 2025
6789916
no indexing_slicing
hanabi1224 Feb 26, 2025
c94730c
go mod tidy
hanabi1224 Feb 26, 2025
8ee02b8
Merge remote-tracking branch 'origin/main' into hm/f3-contract-manife…
hanabi1224 Feb 27, 2025
83ca6db
enable F3 on mainnet
hanabi1224 Feb 27, 2025
c083f09
Merge branch 'main' into hm/f3-contract-manifest-integration
hanabi1224 Feb 27, 2025
cd97d98
fix
hanabi1224 Feb 27, 2025
cc981f1
Merge branch 'main' into hm/f3-contract-manifest-integration
hanabi1224 Mar 3, 2025
220b177
Merge branch 'main' into hm/f3-contract-manifest-integration
hanabi1224 Mar 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/users/reference/env_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ process.
| `FOREST_F3_SIDECAR_RPC_ENDPOINT` | string | 127.0.0.1:23456 | `127.0.0.1:23456` | An RPC endpoint of F3 sidecar. |
| `FOREST_F3_SIDECAR_FFI_ENABLED` | 1 or true | hard-coded per chain | 1 | Whether or not to start the F3 sidecar via FFI |
| `FOREST_F3_CONSENSUS_ENABLED` | 1 or true | hard-coded per chain | 1 | Whether or not to apply the F3 consensus to the node |
| `FOREST_F3_MANIFEST_SERVER` | string | empty | `12D3KooWENMwUF9YxvQxar7uBWJtZkA6amvK4xWmKXfSiHUo2Qq7` | Set dynamic F3 manifest server |
| `FOREST_F3_FINALITY` | integer | inherited from chain configuration | 900 | Set the chain finality epochs in F3 manifest |
| `FOREST_F3_PERMANENT_PARTICIPATING_MINER_ADDRESSES` | comma delimited strings | empty | `t0100,t0101` | Set the miner addresses that participate in F3 permanently |
| `FOREST_F3_INITIAL_POWER_TABLE` | string | empty | `bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i` | Set the F3 initial power table CID |
| `FOREST_F3_ROOT` | string | [FOREST_DATA_ROOT]/f3 | `/var/tmp/f3` | Set the data directory for F3 |
| `FOREST_F3_BOOTSTRAP_EPOCH` | integer | -1 | 100 | Set the bootstrap epoch for F3 |
| `FOREST_F3_MANIFEST_POLL_INTERVAL` | string | empty | `15m` | Set the contract manifest poll interval for F3 |
| `FOREST_DRAND_MAINNET_CONFIG` | string | empty | refer to Drand config format section | Override `DRAND_MAINNET` config |
| `FOREST_DRAND_QUICKNET_CONFIG` | string | empty | refer to Drand config format section | Override `DRAND_QUICKNET` config |

Expand Down
3 changes: 0 additions & 3 deletions f3-sidecar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,3 @@ environment variable `FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT=1` is set.

F3 sidecar is not started by default, set `FOREST_F3_SIDECAR_FFI_ENABLED=1` to
opt in.

Set dynamic manifest server via `FOREST_F3_MANIFEST_SERVER`, e.g.
`FOREST_F3_MANIFEST_SERVER=12D3KooWENMwUF9YxvQxar7uBWJtZkA6amvK4xWmKXfSiHUo2Qq7`
1 change: 1 addition & 0 deletions f3-sidecar/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type F3Api struct {
GetParticipatingMinerIDs func(context.Context) ([]uint64, error)
SignMessage func(context.Context, []byte, []byte) (*crypto.Signature, error)
Finalize func(context.Context, gpbft.TipSetKey) error
GetManifestFromContract func(context.Context) (*manifest.Manifest, error)
}

type FilecoinApi struct {
Expand Down
8 changes: 4 additions & 4 deletions f3-sidecar/ffi_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ import (
var GoF3NodeImpl GoF3Node

type GoF3Node interface {
run(rpc_endpoint *string, jwt *string, f3_rpc_endpoint *string, initial_power_table *string, bootstrap_epoch *int64, finality *int64, f3_root *string, manifest_server *string) bool
run(rpc_endpoint *string, jwt *string, f3_rpc_endpoint *string, initial_power_table *string, bootstrap_epoch *int64, finality *int64, f3_root *string, contract_manifest_poll_interval_seconds *uint64) bool
}

//export CGoF3Node_run
func CGoF3Node_run(rpc_endpoint C.StringRef, jwt C.StringRef, f3_rpc_endpoint C.StringRef, initial_power_table C.StringRef, bootstrap_epoch C.int64_t, finality C.int64_t, f3_root C.StringRef, manifest_server C.StringRef, slot *C.void, cb *C.void) {
func CGoF3Node_run(rpc_endpoint C.StringRef, jwt C.StringRef, f3_rpc_endpoint C.StringRef, initial_power_table C.StringRef, bootstrap_epoch C.int64_t, finality C.int64_t, f3_root C.StringRef, contract_manifest_poll_interval_seconds C.uint64_t, slot *C.void, cb *C.void) {
_new_rpc_endpoint := newString(rpc_endpoint)
_new_jwt := newString(jwt)
_new_f3_rpc_endpoint := newString(f3_rpc_endpoint)
_new_initial_power_table := newString(initial_power_table)
_new_bootstrap_epoch := newC_int64_t(bootstrap_epoch)
_new_finality := newC_int64_t(finality)
_new_f3_root := newString(f3_root)
_new_manifest_server := newString(manifest_server)
resp := GoF3NodeImpl.run(&_new_rpc_endpoint, &_new_jwt, &_new_f3_rpc_endpoint, &_new_initial_power_table, &_new_bootstrap_epoch, &_new_finality, &_new_f3_root, &_new_manifest_server)
_new_contract_manifest_poll_interval_seconds := newC_uint64_t(contract_manifest_poll_interval_seconds)
resp := GoF3NodeImpl.run(&_new_rpc_endpoint, &_new_jwt, &_new_f3_rpc_endpoint, &_new_initial_power_table, &_new_bootstrap_epoch, &_new_finality, &_new_f3_root, &_new_contract_manifest_poll_interval_seconds)
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)
Expand Down
4 changes: 2 additions & 2 deletions f3-sidecar/ffi_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ type f3Impl struct {
ctx context.Context
}

func (f3 *f3Impl) run(rpc_endpoint *string, jwt *string, f3_rpc_endpoint *string, initial_power_table *string, bootstrap_epoch *int64, finality *int64, db *string, manifest_server *string) bool {
func (f3 *f3Impl) run(rpc_endpoint *string, jwt *string, f3_rpc_endpoint *string, initial_power_table *string, bootstrap_epoch *int64, finality *int64, db *string, contract_manifest_poll_interval_seconds *uint64) bool {
var err error = nil
const MAX_RETRY int = 5
nRetry := 0
for nRetry <= MAX_RETRY {
err = run(f3.ctx, *rpc_endpoint, *jwt, *f3_rpc_endpoint, *initial_power_table, *bootstrap_epoch, *finality, *db, *manifest_server)
err = run(f3.ctx, *rpc_endpoint, *jwt, *f3_rpc_endpoint, *initial_power_table, *bootstrap_epoch, *finality, *db, *contract_manifest_poll_interval_seconds)
if err != nil {
nRetry += 1
logger.Errorf("Unexpected F3 failure, retrying(%d) in 10s... error=%s", nRetry, err)
Expand Down
7 changes: 4 additions & 3 deletions f3-sidecar/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ func main() {
flag.Int64Var(&finality, "finality", 900, "chain finality epochs")
var root string
flag.StringVar(&root, "root", "f3-data", "path to the f3 data directory")
var manifestServer string
flag.StringVar(&manifestServer, "manifest-server", "", "the peer id of the dynamic manifest server")
var contract_poll_interval uint64
flag.Uint64Var(&contract_poll_interval, "contract-poll-interval", 900, "contract manifest poll interval seconds")

flag.Parse()

ctx := context.Background()

err := run(ctx, rpcEndpoint, jwt, f3RpcEndpoint, initialPowerTable, bootstrapEpoch, finality, root, manifestServer)
err := run(ctx, rpcEndpoint, jwt, f3RpcEndpoint, initialPowerTable, bootstrapEpoch, finality, root, contract_poll_interval)
if err != nil {
panic(err)
}
Expand Down
78 changes: 78 additions & 0 deletions f3-sidecar/manifest.go
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what are the best practices in Go, but would it make sense to check all pointer parameters in methods that they are not nil? There was recently a nil derefeence in go-f3.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In our case, the parameters are passed from Rust code which are not nil, the signature types that are generated by rust2go are pointers to avoid lifetime issues

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be helpful to comment on this somewhere, given it's not obvious (at least for me).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package main

import (
"context"
"time"

"github.com/filecoin-project/go-f3/manifest"
)

type ContractManifestProvider struct {
started *bool
pollInterval time.Duration
initialManifest *manifest.Manifest
currentManifest *manifest.Manifest
f3Api *F3Api
ch chan *manifest.Manifest
}

func NewContractManifestProvider(initialValue *manifest.Manifest, contract_manifest_poll_interval_seconds uint64, f3Api *F3Api) (*ContractManifestProvider, error) {
if err := initialValue.Validate(); err != nil {
return nil, err
}
started := false
pollInterval := time.Duration(contract_manifest_poll_interval_seconds) * time.Second
p := ContractManifestProvider{
started: &started,
pollInterval: pollInterval,
initialManifest: initialValue,
currentManifest: nil,
f3Api: f3Api,
ch: make(chan *manifest.Manifest),
}
return &p, nil
}

func (p *ContractManifestProvider) Update(m *manifest.Manifest) {
p.currentManifest = m
p.ch <- m
}

func (p *ContractManifestProvider) Start(ctx context.Context) error {
if *p.started {
logger.Warnf("ContractManifestProvider has already been started\n")
return nil
}

started := true
p.started = &started
go func() {
for started && ctx.Err() == nil {
if p.currentManifest == nil {
p.Update(p.initialManifest)
}
logger.Debugf("Polling manifest from contract...\n")
m, err := p.f3Api.GetManifestFromContract(ctx)
if err == nil {
if m != nil {
if !m.Equal(p.currentManifest) {
logger.Infof("Successfully polled manifest from contract, updating...\n")
p.Update(m)
} else {
logger.Infof("Successfully polled unchanged manifest from contract\n")
}
}
} else {
logger.Warnf("failed to get manifest from contract: %s\n", err)
}
time.Sleep(p.pollInterval)
}
}()

return nil
}
func (p *ContractManifestProvider) Stop(context.Context) error {
*p.started = false
return nil
}
func (p *ContractManifestProvider) ManifestUpdates() <-chan *manifest.Manifest { return p.ch }
56 changes: 17 additions & 39 deletions f3-sidecar/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,27 @@ import (
"github.com/filecoin-project/go-f3/manifest"
"github.com/filecoin-project/go-jsonrpc"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
leveldb "github.com/ipfs/go-ds-leveldb"
"github.com/libp2p/go-libp2p/core/peer"
)

func run(ctx context.Context, rpcEndpoint string, jwt string, f3RpcEndpoint string, initialPowerTable string, bootstrapEpoch int64, finality int64, f3Root string, manifestServer string) error {
func run(ctx context.Context, rpcEndpoint string, jwt string, f3RpcEndpoint string, initialPowerTable string, bootstrapEpoch int64, finality int64, f3Root string, contract_manifest_poll_interval_seconds uint64) error {
api := FilecoinApi{}
isJwtProvided := len(jwt) > 0
closer, err := jsonrpc.NewClient(context.Background(), rpcEndpoint, "Filecoin", &api, nil)
if err != nil {
return err
}
defer closer()
network, err := api.StateNetworkName(ctx)
if err != nil {
return err
var network string
for {
network, err = api.StateNetworkName(ctx)
if err == nil {
logger.Infoln("Forest RPC server is online")
break
} else {
logger.Warnln("waiting for Forest RPC server")
time.Sleep(5 * time.Second)
}
}
listenAddrs, err := api.NetAddrsListen(ctx)
if err != nil {
Expand Down Expand Up @@ -95,39 +99,13 @@ func run(ctx context.Context, rpcEndpoint string, jwt string, f3RpcEndpoint stri
}
m.CommitteeLookback = manifest.DefaultCommitteeLookback

var manifestProvider manifest.ManifestProvider
switch manifestServerID, err := peer.Decode(manifestServer); {
case err != nil:
logger.Info("Using static manifest provider")
if manifestProvider, err = manifest.NewStaticManifestProvider(m); err != nil {
return err
}
default:
logger.Infof("Using dynamic manifest provider at %s", manifestServerID)
manifestDS := namespace.Wrap(ds, datastore.NewKey("/f3-dynamic-manifest"))
primaryNetworkName := m.NetworkName
filter := func(m *manifest.Manifest) error {
if m.EC.Finalize {
return fmt.Errorf("refusing dynamic manifest that finalizes tipsets")
}
if m.NetworkName == primaryNetworkName {
return fmt.Errorf(
"refusing dynamic manifest with network name %q that clashes with initial manifest",
primaryNetworkName,
)
}
return nil
}
if manifestProvider, err = manifest.NewDynamicManifestProvider(
p2p.PubSub,
manifestServerID,
manifest.DynamicManifestProviderWithInitialManifest(m),
manifest.DynamicManifestProviderWithDatastore(manifestDS),
manifest.DynamicManifestProviderWithFilter(filter)); err != nil {
return err
}
manifestProvider, err := NewContractManifestProvider(m, contract_manifest_poll_interval_seconds, &ec.f3api)
if err != nil {
return err
}
if err := manifestProvider.Start(ctx); err != nil {
return err
}

f3Module, err := f3.New(ctx, manifestProvider, ds,
p2p.Host, p2p.PubSub, verif, &ec, f3Root)
if err != nil {
Expand Down
2 changes: 0 additions & 2 deletions src/daemon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,6 @@ fn maybe_start_f3_service(
chain_finality,
bootstrap_epoch,
initial_power_table,
manifest_server,
} = crate::f3::get_f3_sidecar_params(&chain_config);
move || {
crate::f3::run_f3_sidecar_if_enabled(
Expand All @@ -616,7 +615,6 @@ fn maybe_start_f3_service(
chain_finality,
std::env::var("FOREST_F3_ROOT")
.unwrap_or(default_f3_root.display().to_string()),
manifest_server.map(|i| i.to_string()).unwrap_or_default(),
);
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion src/f3/go_ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ pub trait GoF3Node {
bootstrap_epoch: i64,
finality: i64,
f3_root: String,
manifest_server: String,
contract_manifest_poll_interval_seconds: u64,
) -> bool;
}
53 changes: 1 addition & 52 deletions src/f3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ mod go_ffi;
use go_ffi::*;

use cid::Cid;
use libp2p::PeerId;

use crate::{networks::ChainConfig, utils::misc::env::is_env_set_and_truthy};

Expand All @@ -18,7 +17,6 @@ pub struct F3Options {
pub chain_finality: i64,
pub bootstrap_epoch: i64,
pub initial_power_table: Cid,
pub manifest_server: Option<PeerId>,
}

pub fn get_f3_sidecar_params(chain_config: &ChainConfig) -> F3Options {
Expand Down Expand Up @@ -55,34 +53,11 @@ pub fn get_f3_sidecar_params(chain_config: &ChainConfig) -> F3Options {
tracing::info!("Using F3 bootstrap epoch {i} set by FOREST_F3_BOOTSTRAP_EPOCH")
})
.unwrap_or(chain_config.f3_bootstrap_epoch);
let manifest_server = match std::env::var("FOREST_F3_MANIFEST_SERVER") {
Ok(v) => {
if v.is_empty() {
None
} else {
match v.parse() {
Ok(i) => Some(i),
_ => {
tracing::warn!(
"Invalid libp2p peer id {v} set by FOREST_F3_MANIFEST_SERVER"
);
None
}
}
.inspect(|i| {
tracing::info!("Using F3 manifest server {i} set by FOREST_F3_MANIFEST_SERVER")
})
.or(chain_config.f3_manifest_server)
}
}
_ => chain_config.f3_manifest_server,
};

F3Options {
chain_finality,
bootstrap_epoch,
initial_power_table,
manifest_server,
}
}

Expand All @@ -95,7 +70,6 @@ pub fn run_f3_sidecar_if_enabled(
_bootstrap_epoch: i64,
_finality: i64,
_f3_root: String,
_manifest_server: String,
) {
if is_sidecar_ffi_enabled(chain_config) {
#[cfg(all(f3sidecar, not(feature = "no-f3-sidecar")))]
Expand All @@ -109,7 +83,7 @@ pub fn run_f3_sidecar_if_enabled(
_bootstrap_epoch,
_finality,
_f3_root,
_manifest_server,
chain_config.f3_contract_poll_interval().as_secs(),
);
}
}
Expand Down Expand Up @@ -147,7 +121,6 @@ mod tests {
chain_finality: chain_config.policy.chain_finality,
bootstrap_epoch: chain_config.f3_bootstrap_epoch,
initial_power_table: chain_config.f3_initial_power_table,
manifest_server: chain_config.f3_manifest_server,
}
);

Expand All @@ -158,29 +131,6 @@ mod tests {
"bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i",
);
std::env::set_var("FOREST_F3_BOOTSTRAP_EPOCH", "100");
// mainnet server
std::env::set_var(
"FOREST_F3_MANIFEST_SERVER",
"12D3KooWENMwUF9YxvQxar7uBWJtZkA6amvK4xWmKXfSiHUo2Qq7",
);
assert_eq!(
get_f3_sidecar_params(&chain_config),
F3Options {
chain_finality: 100,
bootstrap_epoch: 100,
initial_power_table: "bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i"
.parse()
.unwrap(),
manifest_server: Some(
"12D3KooWENMwUF9YxvQxar7uBWJtZkA6amvK4xWmKXfSiHUo2Qq7"
.parse()
.unwrap()
),
}
);

// Unset FOREST_F3_MANIFEST_SERVER
std::env::set_var("FOREST_F3_MANIFEST_SERVER", "");
assert_eq!(
get_f3_sidecar_params(&chain_config),
F3Options {
Expand All @@ -189,7 +139,6 @@ mod tests {
initial_power_table: "bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i"
.parse()
.unwrap(),
manifest_server: None,
}
);
}
Expand Down
Loading
Loading