From 538f903ae8471c3a2141fd4c30c308317dc01690 Mon Sep 17 00:00:00 2001 From: Arkadiy Kukarkin Date: Wed, 25 Mar 2026 14:42:30 +0100 Subject: [PATCH] resolve devnet addresses from FWSS at runtime, strip from generator --- constants/resolve.go | 96 ++++++++++++++++++++++++++++++++++ internal/generate/addresses.go | 41 +-------------- synapse.go | 14 +++++ 3 files changed, 111 insertions(+), 40 deletions(-) create mode 100644 constants/resolve.go diff --git a/constants/resolve.go b/constants/resolve.go new file mode 100644 index 0000000..2e2e74a --- /dev/null +++ b/constants/resolve.go @@ -0,0 +1,96 @@ +package constants + +import ( + "context" + "fmt" + "strings" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" +) + +const fwssABI = `[ + {"type":"function","name":"paymentsContractAddress","inputs":[],"outputs":[{"name":"","type":"address"}],"stateMutability":"view"}, + {"type":"function","name":"viewContractAddress","inputs":[],"outputs":[{"name":"","type":"address"}],"stateMutability":"view"}, + {"type":"function","name":"pdpVerifierAddress","inputs":[],"outputs":[{"name":"","type":"address"}],"stateMutability":"view"}, + {"type":"function","name":"serviceProviderRegistry","inputs":[],"outputs":[{"name":"","type":"address"}],"stateMutability":"view"}, + {"type":"function","name":"sessionKeyRegistry","inputs":[],"outputs":[{"name":"","type":"address"}],"stateMutability":"view"} +]` + +// NetworkAddresses holds the full set of addresses derived from an FWSS contract. +type NetworkAddresses struct { + FWSS common.Address + Payments common.Address + StateView common.Address + PDPVerifier common.Address + SPRegistry common.Address + SessionKeyRegistry common.Address +} + +// ResolveFromFWSS queries a FWSS contract to derive all dependent addresses. +// This is the runtime equivalent of what `go generate` does at build time +// for mainnet and calibration. +func ResolveFromFWSS(ctx context.Context, client *ethclient.Client, fwssAddr common.Address) (*NetworkAddresses, error) { + parsed, err := abi.JSON(strings.NewReader(fwssABI)) + if err != nil { + return nil, fmt.Errorf("parse FWSS abi: %w", err) + } + + callView := func(method string) (common.Address, error) { + data, err := parsed.Pack(method) + if err != nil { + return common.Address{}, fmt.Errorf("pack %s: %w", method, err) + } + result, err := client.CallContract(ctx, ethereum.CallMsg{ + To: &fwssAddr, + Data: data, + }, nil) + if err != nil { + return common.Address{}, fmt.Errorf("call %s: %w", method, err) + } + var addr common.Address + if err := parsed.UnpackIntoInterface(&addr, method, result); err != nil { + return common.Address{}, fmt.Errorf("unpack %s: %w", method, err) + } + return addr, nil + } + + addrs := &NetworkAddresses{FWSS: fwssAddr} + + addrs.Payments, err = callView("paymentsContractAddress") + if err != nil { + return nil, err + } + addrs.StateView, err = callView("viewContractAddress") + if err != nil { + return nil, err + } + addrs.PDPVerifier, err = callView("pdpVerifierAddress") + if err != nil { + return nil, err + } + addrs.SPRegistry, err = callView("serviceProviderRegistry") + if err != nil { + return nil, err + } + addrs.SessionKeyRegistry, err = callView("sessionKeyRegistry") + if err != nil { + return nil, err + } + + return addrs, nil +} + +// RegisterNetwork populates the package-level address maps for a network. +// Use this for devnet or custom deployments where addresses are resolved +// at runtime from FWSS rather than baked in at build time. +func RegisterNetwork(network Network, addrs *NetworkAddresses) { + WarmStorageAddresses[network] = addrs.FWSS + PaymentsAddresses[network] = addrs.Payments + WarmStorageStateViewAddresses[network] = addrs.StateView + PDPVerifierAddresses[network] = addrs.PDPVerifier + SPRegistryAddresses[network] = addrs.SPRegistry + SessionKeyRegistryAddresses[network] = addrs.SessionKeyRegistry +} diff --git a/internal/generate/addresses.go b/internal/generate/addresses.go index 9738a14..04280ee 100644 --- a/internal/generate/addresses.go +++ b/internal/generate/addresses.go @@ -109,11 +109,10 @@ func readAddresses(ctx context.Context, rpcURL string, fwssAddr common.Address) type templateData struct { Mainnet *networkAddresses Calibration *networkAddresses - Devnet *networkAddresses } var tmpl = template.Must(template.New("addresses").Parse(`// Code generated by go generate; DO NOT EDIT. -// Source: FWSS contracts on mainnet, calibration{{ if .Devnet }}, and devnet{{ end }} +// Source: FWSS contracts on mainnet, calibration package constants @@ -123,18 +122,12 @@ import "github.com/ethereum/go-ethereum/common" var ( FWSSAddressMainnet = common.HexToAddress("{{ .Mainnet.FWSS.Hex }}") FWSSAddressCalibration = common.HexToAddress("{{ .Calibration.FWSS.Hex }}") -{{- if .Devnet }} - FWSSAddressDevnet = common.HexToAddress("{{ .Devnet.FWSS.Hex }}") -{{- end }} ) // WarmStorageAddresses aliases the FWSS addresses (root of trust) var WarmStorageAddresses = map[Network]common.Address{ NetworkMainnet: FWSSAddressMainnet, NetworkCalibration: FWSSAddressCalibration, -{{- if .Devnet }} - NetworkDevnet: FWSSAddressDevnet, -{{- end }} } // derived addresses - read from FWSS contracts @@ -142,41 +135,26 @@ var ( PaymentsAddresses = map[Network]common.Address{ NetworkMainnet: common.HexToAddress("{{ .Mainnet.Payments.Hex }}"), NetworkCalibration: common.HexToAddress("{{ .Calibration.Payments.Hex }}"), -{{- if .Devnet }} - NetworkDevnet: common.HexToAddress("{{ .Devnet.Payments.Hex }}"), -{{- end }} } WarmStorageStateViewAddresses = map[Network]common.Address{ NetworkMainnet: common.HexToAddress("{{ .Mainnet.StateView.Hex }}"), NetworkCalibration: common.HexToAddress("{{ .Calibration.StateView.Hex }}"), -{{- if .Devnet }} - NetworkDevnet: common.HexToAddress("{{ .Devnet.StateView.Hex }}"), -{{- end }} } PDPVerifierAddresses = map[Network]common.Address{ NetworkMainnet: common.HexToAddress("{{ .Mainnet.PDPVerifier.Hex }}"), NetworkCalibration: common.HexToAddress("{{ .Calibration.PDPVerifier.Hex }}"), -{{- if .Devnet }} - NetworkDevnet: common.HexToAddress("{{ .Devnet.PDPVerifier.Hex }}"), -{{- end }} } SPRegistryAddresses = map[Network]common.Address{ NetworkMainnet: common.HexToAddress("{{ .Mainnet.SPRegistry.Hex }}"), NetworkCalibration: common.HexToAddress("{{ .Calibration.SPRegistry.Hex }}"), -{{- if .Devnet }} - NetworkDevnet: common.HexToAddress("{{ .Devnet.SPRegistry.Hex }}"), -{{- end }} } SessionKeyRegistryAddresses = map[Network]common.Address{ NetworkMainnet: common.HexToAddress("{{ .Mainnet.SessionKeyRegistry.Hex }}"), NetworkCalibration: common.HexToAddress("{{ .Calibration.SessionKeyRegistry.Hex }}"), -{{- if .Devnet }} - NetworkDevnet: common.HexToAddress("{{ .Devnet.SessionKeyRegistry.Hex }}"), -{{- end }} } ) `)) @@ -216,23 +194,6 @@ func main() { Calibration: calibration, } - // optionally read devnet addresses from a running foc-devnet instance. - // set DEVNET_RPC and DEVNET_FWSS to enable, e.g.: - // DEVNET_RPC=http://127.0.0.1:1234/rpc/v1 DEVNET_FWSS=0x4A8a... go generate ./constants/ - devnetRPC := os.Getenv("DEVNET_RPC") - devnetFWSS := os.Getenv("DEVNET_FWSS") - if devnetRPC != "" && devnetFWSS != "" { - fwssAddr := common.HexToAddress(devnetFWSS) - fmt.Println("reading devnet addresses...") - devnet, err := readAddresses(ctx, devnetRPC, fwssAddr) - if err != nil { - fmt.Fprintf(os.Stderr, "devnet: %v\n", err) - os.Exit(1) - } - printAddresses("devnet", devnet) - data.Devnet = devnet - } - // go generate runs in the package's source directory // if GOPACKAGE is set, we're running via go generate - output to current dir // otherwise output to constants/ for manual invocation from repo root diff --git a/synapse.go b/synapse.go index 64cb7ea..a7f7727 100644 --- a/synapse.go +++ b/synapse.go @@ -68,6 +68,20 @@ func New(ctx context.Context, opts Options) (*Client, error) { warmStorageAddr = WarmStorageAddresses[network] } + // for networks without static addresses (e.g. devnet), resolve from FWSS at runtime + if _, ok := constants.PDPVerifierAddresses[constants.Network(network)]; !ok { + if warmStorageAddr == (common.Address{}) { + ethClient.Close() + return nil, fmt.Errorf("network %s has no built-in addresses; set WarmStorageAddress (FWSS) to resolve at runtime", network) + } + addrs, err := constants.ResolveFromFWSS(ctx, ethClient, warmStorageAddr) + if err != nil { + ethClient.Close() + return nil, fmt.Errorf("failed to resolve addresses from FWSS on %s: %w", network, err) + } + constants.RegisterNetwork(constants.Network(network), addrs) + } + address := crypto.PubkeyToAddress(opts.PrivateKey.PublicKey) client := &Client{