diff --git a/genesis.json b/genesis.json index 4cde15934a..6e5bd398da 100644 --- a/genesis.json +++ b/genesis.json @@ -20,8 +20,17 @@ "astriaSequencerInitialHeight": 2, "astriaCelestiaInitialHeight": 2, "astriaCelestiaHeightVariance": 10, - "astriaBridgeAddresses": [], - "astriaBridgeAllowedAssetDenom": "nria" + "astriaBridgeAddresses": [ + { + "bridgeAddress": "684ae50c49a434199199c9c698115391152d7b3f", + "startHeight": 1, + "assetDenom": "nria", + "assetPrecision": 9 + } + ], + "astriaFeeCollectors": { + "1": "0xaC21B97d35Bf75A7dAb16f35b111a50e78A72F30" + } }, "difficulty": "10000000", "gasLimit": "8000000", diff --git a/go.mod b/go.mod index 12fc55cd18..fd0c6352d9 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,9 @@ module github.com/ethereum/go-ethereum go 1.21 require ( - buf.build/gen/go/astria/execution-apis/grpc/go v1.3.0-20240314225003-19f6b40c3e7b.2 - buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.32.0-20240314225003-19f6b40c3e7b.1 + buf.build/gen/go/astria/execution-apis/grpc/go v1.3.0-20240423053323-ccf38db75f2f.2 + buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.33.0-20240423053323-ccf38db75f2f.1 + buf.build/gen/go/astria/primitives/protocolbuffers/go v1.33.0-20240422195039-812e347acd6b.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 github.com/Microsoft/go-winio v0.6.1 github.com/VictoriaMetrics/fastcache v1.12.1 @@ -32,7 +33,7 @@ require ( github.com/go-stack/stack v1.8.1 github.com/gofrs/flock v0.8.1 github.com/golang-jwt/jwt/v4 v4.5.0 - github.com/golang/protobuf v1.5.3 + github.com/golang/protobuf v1.5.4 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa github.com/google/uuid v1.3.0 @@ -73,13 +74,13 @@ require ( golang.org/x/time v0.3.0 golang.org/x/tools v0.13.0 google.golang.org/grpc v1.53.0 - google.golang.org/protobuf v1.32.0 + google.golang.org/protobuf v1.33.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - buf.build/gen/go/astria/astria/protocolbuffers/go v1.32.0-20240314225002-310b29d1ef86.1 // indirect + buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.33.0-20240423053322-44396ca8658a.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect github.com/DataDog/zstd v1.4.5 // indirect diff --git a/go.sum b/go.sum index 1af9b07197..3c6206b406 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,16 @@ -buf.build/gen/go/astria/astria/grpc/go v1.3.0-20240314225002-310b29d1ef86.2/go.mod h1:FFX7uXjELBVDDB3WOW6lO6Sn36kC9QqkHu5xU9xY4ac= -buf.build/gen/go/astria/astria/protocolbuffers/go v1.28.1-20240314225002-310b29d1ef86.4/go.mod h1:2267P73yUQPZlQyW96+03iM2xSEMVcK0DbRqSYbyLJQ= -buf.build/gen/go/astria/astria/protocolbuffers/go v1.32.0-20240314225002-310b29d1ef86.1 h1:tqubh9diC2bk6BzxO9Obs9KgxvsfTEa0hWKqG9kA45E= -buf.build/gen/go/astria/astria/protocolbuffers/go v1.32.0-20240314225002-310b29d1ef86.1/go.mod h1:Ma4TsfKTQby0r4gCkdb6D2ThBxTNu0mSVNZF1udF2ko= -buf.build/gen/go/astria/execution-apis/grpc/go v1.3.0-20240314225003-19f6b40c3e7b.2 h1:9vSSs8hVVcYrQMt7CTO5r1I4eUhPiXUEBrVBZ9SaCnc= -buf.build/gen/go/astria/execution-apis/grpc/go v1.3.0-20240314225003-19f6b40c3e7b.2/go.mod h1:wSSvcWt7/78D8RQKhBjTaTvl0j7paszROJHHnJCfZ40= -buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.28.1-20240314225003-19f6b40c3e7b.4/go.mod h1:Ow5heKoG4UcB64gsltdmZNOZQsL8khlJzysd5lLwOfI= -buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.32.0-20240314225003-19f6b40c3e7b.1 h1:7dX4IwODvMlzcC0j0wkuLPf4JBzT9EW2O2Vb6tCEHHs= -buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.32.0-20240314225003-19f6b40c3e7b.1/go.mod h1:1PoFKhLruAgpqRKBz6cY/Z5bfXXYj3pHTvtf2uBoRJs= +buf.build/gen/go/astria/execution-apis/grpc/go v1.3.0-20240423053323-ccf38db75f2f.2 h1:hZ3iCorHPftvC8mtOq59JgsXVrzQa7DGVWhhReLfDUU= +buf.build/gen/go/astria/execution-apis/grpc/go v1.3.0-20240423053323-ccf38db75f2f.2/go.mod h1:xHHYSn2PRQE8P9sUfLoH4sDV1dPZzx7knBWLpc4r1ws= +buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.28.1-20240423053323-ccf38db75f2f.4/go.mod h1:L45ZB/W7SxQDHLrKTdGT40ytkcnntNJcF/VyiAK3psE= +buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.33.0-20240423053323-ccf38db75f2f.1 h1:OB4HbEo2YRJc3L7TNSvou07id25Xg8WmuIHmx7FnnfA= +buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.33.0-20240423053323-ccf38db75f2f.1/go.mod h1:LbzJDzXgLEtOzJmPbh6SbBC0RyK1UxkfETur2p06VHU= +buf.build/gen/go/astria/primitives/grpc/go v1.3.0-20240422195039-812e347acd6b.2/go.mod h1:5NfjRl1Y2qnN62OOMBNhKDFJKsZAvrYyJJdcoS438Aw= +buf.build/gen/go/astria/primitives/protocolbuffers/go v1.28.1-20240422195039-812e347acd6b.4/go.mod h1:Db3JxaJwPkhOVCUnIS785SD7TEI4NPB4LvWpvlWr02E= +buf.build/gen/go/astria/primitives/protocolbuffers/go v1.33.0-20240422195039-812e347acd6b.1 h1:ANEC8IuONtxy0go3DZqyXonzPGQ8kxDe3EOJNoqRol8= +buf.build/gen/go/astria/primitives/protocolbuffers/go v1.33.0-20240422195039-812e347acd6b.1/go.mod h1:1VK/IapDxiDL/a+arAHdzNuaTSeC6suYHRK8AxJQrUw= +buf.build/gen/go/astria/sequencerblock-apis/grpc/go v1.3.0-20240423053322-44396ca8658a.2/go.mod h1:YdVN+ZAuYsnCS8PH/TNFApM+3Z+rc0lQ63tHKri1KTE= +buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.28.1-20240423053322-44396ca8658a.4/go.mod h1:Zn4mqAF/djuebKMIqCoYo5aQtZuuUlZ32u9AsbJPNVY= +buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.33.0-20240423053322-44396ca8658a.1 h1:ybwQ3f4wa6hjSYFD6vEw7xfkuPNTJm9IVefs7LwF8uw= +buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.33.0-20240423053322-44396ca8658a.1/go.mod h1:avEoXDW7L9QoMYCDJFVqBm5D/qyZ4BMg31Zi/SFQR6U= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -673,8 +677,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= @@ -1659,8 +1663,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/grpc/execution/server.go b/grpc/execution/server.go index 17e0dc3db1..b9a85681cc 100644 --- a/grpc/execution/server.go +++ b/grpc/execution/server.go @@ -5,7 +5,6 @@ package execution import ( - "bytes" "context" "crypto/sha256" "errors" @@ -14,9 +13,9 @@ import ( "sync" "time" - primitivev1 "buf.build/gen/go/astria/astria/protocolbuffers/go/astria/primitive/v1" astriaGrpc "buf.build/gen/go/astria/execution-apis/grpc/go/astria/execution/v1alpha2/executionv1alpha2grpc" astriaPb "buf.build/gen/go/astria/execution-apis/protocolbuffers/go/astria/execution/v1alpha2" + primitivev1 "buf.build/gen/go/astria/primitives/protocolbuffers/go/astria/primitive/v1" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -25,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/params" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" @@ -46,8 +46,11 @@ type ExecutionServiceServerV1Alpha2 struct { genesisInfoCalled bool getCommitmentStateCalled bool - bridgeAddresses map[string]struct{} - bridgeAllowedAssetID [32]byte + // astria bridge addess to config for that bridge account + bridgeAddresses map[string]*params.AstriaBridgeAddressConfig + // a set of allowed asset IDs structs are left empty + bridgeAllowedAssetIDs map[[32]byte]struct{} + nextFeeRecipient common.Address // Fee recipient for the next block } var ( @@ -91,30 +94,57 @@ func NewExecutionServiceServerV1Alpha2(eth *eth.Ethereum) (*ExecutionServiceServ return nil, errors.New("celestia height variance not set") } - if bc.Config().AstriaBridgeAddresses == nil { + bridgeAddresses := make(map[string]*params.AstriaBridgeAddressConfig) + bridgeAllowedAssetIDs := make(map[[32]byte]struct{}) + if bc.Config().AstriaBridgeAddressConfigs == nil { log.Warn("bridge addresses not set") - } + } else { + nativeBridgeSeen := false + for _, cfg := range bc.Config().AstriaBridgeAddressConfigs { + err := cfg.Validate() + if err != nil { + return nil, fmt.Errorf("invalid bridge address config: %w", err) + } - if bc.Config().AstriaBridgeAllowedAssetDenom == "" && bc.Config().AstriaBridgeAddresses != nil { - return nil, errors.New("bridge allowed asset denom not set") - } + if cfg.Erc20Asset != nil && nativeBridgeSeen { + return nil, errors.New("only one native bridge address is allowed") + } + if cfg.Erc20Asset != nil && !nativeBridgeSeen { + nativeBridgeSeen = true + } - bridgeAddresses := make(map[string]struct{}) - for _, addr := range bc.Config().AstriaBridgeAddresses { - bridgeAddresses[string(addr)] = struct{}{} + bridgeAddresses[string(cfg.BridgeAddress)] = &cfg + assetID := sha256.Sum256([]byte(cfg.AssetDenom)) + bridgeAllowedAssetIDs[assetID] = struct{}{} + } } - bridgeAllowedAssetID := sha256.Sum256([]byte(bc.Config().AstriaBridgeAllowedAssetDenom)) + // To decrease compute cost, we identify the next fee recipient at the start + // and update it as we execute blocks. + nextFeeRecipient := common.Address{} + if bc.Config().AstriaFeeCollectors == nil { + log.Warn("fee asset collectors not set, assets will be burned") + } else { + maxHeightCollectorMatch := uint32(0) + nextBlock := uint32(bc.CurrentBlock().Number.Int64()) + 1 + for height, collector := range bc.Config().AstriaFeeCollectors { + if height <= nextBlock && height > maxHeightCollectorMatch { + maxHeightCollectorMatch = height + nextFeeRecipient = collector + } + } + } if merger := eth.Merger(); !merger.PoSFinalized() { merger.FinalizePoS() } return &ExecutionServiceServerV1Alpha2{ - eth: eth, - bc: bc, - bridgeAddresses: bridgeAddresses, - bridgeAllowedAssetID: bridgeAllowedAssetID, + eth: eth, + bc: bc, + bridgeAddresses: bridgeAddresses, + bridgeAllowedAssetIDs: bridgeAllowedAssetIDs, + nextFeeRecipient: nextFeeRecipient, }, nil } @@ -213,21 +243,28 @@ func (s *ExecutionServiceServerV1Alpha2) ExecuteBlock(ctx context.Context, req * txsToProcess := types.Transactions{} for _, tx := range req.Transactions { if deposit := tx.GetDeposit(); deposit != nil { - bridgeAddress := string(deposit.BridgeAddress) - if _, ok := s.bridgeAddresses[bridgeAddress]; !ok { + bridgeAddress := string(deposit.BridgeAddress.GetInner()) + bac, ok := s.bridgeAddresses[bridgeAddress] + if !ok { log.Debug("ignoring deposit tx from unknown bridge", "bridgeAddress", bridgeAddress) continue } - if !bytes.Equal(deposit.AssetId, s.bridgeAllowedAssetID[:]) { + if len(deposit.AssetId) != 32 { + log.Debug("ignoring deposit tx with invalid asset ID", "assetID", deposit.AssetId) + } + assetID := [32]byte{} + copy(assetID[:], deposit.AssetId[:32]) + if _, ok := s.bridgeAllowedAssetIDs[assetID]; !ok { log.Debug("ignoring deposit tx with disallowed asset ID", "assetID", deposit.AssetId) continue } + amount := protoU128ToBigInt(deposit.Amount) address := common.HexToAddress(deposit.DestinationChainAddress) txdata := types.DepositTx{ From: address, - Value: protoU128ToBigInt(deposit.Amount), + Value: bac.ScaledDepositAmount(amount), Gas: 0, } @@ -264,7 +301,7 @@ func (s *ExecutionServiceServerV1Alpha2) ExecuteBlock(ctx context.Context, req * Parent: prevHeadHash, Timestamp: uint64(req.GetTimestamp().GetSeconds()), Random: common.Hash{}, - FeeRecipient: common.Address{}, + FeeRecipient: s.nextFeeRecipient, } payload, err := s.eth.Miner().BuildPayload(payloadAttributes) if err != nil { @@ -297,6 +334,10 @@ func (s *ExecutionServiceServerV1Alpha2) ExecuteBlock(ctx context.Context, req * }, } + if next, ok := s.bc.Config().AstriaFeeCollectors[res.Number+1]; ok { + s.nextFeeRecipient = next + } + log.Info("ExecuteBlock completed", "request", req, "response", res) totalExecutedTxCount.Inc(int64(len(block.Transactions()))) executeBlockSuccessCount.Inc(1) diff --git a/params/config.go b/params/config.go index 0c4bb52d4f..35e5c72fe9 100644 --- a/params/config.go +++ b/params/config.go @@ -337,14 +337,14 @@ type ChainConfig struct { IsDevMode bool `json:"isDev,omitempty"` // Astria Specific Configuration - AstriaOverrideGenesisExtraData bool `json:"astriaOverrideGenesisExtraData,omitempty"` - AstriaExtraDataOverride hexutil.Bytes `json:"astriaExtraDataOverride,omitempty"` - AstriaRollupName string `json:"astriaRollupName,omitempty"` - AstriaSequencerInitialHeight uint32 `json:"astriaSequencerInitialHeight"` - AstriaCelestiaInitialHeight uint32 `json:"astriaCelestiaInitialHeight"` - AstriaCelestiaHeightVariance uint32 `json:"astriaCelestiaHeightVariance,omitempty"` - AstriaBridgeAddresses []hexutil.Bytes `json:"astriaBridgeAddresses,omitempty"` - AstriaBridgeAllowedAssetDenom string `json:"astriaBridgeAllowedAssetDenom,omitempty"` + AstriaOverrideGenesisExtraData bool `json:"astriaOverrideGenesisExtraData,omitempty"` + AstriaExtraDataOverride hexutil.Bytes `json:"astriaExtraDataOverride,omitempty"` + AstriaRollupName string `json:"astriaRollupName"` + AstriaSequencerInitialHeight uint32 `json:"astriaSequencerInitialHeight"` + AstriaCelestiaInitialHeight uint32 `json:"astriaCelestiaInitialHeight"` + AstriaCelestiaHeightVariance uint32 `json:"astriaCelestiaHeightVariance,omitempty"` + AstriaBridgeAddressConfigs []AstriaBridgeAddressConfig `json:"astriaBridgeAddresses,omitempty"` + AstriaFeeCollectors map[uint32]common.Address `json:"astriaFeeCollectors"` } func (c *ChainConfig) AstriaExtraData() []byte { @@ -358,8 +358,6 @@ func (c *ChainConfig) AstriaExtraData() []byte { c.AstriaSequencerInitialHeight, c.AstriaCelestiaInitialHeight, c.AstriaCelestiaHeightVariance, - c.AstriaBridgeAddresses, - c.AstriaBridgeAllowedAssetDenom, }) if uint64(len(extra)) > MaximumExtraDataSize { log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", MaximumExtraDataSize) @@ -913,3 +911,52 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules IsVerkle: c.IsVerkle(num, timestamp), } } + +type AstriaBridgeAddressConfig struct { + BridgeAddress hexutil.Bytes `json:"bridgeAddress"` + StartHeight uint32 `json:"startHeight"` + AssetDenom string `json:"assetDenom"` + AssetPrecision uint16 `json:"assetPrecision"` + Erc20Asset *AstriaErc20AssetConfig `json:"erc20Asset,omitempty"` +} + +type AstriaErc20AssetConfig struct { + Erc20Address common.Address `json:"erc20Address"` + ContractPrecision uint16 `json:"contractPrecision"` +} + +func (abc *AstriaBridgeAddressConfig) Validate() error { + if len(abc.BridgeAddress) != 20 { + return fmt.Errorf("bridge address must be 20 bytes") + } + if abc.StartHeight == 0 { + return fmt.Errorf("start height must be greater than 0") + } + if abc.AssetDenom == "" { + return fmt.Errorf("asset denom must be set") + } + if abc.Erc20Asset == nil && abc.AssetPrecision > 18 { + return fmt.Errorf("asset precision of native asset must be less than or equal to 18") + } + if abc.Erc20Asset != nil && abc.AssetPrecision > abc.Erc20Asset.ContractPrecision { + return fmt.Errorf("asset precision must be less than or equal to contract precision") + } + // TODO: support erc20 bridged assets + if abc.Erc20Asset != nil { + return fmt.Errorf("cannot currently process erc20 bridged assets") + } + + return nil +} + +func (abc *AstriaBridgeAddressConfig) ScaledDepositAmount(deposit *big.Int) *big.Int { + var exponent uint16 + if abc.Erc20Asset != nil { + exponent = abc.Erc20Asset.ContractPrecision - abc.AssetPrecision + } else { + exponent = 18 - abc.AssetPrecision + } + multiplier := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(exponent)), nil) + + return new(big.Int).Mul(deposit, multiplier) +}