This repository was archived by the owner on Oct 20, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 134
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Support alternative mempools (#336)
- Loading branch information
Showing
16 changed files
with
564 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package testutils | ||
|
||
import "github.com/ethereum/go-ethereum/common/hexutil" | ||
|
||
func AltMempoolMock() map[string]any { | ||
return map[string]any{ | ||
"description": "Mock Alt Mempool", | ||
"chainIds": []any{hexutil.EncodeBig(ChainID)}, | ||
"allowlist": []any{ | ||
map[string]any{ | ||
"description": "Mock forbiddenOpcode rule", | ||
"rule": "forbiddenOpcode", | ||
"entity": "account", | ||
"contract": "0x0000000000000000000000000000000000000000", | ||
"opcode": "GAS", | ||
}, | ||
map[string]any{ | ||
"description": "Mock forbiddenPrecompile rule", | ||
"rule": "forbiddenPrecompile", | ||
"entity": "account", | ||
"contract": "0x0000000000000000000000000000000000000000", | ||
"precompile": "0x0000000000000000000000000000000000000000", | ||
}, | ||
map[string]any{ | ||
"description": "Mock invalidStorageAccess rule", | ||
"rule": "invalidStorageAccess", | ||
"entity": "account", | ||
"contract": "0x0000000000000000000000000000000000000000", | ||
"slot": "0x0000000000000000000000000000000000000000", | ||
}, | ||
map[string]any{ | ||
"description": "Mock notStaked rule", | ||
"rule": "notStaked", | ||
"entity": "0x0000000000000000000000000000000000000000", | ||
}, | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package altmempools | ||
|
||
import ( | ||
"encoding/json" | ||
"math/big" | ||
"net/http" | ||
|
||
"github.com/ethereum/go-ethereum/common/hexutil" | ||
"github.com/puzpuzpuz/xsync/v3" | ||
) | ||
|
||
// Directory maintains a collection of alternative mempool configurations. It allows a consumer to check if a | ||
// known alternative mempool exists that will allow specific exceptions that the canonical mempool cannot | ||
// accept. | ||
type Directory struct { | ||
invalidStorageAccess *xsync.MapOf[string, []string] | ||
} | ||
|
||
type Config struct { | ||
Id string | ||
Data map[string]any | ||
} | ||
|
||
func invalidStorageAccessID(entity string, contract string, slot string) string { | ||
return entity + contract + slot | ||
} | ||
|
||
func fetchMempoolConfig(url string) (map[string]any, error) { | ||
resp, err := http.Get(url) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer resp.Body.Close() | ||
|
||
var data map[string]any | ||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { | ||
return nil, err | ||
} | ||
return data, nil | ||
} | ||
|
||
// New accepts an array of alternative mempool configs and returns a Directory. | ||
func New(chain *big.Int, altMempools []*Config) (*Directory, error) { | ||
dir := &Directory{ | ||
invalidStorageAccess: xsync.NewMapOf[string, []string](), | ||
} | ||
for _, alt := range altMempools { | ||
if err := Schema.Validate(alt.Data); err != nil { | ||
return nil, err | ||
} | ||
|
||
skip := true | ||
for _, item := range alt.Data["chainIds"].([]any) { | ||
allowed, err := hexutil.DecodeBig(item.(string)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if chain.Cmp(allowed) == 0 { | ||
skip = false | ||
} | ||
} | ||
if skip { | ||
continue | ||
} | ||
|
||
for _, item := range alt.Data["allowlist"].([]any) { | ||
config := item.(map[string]any) | ||
switch config["rule"].(string) { | ||
case "invalidStorageAccess": | ||
{ | ||
isaId := invalidStorageAccessID( | ||
config["entity"].(string), | ||
config["contract"].(string), | ||
config["slot"].(string), | ||
) | ||
curr, _ := dir.invalidStorageAccess.Load(isaId) | ||
dir.invalidStorageAccess.Store(isaId, append(curr, alt.Id)) | ||
} | ||
} | ||
} | ||
} | ||
|
||
return dir, nil | ||
} | ||
|
||
// NewFromIPFS will pull alternative mempool configs from IPFS and returns a Directory. The mempool id is | ||
// equal to an IPFS CID. | ||
func NewFromIPFS(chain *big.Int, ipfsGateway string, ids []string) (*Directory, error) { | ||
var alts []*Config | ||
for _, id := range ids { | ||
data, err := fetchMempoolConfig(ipfsGateway + "/" + id) | ||
if err != nil { | ||
return nil, err | ||
} | ||
alts = append(alts, &Config{id, data}) | ||
} | ||
|
||
return New(chain, alts) | ||
} | ||
|
||
// HasInvalidStorageAccessException will attempt to find all mempools ids that will accept the given invalid | ||
// storage access pattern and return it. If none is found, an empty array will be returned. | ||
func (d *Directory) HasInvalidStorageAccessException(entity string, contract string, slot string) []string { | ||
ids, _ := d.invalidStorageAccess.Load(invalidStorageAccessID(entity, contract, slot)) | ||
return ids | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package altmempools_test | ||
|
||
import ( | ||
"math/big" | ||
"testing" | ||
|
||
"github.com/stackup-wallet/stackup-bundler/internal/testutils" | ||
"github.com/stackup-wallet/stackup-bundler/pkg/altmempools" | ||
) | ||
|
||
func TestDirectoryHasSingleInvalidStorageAccessException(t *testing.T) { | ||
id := "1" | ||
alts := []*altmempools.Config{ | ||
{Id: id, Data: testutils.AltMempoolMock()}, | ||
} | ||
dir, err := altmempools.New(testutils.ChainID, alts) | ||
if err != nil { | ||
t.Fatal("error initializing directory") | ||
} | ||
|
||
mempools := dir.HasInvalidStorageAccessException( | ||
"account", | ||
"0x0000000000000000000000000000000000000000", | ||
"0x0000000000000000000000000000000000000000", | ||
) | ||
if len(mempools) != 1 || mempools[0] != id { | ||
t.Fatalf("got %v, want [1]", mempools) | ||
} | ||
} | ||
|
||
func TestDirectoryHasManyInvalidStorageAccessExceptions(t *testing.T) { | ||
id1 := "1" | ||
id2 := "2" | ||
alts := []*altmempools.Config{ | ||
{Id: id1, Data: testutils.AltMempoolMock()}, | ||
{Id: id2, Data: testutils.AltMempoolMock()}, | ||
} | ||
dir, err := altmempools.New(testutils.ChainID, alts) | ||
if err != nil { | ||
t.Fatal("error initializing directory") | ||
} | ||
|
||
mempools := dir.HasInvalidStorageAccessException( | ||
"account", | ||
"0x0000000000000000000000000000000000000000", | ||
"0x0000000000000000000000000000000000000000", | ||
) | ||
if len(mempools) != 2 || mempools[0] != id1 && mempools[1] != id2 { | ||
t.Fatalf("got %v, want [1 2]", mempools) | ||
} | ||
} | ||
|
||
func TestDirectoryHasNoInvalidStorageAccessExceptions(t *testing.T) { | ||
id := "1" | ||
alts := []*altmempools.Config{ | ||
{Id: id, Data: testutils.AltMempoolMock()}, | ||
} | ||
dir, err := altmempools.New(testutils.ChainID, alts) | ||
if err != nil { | ||
t.Fatal("error initializing directory") | ||
} | ||
|
||
mempools := dir.HasInvalidStorageAccessException( | ||
"paymaster", | ||
"0x0000000000000000000000000000000000000000", | ||
"0x0000000000000000000000000000000000000000", | ||
) | ||
if len(mempools) != 0 { | ||
t.Fatalf("got %v, want []", mempools) | ||
} | ||
} | ||
|
||
func TestDirectoryIncompatibleChain(t *testing.T) { | ||
alts := []*altmempools.Config{ | ||
{Id: "1", Data: testutils.AltMempoolMock()}, | ||
} | ||
dir, err := altmempools.New(big.NewInt(2), alts) | ||
if err != nil { | ||
t.Fatal("error initializing directory") | ||
} | ||
|
||
mempools := dir.HasInvalidStorageAccessException( | ||
"account", | ||
"0x0000000000000000000000000000000000000000", | ||
"0x0000000000000000000000000000000000000000", | ||
) | ||
if len(mempools) != 0 { | ||
t.Fatalf("got %v, want []", mempools) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// Package altmempool provides functions to pull an alternative mempool config from an IPFS gateway and | ||
// validate it against a schema. | ||
// | ||
// Schema originally written by @dancoombs: https://hackmd.io/@dancoombs/BJYRz3h8n. | ||
package altmempools |
Oops, something went wrong.