Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions integration/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
github.com/dgraph-io/badger/v4 v4.9.2 // indirect
github.com/dgraph-io/ristretto/v2 v2.4.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.6.0 // indirect
Expand All @@ -85,6 +86,7 @@ require (
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20260604005048-7023385849c0 // indirect
Expand Down Expand Up @@ -118,6 +120,7 @@ require (
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kilic/bls12-381 v0.1.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/knadh/koanf/maps v0.1.2 // indirect
github.com/knadh/koanf/parsers/yaml v1.1.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions integration/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,8 @@ github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/dgraph-io/badger/v4 v4.9.2 h1:Wb5qw8gElqwV1a8msHTeQKova9b1V10heFKMIiPd80E=
github.com/dgraph-io/badger/v4 v4.9.2/go.mod h1:nJjaJTUOSsQEBhsq209FmwCvMJzEA3e74RjZw6V2pQI=
github.com/dgraph-io/ristretto/v2 v2.4.0 h1:I/w09yLjhdcVD2QV192UJcq8dPBaAJb9pOuMyNy0XlU=
github.com/dgraph-io/ristretto/v2 v2.4.0/go.mod h1:0KsrXtXvnv0EqnzyowllbVJB8yBonswa2lTCK2gGo9E=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
Expand Down Expand Up @@ -871,6 +873,8 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
Expand Down
52 changes: 52 additions & 0 deletions integration/token/common/sdk/evmdlog/sdk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

// Package evmdlog wires the EVM network driver together with the zkatdlog (nogh) token driver over
// the plain FSC view SDK (no Fabric platform). It is used by the EVM integration tests.
package evmdlog

import (
"errors"

dlog "github.com/LFDT-Panurus/panurus/token/core/zkatdlog/nogh/v1/driver"
"github.com/LFDT-Panurus/panurus/token/sdk"
tokensdk "github.com/LFDT-Panurus/panurus/token/sdk/dig"
"github.com/LFDT-Panurus/panurus/x/token/services/network/evm"
"github.com/hyperledger-labs/fabric-smart-client/integration/nwo/fsc/support/libp2p"
common "github.com/hyperledger-labs/fabric-smart-client/platform/common/sdk/dig"
viewsdk "github.com/hyperledger-labs/fabric-smart-client/platform/view/sdk/dig"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services"
"go.uber.org/dig"
)

// SDK bundles the EVM network driver and the dlog token driver.
type SDK struct {
common.SDK
}

// NewSDK returns a new EVM+dlog SDK rooted at the given registry.
func NewSDK(registry services.Registry) *SDK {
return NewFrom(tokensdk.NewFrom(viewsdk.NewSDK(registry)))
}

// NewFrom composes the EVM+dlog SDK on top of an existing SDK.
func NewFrom(sdk common.SDK) *SDK {
return &SDK{SDK: libp2p.NewFrom(sdk)}
}

// Install registers the token driver and the EVM network driver in the dig container.
func (p *SDK) Install() error {
if err := errors.Join(
sdk.RegisterTokenDriverDependencies(p.Container()),
p.Container().Provide(dlog.NewTokenDriver, dig.Group("token-drivers")),
p.Container().Provide(dlog.NewValidatorDriver, dig.Group("validator-drivers")),
p.Container().Provide(evm.NewDriver, dig.Group("network-drivers")),
); err != nil {
return err
}

return p.SDK.Install()
}
26 changes: 26 additions & 0 deletions integration/token/common/sdk/evmdlog/sdk_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package evmdlog

import (
"testing"

tokensdk "github.com/LFDT-Panurus/panurus/token/sdk/dig"
dig2 "github.com/hyperledger-labs/fabric-smart-client/platform/common/sdk/dig"
viewsdk "github.com/hyperledger-labs/fabric-smart-client/platform/view/sdk/dig"
"github.com/stretchr/testify/require"
)

// TestEVMWiring dry-runs the dig container to prove the EVM network driver and the dlog token driver
// are wired correctly over the plain view SDK, without a Fabric platform. It validates registration
// (the "prove it plugs in" gate for Phase 1.2) without starting any node.
func TestEVMWiring(t *testing.T) {
require.NoError(t, viewsdk.DryRunWiring(
func(sdk dig2.SDK) *SDK { return NewFrom(tokensdk.NewFrom(sdk)) },
viewsdk.WithBool("token.enabled", true),
))
}
87 changes: 87 additions & 0 deletions x/token/services/network/evm/client/evmclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package client

import (
"context"
"math/big"
)

//go:generate counterfeiter -o mock/evmclient.go -fake-name EVMClient . EVMClient

// Receipt is the subset of an Ethereum transaction receipt the driver needs.
type Receipt struct {
TxHash Hash
BlockNumber *uint64 // nil until the transaction is mined
Status uint64 // 1 = success, 0 = reverted
Logs []Log
}

// Log is an EVM event log entry.
type Log struct {
Address Address
Topics []Hash
Data []byte
TxHash Hash
BlockNumber uint64
}

// LogFilter selects logs by contract address, block range and indexed topics.
// Topics follows the eth_getLogs convention: position i lists the acceptable values for topic i;
// an empty inner slice matches any value at that position.
type LogFilter struct {
Address Address
FromBlock uint64
ToBlock uint64
Topics [][]Hash
}

// GasFees carries the EIP-1559 fee parameters suggested by the node.
type GasFees struct {
MaxFeePerGas *big.Int
MaxPriorityFeePerGas *big.Int
}

// CallMsg describes a read-only call or a gas estimation request.
type CallMsg struct {
From *Address
To *Address
Data []byte
Value *big.Int
}

// EVMClient abstracts the JSON-RPC surface the network driver depends on. It is intentionally
// minimal and backend-agnostic so the driver works against any EVM node, including fabric-x-evm.
// State-reading calls take an explicit block tag (for example "finalized") so callers control the
// consistency/finality of the data they read.
type EVMClient interface {
// ChainID returns the chain ID reported by the node.
ChainID(ctx context.Context) (*big.Int, error)
// Ping checks that the node is reachable.
Ping(ctx context.Context) error

// Call performs a read-only contract call at the given block tag and returns the raw result.
Call(ctx context.Context, to Address, data []byte, blockTag string) ([]byte, error)
// GetLogs returns the logs matching the filter.
GetLogs(ctx context.Context, q LogFilter) ([]Log, error)

// PendingNonceAt returns the next nonce for account including pending transactions.
PendingNonceAt(ctx context.Context, account Address) (uint64, error)
// EstimateGas estimates the gas needed to execute msg.
EstimateGas(ctx context.Context, msg CallMsg) (uint64, error)
// SuggestGasFees returns the node's suggested EIP-1559 fees.
SuggestGasFees(ctx context.Context) (GasFees, error)

// SendRawTransaction submits a signed, RLP-encoded transaction and returns its hash.
SendRawTransaction(ctx context.Context, rawTx []byte) (Hash, error)
// GetTransactionReceipt returns the receipt for txHash, or (nil, nil) if not yet mined.
GetTransactionReceipt(ctx context.Context, txHash Hash) (*Receipt, error)
// IsPending reports whether txHash is still pending in the mempool. found is false when the node
// does not know the transaction at all (dropped or never seen), which the finality resolver uses
// to distinguish "pending" from "dropped".
IsPending(ctx context.Context, txHash Hash) (pending bool, found bool, err error)
}
68 changes: 68 additions & 0 deletions x/token/services/network/evm/client/reachability_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package client_test

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os/exec"
"testing"
"time"

"github.com/stretchr/testify/require"
)

// TestAnvilReachability is the Phase 1.2 reachability spike. It starts a local anvil node and
// confirms the JSON-RPC surface responds to eth_chainId. It skips when anvil is not on PATH, so it
// is safe in CI; run it locally with anvil (Foundry) installed to exercise the smoke path. The same
// raw JSON-RPC probe works against a fabric-x-evm gateway by pointing endpoint at it.
func TestAnvilReachability(t *testing.T) {
anvilBin, err := exec.LookPath("anvil")
if err != nil {
t.Skip("anvil not on PATH; skipping reachability spike (install Foundry to run it)")
}

const port = 8547
cmd := exec.Command(anvilBin, "--port", fmt.Sprintf("%d", port), "--silent")
require.NoError(t, cmd.Start())
t.Cleanup(func() { _ = cmd.Process.Kill() })

endpoint := fmt.Sprintf("http://127.0.0.1:%d", port)
chainID := pollChainID(t, endpoint, 5*time.Second)
require.NotEmpty(t, chainID, "expected a chain id from eth_chainId")
t.Logf("anvil reachable at %s, chainId=%s", endpoint, chainID)
}

// pollChainID calls eth_chainId until it succeeds or the timeout elapses, returning the hex chain id.
func pollChainID(t *testing.T, endpoint string, timeout time.Duration) string {
t.Helper()
deadline := time.Now().Add(timeout)
body := []byte(`{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}`)
for time.Now().Before(deadline) {
resp, err := http.Post(endpoint, "application/json", bytes.NewReader(body))
if err != nil {
time.Sleep(50 * time.Millisecond)

continue
}
raw, _ := io.ReadAll(resp.Body)
_ = resp.Body.Close()

var out struct {
Result string `json:"result"`
}
if json.Unmarshal(raw, &out) == nil && out.Result != "" {
return out.Result
}
time.Sleep(50 * time.Millisecond)
}

return ""
}
Loading