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

SUAVE dns #196

Merged
merged 24 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
24 changes: 22 additions & 2 deletions cmd/geth/forgecmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ var (
Name: "whitelist",
Usage: `The whitelist external endpoints to call`,
}
dnsRegistryForgeFlag = &cli.StringSliceFlag{
Name: "dns-registry",
Usage: `The DNS registry to resolve aliases to endpoints`,
}
ethBackendForgeFlag = &cli.StringFlag{
Name: "eth-backend",
Usage: `The endpoint of the confidential eth backend`,
Expand All @@ -47,8 +51,9 @@ var (
)

type suaveForgeConfig struct {
Whitelist []string `toml:"whitelist"`
EthBackend string `toml:"eth_backend"`
Whitelist []string `toml:"whitelist"`
DnsRegistry map[string]string `toml:"dns_registry"`
EthBackend string `toml:"eth_backend"`
}

func readContext(ctx *cli.Context) (*vm.SuaveContext, error) {
Expand Down Expand Up @@ -87,6 +92,19 @@ func readContext(ctx *cli.Context) (*vm.SuaveContext, error) {
if ctx.IsSet(whiteListForgeFlag.Name) {
cfg.Whitelist = ctx.StringSlice(whiteListForgeFlag.Name)
}
if ctx.IsSet(dnsRegistryForgeFlag.Name) {
dnsRegistry := make(map[string]string)
for _, endpoint := range ctx.StringSlice(dnsRegistryForgeFlag.Name) {
parts := strings.Split(endpoint, "=")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid value for remote backend endpoint: %s", endpoint)
}
name := parts[0]
domain := parts[1]
dnsRegistry[name] = domain
}
cfg.DnsRegistry = dnsRegistry
}

// create the suave context
var suaveEthBackend suave.ConfidentialEthBackend
Expand All @@ -109,6 +127,7 @@ func readContext(ctx *cli.Context) (*vm.SuaveContext, error) {
backend := &vm.SuaveExecutionBackend{
ExternalWhitelist: cfg.Whitelist,
ConfidentialEthBackend: suaveEthBackend,
DnsRegistry: cfg.DnsRegistry,
EthBlockSigningKey: blsKey,
EthBundleSigningKey: ecdsaKey,
}
Expand All @@ -127,6 +146,7 @@ var (
Flags: []cli.Flag{
isLocalForgeFlag,
whiteListForgeFlag,
dnsRegistryForgeFlag,
ethBackendForgeFlag,
tomlConfigForgeFlag,
},
Expand Down
3 changes: 3 additions & 0 deletions cmd/geth/forgecmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,18 @@ func TestForgeReadConfig(t *testing.T) {
sCtx, err := readContext(ctx)
require.NoError(t, err)
require.Equal(t, sCtx.Backend.ExternalWhitelist, []string{"a", "b"})
require.Equal(t, sCtx.Backend.DnsRegistry, map[string]string{"a": "b", "c": "d"})
require.Equal(t, sCtx.Backend.ConfidentialEthBackend.(*suave_backends.RemoteEthBackend).Endpoint(), "suave")

// override the config if the flags are set
ctx.Set("eth-backend", "http://localhost:8545")
ctx.Set("whitelist", "c,d")
ctx.Set("dns-registry", "e=f,g=h")

sCtx, err = readContext(ctx)
require.NoError(t, err)
require.Equal(t, sCtx.Backend.ExternalWhitelist, []string{"c", "d"})
require.Equal(t, sCtx.Backend.DnsRegistry, map[string]string{"e": "f", "g": "h"})
require.Equal(t, sCtx.Backend.ConfidentialEthBackend.(*suave_backends.RemoteEthBackend).Endpoint(), "http://localhost:8545")

// set flags to null and use default values
Expand Down
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ var (

suaveFlags = []cli.Flag{
utils.SuaveEthRemoteBackendEndpointFlag,
utils.SuaveDnsFlag,
utils.SuaveConfidentialTransportRedisEndpointFlag,
utils.SuaveConfidentialStoreRedisEndpointFlag,
utils.SuaveConfidentialStorePebbleDbPathFlag,
Expand Down
1 change: 1 addition & 0 deletions cmd/geth/testdata/forge.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[profile.suave]
whitelist = ["a", "b"]
dns_registry = { a = "b", c = "d" }
eth_backend = "suave"
[profile.ci.fuzz]
runs = 10_000
Expand Down
20 changes: 20 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,12 @@ var (
Category: flags.SuaveCategory,
}

SuaveDnsFlag = &cli.StringSliceFlag{
Name: "suave.dns",
Usage: "Suave domain name resolver settings. (format: alias=url)",
Category: flags.SuaveCategory,
}

SuaveConfidentialTransportRedisEndpointFlag = &cli.StringFlag{
Name: "suave.confidential.redis-transport-endpoint",
Usage: "Redis endpoint to use as confidential store transport (default: no transport)",
Expand Down Expand Up @@ -1724,6 +1730,20 @@ func SetSuaveConfig(ctx *cli.Context, stack *node.Node, cfg *suave.Config) {
cfg.SuaveEthRemoteBackendEndpoint = ctx.String(SuaveEthRemoteBackendEndpointFlag.Name)
}

if ctx.IsSet(SuaveDnsFlag.Name) {
dnsRegistry := make(map[string]string)
for _, endpoint := range ctx.StringSlice(SuaveDnsFlag.Name) {
parts := strings.Split(endpoint, "=")
if len(parts) != 2 {
Fatalf("invalid value for remote backend endpoint: %s", endpoint)
}
name := parts[0]
domain := parts[1]
dnsRegistry[name] = domain
}
cfg.DnsRegistry = dnsRegistry
}

if ctx.IsSet(SuaveConfidentialTransportRedisEndpointFlag.Name) {
cfg.RedisStorePubsubUri = ctx.String(SuaveConfidentialTransportRedisEndpointFlag.Name)
}
Expand Down
32 changes: 19 additions & 13 deletions core/vm/contracts_suave.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,21 +213,27 @@ func (s *suaveRuntime) doHTTPRequest(request types.HttpRequest) ([]byte, error)
body = bytes.NewReader(request.Body)
}

// decode the url and check if the domain is allowed
parsedURL, err := url.Parse(request.Url)
if err != nil {
panic(err)
}

var allowed bool
for _, allowedDomain := range s.suaveContext.Backend.ExternalWhitelist {
if allowedDomain == "*" || allowedDomain == parsedURL.Hostname() {
allowed = true
break
// resolve dns if possible
if endpoint, ok := s.suaveContext.Backend.DnsRegistry[request.Url]; ok {
request.Url = endpoint
} else {
// decode the url and check if the domain is allowed
parsedURL, err := url.Parse(request.Url)
if err != nil {
panic(err)
}

// check if the domain is allowed
for _, allowedDomain := range s.suaveContext.Backend.ExternalWhitelist {
if allowedDomain == "*" || allowedDomain == parsedURL.Hostname() {
allowed = true
break
}
}
if !allowed {
return nil, fmt.Errorf("domain %s is not allowed", parsedURL.Hostname())
}
}
if !allowed {
return nil, fmt.Errorf("domain %s is not allowed", parsedURL.Hostname())
}

req, err := http.NewRequest(request.Method, request.Url, body)
Expand Down
2 changes: 2 additions & 0 deletions core/vm/contracts_suave_eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,9 @@ func (b *suaveRuntime) buildEthBlock(blockArgs types.BuildBlockArgs, dataID type
}

log.Info("requesting a block be built", "mergedBundles", mergedBundles)

envelope, err := b.suaveContext.Backend.ConfidentialEthBackend.BuildEthBlockFromBundles(context.TODO(), &blockArgs, mergedBundles)

if err != nil {
return nil, nil, fmt.Errorf("could not build eth block: %w", err)
}
Expand Down
18 changes: 15 additions & 3 deletions core/vm/contracts_suave_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,17 +192,19 @@ func basicHandler(w http.ResponseWriter, r *http.Request) {
}

func TestSuave_HttpRequest_Basic(t *testing.T) {
srv := httptest.NewServer(&httpTestHandler{
fn: basicHandler,
})

s := &suaveRuntime{
suaveContext: &SuaveContext{
Backend: &SuaveExecutionBackend{
ExternalWhitelist: []string{"127.0.0.1"},
DnsRegistry: map[string]string{"goerli": srv.URL},
},
},
}

srv := httptest.NewServer(&httpTestHandler{
fn: basicHandler,
})
defer srv.Close()

cases := []struct {
Expand Down Expand Up @@ -260,6 +262,16 @@ func TestSuave_HttpRequest_Basic(t *testing.T) {
req: types.HttpRequest{Url: srv.URL, Method: "POST", Headers: []string{"fail:1"}},
err: true,
},
{
// DNS resolution success
req: types.HttpRequest{Url: "goerli", Method: "GET"},
resp: []byte("ok1"),
},
{
// DNS resolution failure
req: types.HttpRequest{Url: "sepolia", Method: "GET"},
err: true,
},
}

for _, c := range cases {
Expand Down
1 change: 1 addition & 0 deletions core/vm/suave.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type SuaveExecutionBackend struct {
EthBundleSigningKey *ecdsa.PrivateKey
EthBlockSigningKey *bls.SecretKey
ExternalWhitelist []string
DnsRegistry map[string]string
ConfidentialStore ConfidentialStore
ConfidentialEthBackend suave.ConfidentialEthBackend
}
Expand Down
2 changes: 2 additions & 0 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type EthAPIBackend struct {
suaveEngine *cstore.CStoreEngine
suaveEthBackend suave.ConfidentialEthBackend
suaveExternalWhitelist []string
suaveDnsRegistry map[string]string
}

// For testing purposes
Expand Down Expand Up @@ -445,6 +446,7 @@ func (b *EthAPIBackend) SuaveContext(requestTx *types.Transaction, ccr *types.Co
EthBundleSigningKey: b.suaveEthBundleSigningKey,
EthBlockSigningKey: b.suaveEthBlockSigningKey,
ExternalWhitelist: b.suaveExternalWhitelist,
DnsRegistry: b.suaveDnsRegistry,
ConfidentialStore: storeTransaction,
ConfidentialEthBackend: b.suaveEthBackend,
},
Expand Down
3 changes: 2 additions & 1 deletion eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {

confidentialStoreEngine := cstore.NewEngine(confidentialStoreBackend, confidentialStoreTransport, suaveDaSigner, types.LatestSigner(chainConfig))

eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil, suaveEthBundleSigningKey, suaveEthBlockSigningKey, confidentialStoreEngine, suaveEthBackend, config.Suave.ExternalWhitelist}
eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil,
suaveEthBundleSigningKey, suaveEthBlockSigningKey, confidentialStoreEngine, suaveEthBackend, config.Suave.ExternalWhitelist, config.Suave.DnsRegistry}
if eth.APIBackend.allowUnprotectedTxs {
log.Info("Unprotected transactions allowed")
}
Expand Down
3 changes: 2 additions & 1 deletion suave/core/config.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package suave

type Config struct {
SuaveEthRemoteBackendEndpoint string
SuaveEthRemoteBackendEndpoint string // deprecated
RedisStorePubsubUri string
RedisStoreUri string
PebbleDbPath string
EthBundleSigningKeyHex string
EthBlockSigningKeyHex string
ExternalWhitelist []string
DnsRegistry map[string]string
}

var DefaultConfig = Config{}
39 changes: 39 additions & 0 deletions suave/e2e/workflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,39 @@ func TestE2ERemoteCalls(t *testing.T) {
})
}

func TestE2ERemoteCallsWithDns(t *testing.T) {
httpSrv := httptest.NewServer(&dummyRelayer{fn: func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte{0x1, 0x2, 0x3})
}})
defer httpSrv.Close()

fr := newFramework(t, WithDnsRegistry(map[string]string{"goerli": httpSrv.URL}))
defer fr.Close()

clt := fr.NewSDKClient()

contractAddr := common.Address{0x3}
contract := sdk.GetContract(contractAddr, exampleCallSourceContract.Abi, clt)

t.Run("DNS registered", func(t *testing.T) {
req := &types.HttpRequest{
Method: "GET",
Url: "goerli",
}
_, err := contract.SendTransaction("remoteCall", []interface{}{req}, nil)
require.NoError(t, err)
})

t.Run("DNS not registered", func(t *testing.T) {
req := &types.HttpRequest{
Method: "GET",
Url: "sepolia",
}
_, err := contract.SendTransaction("remoteCall", []interface{}{req}, nil)
require.Error(t, err)
})
}

func TestE2EPrecompile_Builder(t *testing.T) {
fr := newFramework(t, WithKettleAddress())
defer fr.Close()
Expand Down Expand Up @@ -1406,6 +1439,12 @@ func WithWhitelist(whitelist []string) frameworkOpt {
}
}

func WithDnsRegistry(registry map[string]string) frameworkOpt {
return func(c *frameworkConfig) {
c.suaveConfig.DnsRegistry = registry
}
}

func newFramework(t *testing.T, opts ...frameworkOpt) *framework {
cfg := defaultFrameworkConfig
for _, opt := range opts {
Expand Down
Loading