diff --git a/mod/chain-spec/go.mod b/mod/chain-spec/go.mod index ee6ebc0169..7ed953b767 100644 --- a/mod/chain-spec/go.mod +++ b/mod/chain-spec/go.mod @@ -3,13 +3,24 @@ module github.com/berachain/beacon-kit/mod/chain-spec go 1.23.0 require ( + github.com/berachain/beacon-kit/mod/errors v0.0.0-20240610210054-bfdc14c4013c github.com/berachain/beacon-kit/mod/primitives v0.0.0-20240911165923-82f71ec86570 github.com/stretchr/testify v1.9.0 ) require ( + github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/redact v1.1.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/getsentry/sentry-go v0.28.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/mod/chain-spec/go.sum b/mod/chain-spec/go.sum index 5f2efaee22..53f19deb43 100644 --- a/mod/chain-spec/go.sum +++ b/mod/chain-spec/go.sum @@ -1,20 +1,73 @@ +github.com/berachain/beacon-kit/mod/errors v0.0.0-20240610210054-bfdc14c4013c h1:rPoD2zVkIzuMC4R/XMuwx6eanJL8ccu37sLro+eIj3Y= +github.com/berachain/beacon-kit/mod/errors v0.0.0-20240610210054-bfdc14c4013c/go.mod h1:xgngH5/PYbyW+YDEmRhbBy3V333GXsNWF4DAkjYCmfs= github.com/berachain/beacon-kit/mod/primitives v0.0.0-20240911165923-82f71ec86570 h1:w0Gkg31VQRFDv0EJjYgVtlpza7kSaJq7U28zxZjfZeE= github.com/berachain/beacon-kit/mod/primitives v0.0.0-20240911165923-82f71ec86570/go.mod h1:Mrq1qol8vbkgZp2IMPFwngg75qE3k9IvT2MouBEhuus= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k= +github.com/getsentry/sentry-go v0.28.1/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/mod/chain-spec/pkg/chain/chain_spec.go b/mod/chain-spec/pkg/chain/chain_spec.go index 5d65f45e2c..828afae9f4 100644 --- a/mod/chain-spec/pkg/chain/chain_spec.go +++ b/mod/chain-spec/pkg/chain/chain_spec.go @@ -183,6 +183,17 @@ type Spec[ // GetCometBFTConfigForSlot retrieves the CometBFT config for a specific // slot. GetCometBFTConfigForSlot(slot SlotT) CometBFTConfigT + + // Berachain Values + + // EVMInflationAddress returns the address on the EVM which will receive + // the inflation amount of native EVM balance through a withdrawal every + // block. + EVMInflationAddress() ExecutionAddressT + + // EVMInflationPerBlock returns the amount of native EVM balance (in Gwei) + // to be minted to the EVMInflationAddress via a withdrawal every block. + EVMInflationPerBlock() uint64 } // chainSpec is a concrete implementation of the ChainSpec interface, holding @@ -207,14 +218,27 @@ func NewChainSpec[ CometBFTConfigT any, ](data SpecData[ DomainTypeT, EpochT, ExecutionAddressT, SlotT, CometBFTConfigT, -]) Spec[ +]) (Spec[ DomainTypeT, EpochT, ExecutionAddressT, SlotT, CometBFTConfigT, -] { - return &chainSpec[ +], error) { + c := &chainSpec[ DomainTypeT, EpochT, ExecutionAddressT, SlotT, CometBFTConfigT, ]{ Data: data, } + return c, c.validate() +} + +// validate ensures that the chain spec is valid, returning error if it is not. +func (c *chainSpec[ + DomainTypeT, EpochT, ExecutionAddressT, SlotT, CometBFTConfigT, +]) validate() error { + if c.MaxWithdrawalsPerPayload() <= 1 { + return ErrInsufficientMaxWithdrawalsPerPayload + } + + // TODO: Add more validation rules here. + return nil } // MinDepositAmount returns the minimum deposit amount required. @@ -476,3 +500,19 @@ func (c chainSpec[ ]) GetCometBFTConfigForSlot(_ SlotT) CometBFTConfigT { return c.Data.CometValues } + +// EVMInflationAddress returns the address on the EVM which will receive the +// inflation amount of native EVM balance through a withdrawal every block. +func (c chainSpec[ + DomainTypeT, EpochT, ExecutionAddressT, SlotT, CometBFTConfigT, +]) EVMInflationAddress() ExecutionAddressT { + return c.Data.EVMInflationAddress +} + +// EVMInflationPerBlock returns the amount of native EVM balance (in Gwei) to +// be minted to the EVMInflationAddress via a withdrawal every block. +func (c chainSpec[ + DomainTypeT, EpochT, ExecutionAddressT, SlotT, CometBFTConfigT, +]) EVMInflationPerBlock() uint64 { + return c.Data.EVMInflationPerBlock +} diff --git a/mod/chain-spec/pkg/chain/data.go b/mod/chain-spec/pkg/chain/data.go index 3c3ae2a637..308e563906 100644 --- a/mod/chain-spec/pkg/chain/data.go +++ b/mod/chain-spec/pkg/chain/data.go @@ -144,6 +144,15 @@ type SpecData[ // KZGCommitmentInclusionProofDepth is the depth of the KZG inclusion proof. KZGCommitmentInclusionProofDepth uint64 `mapstructure:"kzg-commitment-inclusion-proof-depth"` - // CometValues + // Comet Values CometValues CometBFTConfigT `mapstructure:"comet-bft-config"` + + // Berachain Values + // + // EVMInflationAddress is the address on the EVM which will receive the + // inflation amount of native EVM balance through a withdrawal every block. + EVMInflationAddress ExecutionAddressT `mapstructure:"evm-inflation-address"` + // EVMInflationPerBlock is the amount of native EVM balance (in Gwei) to be + // minted to the EVMInflationAddress via a withdrawal every block. + EVMInflationPerBlock uint64 `mapstructure:"evm-inflation-per-block"` } diff --git a/mod/chain-spec/pkg/chain/errors.go b/mod/chain-spec/pkg/chain/errors.go new file mode 100644 index 0000000000..6dc55f59ef --- /dev/null +++ b/mod/chain-spec/pkg/chain/errors.go @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: BUSL-1.1 +// +// Copyright (C) 2024, Berachain Foundation. All rights reserved. +// Use of this software is governed by the Business Source License included +// in the LICENSE file of this repository and at www.mariadb.com/bsl11. +// +// ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY +// TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER +// VERSIONS OF THE LICENSED WORK. +// +// THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF +// LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF +// LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE). +// +// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +// AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +// TITLE. + +package chain + +import "github.com/berachain/beacon-kit/mod/errors" + +var ( + // ErrInsufficientMaxWithdrawalsPerPayload is returned when the max + // withdrawals per payload less than 2. Must allow at least one for the EVM + // inflation withdrawal, and at least one more for a validator withdrawal + // per block. + ErrInsufficientMaxWithdrawalsPerPayload = errors.New( + "max withdrawals per payload must be greater than 1") +) diff --git a/mod/chain-spec/pkg/chain/helpers_test.go b/mod/chain-spec/pkg/chain/helpers_test.go index d0d97a5b62..1de79f616c 100644 --- a/mod/chain-spec/pkg/chain/helpers_test.go +++ b/mod/chain-spec/pkg/chain/helpers_test.go @@ -37,8 +37,10 @@ type ( cometBFTConfig struct{} ) +// TODO: Add setupValid, setupInvalid functions and use in each test. + // Create an instance of chainSpec with test data. -var spec = chain.NewChainSpec( +var spec, _ = chain.NewChainSpec( chain.SpecData[ domainType, epoch, executionAddress, slot, cometBFTConfig, ]{ @@ -46,6 +48,7 @@ var spec = chain.NewChainSpec( ElectraForkEpoch: 10, SlotsPerEpoch: 32, MinEpochsForBlobsSidecarsRequest: 5, + MaxWithdrawalsPerPayload: 2, }, ) diff --git a/mod/config/pkg/spec/betnet.go b/mod/config/pkg/spec/betnet.go index edaef1254c..289b663ca6 100644 --- a/mod/config/pkg/spec/betnet.go +++ b/mod/config/pkg/spec/betnet.go @@ -27,13 +27,13 @@ import ( ) // BetnetChainSpec is the ChainSpec for the localnet. -func BetnetChainSpec() chain.Spec[ +func BetnetChainSpec() (chain.Spec[ common.DomainType, math.Epoch, common.ExecutionAddress, math.Slot, any, -] { +], error) { testnetSpec := BaseSpec() testnetSpec.DepositEth1ChainID = BetnetEth1ChainID return chain.NewChainSpec(testnetSpec) diff --git a/mod/config/pkg/spec/boonet.go b/mod/config/pkg/spec/boonet.go index 83213baba2..36fe46083e 100644 --- a/mod/config/pkg/spec/boonet.go +++ b/mod/config/pkg/spec/boonet.go @@ -27,13 +27,13 @@ import ( ) // BoonetChainSpec is the ChainSpec for the localnet. -func BoonetChainSpec() chain.Spec[ +func BoonetChainSpec() (chain.Spec[ common.DomainType, math.Epoch, common.ExecutionAddress, math.Slot, any, -] { +], error) { testnetSpec := BaseSpec() testnetSpec.DepositEth1ChainID = BoonetEth1ChainID return chain.NewChainSpec(testnetSpec) diff --git a/mod/config/pkg/spec/constants.go b/mod/config/pkg/spec/constants.go index 2b0cea5548..cf978fdbd2 100644 --- a/mod/config/pkg/spec/constants.go +++ b/mod/config/pkg/spec/constants.go @@ -22,13 +22,13 @@ package spec const ( - // BoonetEth1ChainID is the chain ID for the local devnet. + // BoonetEth1ChainID is the chain ID for a local devnet. BoonetEth1ChainID uint64 = 80000 - // BetnetEth1ChainID is the chain ID for the local devnet. + // BetnetEth1ChainID is the chain ID for a local devnet. BetnetEth1ChainID uint64 = 80088 - // DevnetEth1ChainID is the chain ID for the local devnet. + // DevnetEth1ChainID is the chain ID for a local devnet. DevnetEth1ChainID uint64 = 80087 // TestnetEth1ChainID is the chain ID for the bArtio testnet. diff --git a/mod/config/pkg/spec/devnet.go b/mod/config/pkg/spec/devnet.go index 9e189b1d83..6b13acac17 100644 --- a/mod/config/pkg/spec/devnet.go +++ b/mod/config/pkg/spec/devnet.go @@ -27,13 +27,13 @@ import ( ) // DevnetChainSpec is the ChainSpec for the localnet. -func DevnetChainSpec() chain.Spec[ +func DevnetChainSpec() (chain.Spec[ common.DomainType, math.Epoch, common.ExecutionAddress, math.Slot, any, -] { +], error) { testnetSpec := BaseSpec() testnetSpec.DepositEth1ChainID = DevnetEth1ChainID return chain.NewChainSpec(testnetSpec) diff --git a/mod/config/pkg/spec/testnet.go b/mod/config/pkg/spec/testnet.go index a9d2970506..a3a76b3e97 100644 --- a/mod/config/pkg/spec/testnet.go +++ b/mod/config/pkg/spec/testnet.go @@ -28,14 +28,14 @@ import ( cmttypes "github.com/cometbft/cometbft/types" ) -// TestnetChainSpec is the ChainSpec for the localnet. -func TestnetChainSpec() chain.Spec[ +// TestnetChainSpec is the ChainSpec for the bArtio testnet. +func TestnetChainSpec() (chain.Spec[ common.DomainType, math.Epoch, common.ExecutionAddress, math.Slot, any, -] { +], error) { testnetSpec := BaseSpec() testnetSpec.DepositEth1ChainID = TestnetEth1ChainID return chain.NewChainSpec(testnetSpec) @@ -59,11 +59,11 @@ func BaseSpec() chain.SpecData[ math.Slot, any, ]{ - // // Gwei value constants. - MinDepositAmount: uint64(1e9), - MaxEffectiveBalance: uint64(32e9), - EjectionBalance: uint64(16e9), - EffectiveBalanceIncrement: uint64(1e9), + // Gwei value constants. + MinDepositAmount: 1e9, + MaxEffectiveBalance: 32e9, + EjectionBalance: 16e9, + EffectiveBalanceIncrement: 1e9, // Time parameters constants. SlotsPerEpoch: 32, MinEpochsToInactivityPenalty: 4, @@ -122,6 +122,17 @@ func BaseSpec() chain.SpecData[ FieldElementsPerBlob: 4096, BytesPerBlob: 131072, KZGCommitmentInclusionProofDepth: 17, - CometValues: cmtConsensusParams, + // Comet values. + CometValues: cmtConsensusParams, + // Berachain values. + // + // Expected address of the BGT contract on the bArtio testnet. + EVMInflationAddress: common.NewExecutionAddressFromHex( + "0x289274787bAF083C15A45a174b7a8e44F0720660", + ), + // EVM inflation of 10 BERA per block minting to back potential BGT + // token redemptions for BERA (sets the upper bound on amount of BGT + // that can be redeemed for BERA). + EVMInflationPerBlock: 10e9, } } diff --git a/mod/da/pkg/store/pruner_test.go b/mod/da/pkg/store/pruner_test.go index 6b3b12ee56..506b041780 100644 --- a/mod/da/pkg/store/pruner_test.go +++ b/mod/da/pkg/store/pruner_test.go @@ -107,18 +107,20 @@ func TestBuildPruneRangeFn(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cs := chain.NewChainSpec( + cs, err := chain.NewChainSpec( chain.SpecData[ bytes.B4, math.U64, common.ExecutionAddress, math.U64, any, ]{ SlotsPerEpoch: tt.slotsPerEpoch, MinEpochsForBlobsSidecarsRequest: tt.minEpochs, + MaxWithdrawalsPerPayload: 2, }, ) + require.NoError(t, err) pruneFn := store.BuildPruneRangeFn[MockBeaconBlock]( cs, ) - event := async.NewEvent[MockBeaconBlock]( + event := async.NewEvent( context.Background(), async.EventID("mock"), MockBeaconBlock{ diff --git a/mod/node-core/pkg/components/chain_spec.go b/mod/node-core/pkg/components/chain_spec.go index c70bb3b9fa..4bb7128ff8 100644 --- a/mod/node-core/pkg/components/chain_spec.go +++ b/mod/node-core/pkg/components/chain_spec.go @@ -28,28 +28,32 @@ import ( ) const ( - ChainSpecTypeEnvVar = "CHAIN_SPEC" - DevnetChainSpecType = "devnet" - BetnetChainSpecType = "betnet" - BoonetChainSpecType = "boonet" + ChainSpecTypeEnvVar = "CHAIN_SPEC" + DevnetChainSpecType = "devnet" + BetnetChainSpecType = "betnet" + BoonetChainSpecType = "boonet" + TestnetChainSpecType = "testnet" ) // ProvideChainSpec provides the chain spec based on the environment variable. -func ProvideChainSpec() common.ChainSpec { +func ProvideChainSpec() (common.ChainSpec, error) { // TODO: This is hood as fuck needs to be improved // but for now we ball to get CI unblocked. - specType := os.Getenv(ChainSpecTypeEnvVar) - var chainSpec common.ChainSpec - switch specType { + var ( + chainSpec common.ChainSpec + err error + ) + switch os.Getenv(ChainSpecTypeEnvVar) { case DevnetChainSpecType: - chainSpec = spec.DevnetChainSpec() + chainSpec, err = spec.DevnetChainSpec() case BetnetChainSpecType: - chainSpec = spec.BetnetChainSpec() + chainSpec, err = spec.BetnetChainSpec() case BoonetChainSpecType: - chainSpec = spec.BoonetChainSpec() + chainSpec, err = spec.BoonetChainSpec() + case TestnetChainSpecType: + fallthrough default: - chainSpec = spec.TestnetChainSpec() + chainSpec, err = spec.TestnetChainSpec() } - - return chainSpec + return chainSpec, err } diff --git a/mod/node-core/pkg/components/dispatcher.go b/mod/node-core/pkg/components/dispatcher.go index 028bbe2e3f..5ba612bca9 100644 --- a/mod/node-core/pkg/components/dispatcher.go +++ b/mod/node-core/pkg/components/dispatcher.go @@ -57,7 +57,9 @@ func ProvideDispatcher[ dp.WithEvent[async.Event[ConsensusSidecars]](async.SidecarsReceived), dp.WithEvent[async.Event[BeaconBlockT]](async.BeaconBlockVerified), dp.WithEvent[async.Event[BlobSidecarsT]](async.SidecarsVerified), - dp.WithEvent[async.Event[ConsensusBlockT]](async.FinalBeaconBlockReceived), + dp.WithEvent[async.Event[ConsensusBlockT]]( + async.FinalBeaconBlockReceived, + ), dp.WithEvent[async.Event[BlobSidecarsT]](async.FinalSidecarsReceived), dp.WithEvent[ValidatorUpdateEvent]( async.FinalValidatorUpdatesProcessed, diff --git a/mod/primitives/pkg/common/execution.go b/mod/primitives/pkg/common/execution.go index 3addc635fb..ece76afa56 100644 --- a/mod/primitives/pkg/common/execution.go +++ b/mod/primitives/pkg/common/execution.go @@ -21,6 +21,7 @@ package common import ( + "bytes" "encoding" "github.com/berachain/beacon-kit/mod/primitives/pkg/encoding/hex" @@ -97,6 +98,11 @@ func NewExecutionAddressFromHex(input string) ExecutionAddress { return ExecutionAddress(hex.MustToBytes(input)) } +// Equals returns true if the two addresses are the same. +func (a ExecutionAddress) Equals(other ExecutionAddress) bool { + return bytes.Equal(a[:], other[:]) +} + // Hex converts an address to a hex string. func (a ExecutionAddress) Hex() string { return string(a.checksumHex()) } diff --git a/mod/state-transition/pkg/core/helpers_test.go b/mod/state-transition/pkg/core/core_test.go similarity index 74% rename from mod/state-transition/pkg/core/helpers_test.go rename to mod/state-transition/pkg/core/core_test.go index 801c696d31..e9f19b0d05 100644 --- a/mod/state-transition/pkg/core/helpers_test.go +++ b/mod/state-transition/pkg/core/core_test.go @@ -30,20 +30,25 @@ import ( "cosmossdk.io/store" "cosmossdk.io/store/metrics" storetypes "cosmossdk.io/store/types" + "github.com/berachain/beacon-kit/mod/chain-spec/pkg/chain" "github.com/berachain/beacon-kit/mod/consensus-types/pkg/types" engineprimitives "github.com/berachain/beacon-kit/mod/engine-primitives/pkg/engine-primitives" "github.com/berachain/beacon-kit/mod/log/pkg/noop" "github.com/berachain/beacon-kit/mod/node-core/pkg/components" + "github.com/berachain/beacon-kit/mod/primitives/pkg/bytes" "github.com/berachain/beacon-kit/mod/primitives/pkg/common" - "github.com/berachain/beacon-kit/mod/primitives/pkg/crypto" + cryptomocks "github.com/berachain/beacon-kit/mod/primitives/pkg/crypto/mocks" + "github.com/berachain/beacon-kit/mod/primitives/pkg/math" "github.com/berachain/beacon-kit/mod/primitives/pkg/transition" "github.com/berachain/beacon-kit/mod/state-transition/pkg/core" + "github.com/berachain/beacon-kit/mod/state-transition/pkg/core/mocks" statedb "github.com/berachain/beacon-kit/mod/state-transition/pkg/core/state" "github.com/berachain/beacon-kit/mod/storage/pkg/beacondb" "github.com/berachain/beacon-kit/mod/storage/pkg/db" "github.com/berachain/beacon-kit/mod/storage/pkg/encoding" dbm "github.com/cosmos/cosmos-db" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -82,37 +87,8 @@ type ( *engineprimitives.Withdrawal, types.WithdrawalCredentials, ] -) -func createStateProcessor( - cs common.ChainSpec, - execEngine core.ExecutionEngine[ - *types.ExecutionPayload, - *types.ExecutionPayloadHeader, - engineprimitives.Withdrawals, - ], - signer crypto.BLSSigner, - fGetAddressFromPubKey func(crypto.BLSPubkey) ([]byte, error), -) *core.StateProcessor[ - *types.BeaconBlock, - *types.BeaconBlockBody, - *types.BeaconBlockHeader, - *TestBeaconStateT, - *transition.Context, - *types.Deposit, - *types.Eth1Data, - *types.ExecutionPayload, - *types.ExecutionPayloadHeader, - *types.Fork, - *types.ForkData, - *TestKVStoreT, - *types.Validator, - types.Validators, - *engineprimitives.Withdrawal, - engineprimitives.Withdrawals, - types.WithdrawalCredentials, -] { - return core.NewStateProcessor[ + TestStateProcessorT = core.StateProcessor[ *types.BeaconBlock, *types.BeaconBlockBody, *types.BeaconBlockHeader, @@ -130,14 +106,8 @@ func createStateProcessor( *engineprimitives.Withdrawal, engineprimitives.Withdrawals, types.WithdrawalCredentials, - ]( - noop.NewLogger[any](), - cs, - execEngine, - signer, - fGetAddressFromPubKey, - ) -} + ] +) type testKVStoreService struct { ctx sdk.Context @@ -199,6 +169,84 @@ func initStore() ( ), nil } +func setupChain(t *testing.T, chainSpecType string) chain.Spec[ + bytes.B4, math.U64, common.ExecutionAddress, math.U64, any, +] { + t.Helper() + + t.Setenv(components.ChainSpecTypeEnvVar, chainSpecType) + cs, err := components.ProvideChainSpec() + require.NoError(t, err) + + return cs +} + +func setupState( + t *testing.T, cs chain.Spec[ + bytes.B4, math.U64, common.ExecutionAddress, math.U64, any, + ], +) ( + *TestStateProcessorT, + *TestBeaconStateT, + *transition.Context, +) { + t.Helper() + + execEngine := mocks.NewExecutionEngine[ + *types.ExecutionPayload, + *types.ExecutionPayloadHeader, + engineprimitives.Withdrawals, + ](t) + + mocksSigner := &cryptomocks.BLSSigner{} + mocksSigner.On( + "VerifySignature", + mock.Anything, mock.Anything, mock.Anything, + ).Return(nil) + + dummyProposerAddr := []byte{0xff} + + sp := core.NewStateProcessor[ + *types.BeaconBlock, + *types.BeaconBlockBody, + *types.BeaconBlockHeader, + *TestBeaconStateT, + *transition.Context, + *types.Deposit, + *types.Eth1Data, + *types.ExecutionPayload, + *types.ExecutionPayloadHeader, + *types.Fork, + *types.ForkData, + *TestKVStoreT, + *types.Validator, + types.Validators, + *engineprimitives.Withdrawal, + engineprimitives.Withdrawals, + types.WithdrawalCredentials, + ]( + noop.NewLogger[any](), + cs, + execEngine, + mocksSigner, + func(bytes.B48) ([]byte, error) { + return dummyProposerAddr, nil + }, + ) + + kvStore, err := initStore() + require.NoError(t, err) + beaconState := new(TestBeaconStateT).NewFromDB(kvStore, cs) + + ctx := &transition.Context{ + SkipPayloadVerification: true, + SkipValidateResult: true, + ProposerAddress: dummyProposerAddr, + } + + return sp, beaconState, ctx +} + func buildNextBlock( t *testing.T, beaconState *TestBeaconStateT, diff --git a/mod/state-transition/pkg/core/errors.go b/mod/state-transition/pkg/core/errors.go index c384c8c198..d40f7a47a6 100644 --- a/mod/state-transition/pkg/core/errors.go +++ b/mod/state-transition/pkg/core/errors.go @@ -76,4 +76,9 @@ var ( // ErrNumWithdrawalsMismatch is returned when the number of withdrawals // in a block does not match the expected value. ErrNumWithdrawalsMismatch = errors.New("number of withdrawals mismatch") + + // ErrWithdrawalMismatch is returned when the withdrawals in a payload do + // not match the local state's expected value. + ErrWithdrawalMismatch = errors.New( + "withdrawal mismatch between local state and payload") ) diff --git a/mod/state-transition/pkg/core/state/statedb.go b/mod/state-transition/pkg/core/state/statedb.go index 830ad7e310..45bd704756 100644 --- a/mod/state-transition/pkg/core/state/statedb.go +++ b/mod/state-transition/pkg/core/state/statedb.go @@ -187,6 +187,9 @@ func (s *StateDB[ // ExpectedWithdrawals as defined in the Ethereum 2.0 Specification: // https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#new-get_expected_withdrawals // +// NOTE: This function is modified from the spec to allow a fixed withdrawal +// (as the first withdrawal) used for EVM inflation. +// //nolint:lll func (s *StateDB[ _, _, _, _, _, _, ValidatorT, _, WithdrawalT, _, @@ -196,8 +199,12 @@ func (s *StateDB[ balance math.Gwei withdrawalAddress common.ExecutionAddress withdrawals = make([]WithdrawalT, 0) + withdrawal WithdrawalT ) + // The first withdrawal is fixed to be the EVM inflation withdrawal. + withdrawals = append(withdrawals, s.EVMInflationWithdrawal()) + slot, err := s.GetSlot() if err != nil { return nil, err @@ -244,8 +251,6 @@ func (s *StateDB[ // Set the amount of the withdrawal depending on the balance of the // validator. - var withdrawal WithdrawalT - //nolint:gocritic // ok. if validator.IsFullyWithdrawable(balance, epoch) { withdrawals = append(withdrawals, withdrawal.New( @@ -284,8 +289,7 @@ func (s *StateDB[ } // Cap the number of withdrawals to the maximum allowed per payload. - //#nosec:G701 // won't overflow in practice. - if len(withdrawals) == int(s.cs.MaxWithdrawalsPerPayload()) { + if uint64(len(withdrawals)) == s.cs.MaxWithdrawalsPerPayload() { break } @@ -298,6 +302,22 @@ func (s *StateDB[ return withdrawals, nil } +// EVMInflationWithdrawal returns the withdrawal used for EVM balance inflation. +// +// NOTE: The withdrawal index and validator index are both set to 0 as they are +// not used during processing. +func (s *StateDB[ + _, _, _, _, _, _, _, _, WithdrawalT, _, +]) EVMInflationWithdrawal() WithdrawalT { + var withdrawal WithdrawalT + return withdrawal.New( + 0, + 0, + s.cs.EVMInflationAddress(), + math.Gwei(s.cs.EVMInflationPerBlock()), + ) +} + // GetMarshallable is the interface for the beacon store. // //nolint:funlen,gocognit // todo fix somehow diff --git a/mod/state-transition/pkg/core/state_processor_genesis.go b/mod/state-transition/pkg/core/state_processor_genesis.go index 7633ddfa5a..aff9516dc5 100644 --- a/mod/state-transition/pkg/core/state_processor_genesis.go +++ b/mod/state-transition/pkg/core/state_processor_genesis.go @@ -68,7 +68,6 @@ func (sp *StateProcessor[ } // Eth1DepositIndex will be set in processDeposit - if err := st.SetEth1Data( eth1Data.New( common.Root{}, @@ -145,9 +144,7 @@ func (sp *StateProcessor[ return nil, err } - if err = st.SetNextWithdrawalValidatorIndex( - 0, - ); err != nil { + if err = st.SetNextWithdrawalValidatorIndex(0); err != nil { return nil, err } diff --git a/mod/state-transition/pkg/core/state_processor_genesis_test.go b/mod/state-transition/pkg/core/state_processor_genesis_test.go index 95a88c0272..5f3f8031c6 100644 --- a/mod/state-transition/pkg/core/state_processor_genesis_test.go +++ b/mod/state-transition/pkg/core/state_processor_genesis_test.go @@ -24,68 +24,45 @@ import ( "testing" "github.com/berachain/beacon-kit/mod/chain-spec/pkg/chain" - "github.com/berachain/beacon-kit/mod/config/pkg/spec" "github.com/berachain/beacon-kit/mod/consensus-types/pkg/types" - engineprimitives "github.com/berachain/beacon-kit/mod/engine-primitives/pkg/engine-primitives" - "github.com/berachain/beacon-kit/mod/primitives/pkg/bytes" + "github.com/berachain/beacon-kit/mod/node-core/pkg/components" "github.com/berachain/beacon-kit/mod/primitives/pkg/common" "github.com/berachain/beacon-kit/mod/primitives/pkg/constants" - cryptomocks "github.com/berachain/beacon-kit/mod/primitives/pkg/crypto/mocks" "github.com/berachain/beacon-kit/mod/primitives/pkg/math" "github.com/berachain/beacon-kit/mod/primitives/pkg/version" - "github.com/berachain/beacon-kit/mod/state-transition/pkg/core/mocks" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) func TestInitialize(t *testing.T) { - // Create state processor to test - cs := spec.BetnetChainSpec() - execEngine := mocks.NewExecutionEngine[ - *types.ExecutionPayload, - *types.ExecutionPayloadHeader, - engineprimitives.Withdrawals, - ](t) - mocksSigner := &cryptomocks.BLSSigner{} - - sp := createStateProcessor( - cs, - execEngine, - mocksSigner, - dummyProposerAddressVerifier, - ) - - // create test inputs - kvStore, err := initStore() - require.NoError(t, err) - beaconState := new(TestBeaconStateT).NewFromDB(kvStore, cs) + cs := setupChain(t, components.BetnetChainSpecType) + sp, st, _ := setupState(t, cs) var ( deposits = []*types.Deposit{ { - Pubkey: [48]byte{0x01}, + Pubkey: [48]byte{0x00}, Amount: math.Gwei(cs.MaxEffectiveBalance()), - Index: uint64(0), + Index: 0, }, { - Pubkey: [48]byte{0x02}, + Pubkey: [48]byte{0x01}, Amount: math.Gwei(cs.MaxEffectiveBalance() / 2), - Index: uint64(1), + Index: 1, }, { - Pubkey: [48]byte{0x03}, + Pubkey: [48]byte{0x02}, Amount: math.Gwei(cs.EffectiveBalanceIncrement()), - Index: uint64(2), + Index: 2, }, { - Pubkey: [48]byte{0x04}, + Pubkey: [48]byte{0x03}, Amount: math.Gwei(2 * cs.MaxEffectiveBalance()), - Index: uint64(3), + Index: 3, }, { - Pubkey: [48]byte{0x05}, + Pubkey: [48]byte{0x04}, Amount: math.Gwei(cs.EffectiveBalanceIncrement() * 2 / 3), - Index: uint64(4), + Index: 4, }, } executionPayloadHeader = new(types.ExecutionPayloadHeader).Empty() @@ -96,18 +73,9 @@ func TestInitialize(t *testing.T) { } ) - // define mocks expectations - mocksSigner.On( - "VerifySignature", - mock.Anything, mock.Anything, mock.Anything, - ).Return(nil) - // run test vals, err := sp.InitializePreminedBeaconStateFromEth1( - beaconState, - deposits, - executionPayloadHeader, - fork.CurrentVersion, + st, deposits, executionPayloadHeader, fork.CurrentVersion, ) // check outputs @@ -115,20 +83,20 @@ func TestInitialize(t *testing.T) { require.Len(t, vals, len(deposits)) // check beacon state changes - resSlot, err := beaconState.GetSlot() + resSlot, err := st.GetSlot() require.NoError(t, err) require.Equal(t, math.Slot(0), resSlot) - resFork, err := beaconState.GetFork() + resFork, err := st.GetFork() require.NoError(t, err) require.Equal(t, fork, resFork) for _, dep := range deposits { - checkValidatorNonBartio(t, cs, beaconState, dep) + checkValidatorNonBartio(t, cs, st, dep) } // check that validator index is duly set - latestValIdx, err := beaconState.GetEth1DepositIndex() + latestValIdx, err := st.GetEth1DepositIndex() require.NoError(t, err) require.Equal(t, uint64(len(deposits)-1), latestValIdx) } @@ -160,53 +128,35 @@ func checkValidatorNonBartio( } func TestInitializeBartio(t *testing.T) { - // Create state processor to test - cs := spec.TestnetChainSpec() - execEngine := mocks.NewExecutionEngine[ - *types.ExecutionPayload, - *types.ExecutionPayloadHeader, - engineprimitives.Withdrawals, - ](t) - mocksSigner := &cryptomocks.BLSSigner{} - - sp := createStateProcessor( - cs, - execEngine, - mocksSigner, - dummyProposerAddressVerifier, - ) - - // create test inputs - kvStore, err := initStore() - require.NoError(t, err) - beaconState := new(TestBeaconStateT).NewFromDB(kvStore, cs) + cs := setupChain(t, components.TestnetChainSpecType) + sp, st, _ := setupState(t, cs) var ( deposits = []*types.Deposit{ { - Pubkey: [48]byte{0x01}, + Pubkey: [48]byte{0x00}, Amount: math.Gwei(cs.MaxEffectiveBalance()), - Index: uint64(0), + Index: 0, }, { - Pubkey: [48]byte{0x02}, + Pubkey: [48]byte{0x01}, Amount: math.Gwei(cs.MaxEffectiveBalance() / 2), - Index: uint64(1), + Index: 1, }, { - Pubkey: [48]byte{0x03}, + Pubkey: [48]byte{0x02}, Amount: math.Gwei(cs.EffectiveBalanceIncrement()), - Index: uint64(2), + Index: 2, }, { - Pubkey: [48]byte{0x04}, + Pubkey: [48]byte{0x03}, Amount: math.Gwei(2 * cs.MaxEffectiveBalance()), - Index: uint64(3), + Index: 3, }, { - Pubkey: [48]byte{0x05}, + Pubkey: [48]byte{0x04}, Amount: math.Gwei(cs.EffectiveBalanceIncrement() * 2 / 3), - Index: uint64(4), + Index: 4, }, } executionPayloadHeader = new(types.ExecutionPayloadHeader).Empty() @@ -217,18 +167,9 @@ func TestInitializeBartio(t *testing.T) { } ) - // define mocks expectations - mocksSigner.On( - "VerifySignature", - mock.Anything, mock.Anything, mock.Anything, - ).Return(nil) - // run test vals, err := sp.InitializePreminedBeaconStateFromEth1( - beaconState, - deposits, - executionPayloadHeader, - fork.CurrentVersion, + st, deposits, executionPayloadHeader, fork.CurrentVersion, ) // check outputs @@ -236,20 +177,20 @@ func TestInitializeBartio(t *testing.T) { require.Len(t, vals, len(deposits)) // check beacon state changes - resSlot, err := beaconState.GetSlot() + resSlot, err := st.GetSlot() require.NoError(t, err) require.Equal(t, math.Slot(0), resSlot) - resFork, err := beaconState.GetFork() + resFork, err := st.GetFork() require.NoError(t, err) require.Equal(t, fork, resFork) for _, dep := range deposits { - checkValidatorBartio(t, cs, beaconState, dep) + checkValidatorBartio(t, cs, st, dep) } // check that validator index is duly set - latestValIdx, err := beaconState.GetEth1DepositIndex() + latestValIdx, err := st.GetEth1DepositIndex() require.NoError(t, err) require.Equal(t, uint64(len(deposits)-1), latestValIdx) } @@ -320,9 +261,3 @@ func commonChecksValidators( require.Equal(t, math.Gwei(0), val.EffectiveBalance) } } - -// in genesis UTs we don't need to verify proposer address -// (no one proposes genesis), hence the dummy implementation. -func dummyProposerAddressVerifier(bytes.B48) ([]byte, error) { - return nil, nil -} diff --git a/mod/state-transition/pkg/core/state_processor_staking.go b/mod/state-transition/pkg/core/state_processor_staking.go index c161d32864..57a56f7160 100644 --- a/mod/state-transition/pkg/core/state_processor_staking.go +++ b/mod/state-transition/pkg/core/state_processor_staking.go @@ -225,6 +225,9 @@ func (sp *StateProcessor[ // processWithdrawals as per the Ethereum 2.0 specification. // https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#new-process_withdrawals // +// NOTE: This function is modified from the spec to allow a fixed withdrawal +// (must be the first withdrawal) used for EVM inflation. +// //nolint:lll func (sp *StateProcessor[ _, BeaconBlockBodyT, _, BeaconStateT, _, _, _, _, _, _, _, _, _, _, _, _, _, @@ -232,7 +235,6 @@ func (sp *StateProcessor[ st BeaconStateT, body BeaconBlockBodyT, ) error { - // Dequeue and verify the logs. var ( nextValidatorIndex math.ValidatorIndex payload = body.GetExecutionPayload() @@ -246,7 +248,7 @@ func (sp *StateProcessor[ } numWithdrawals := len(expectedWithdrawals) - // Ensure the withdrawals have the same length + // Ensure the expected and payload withdrawals have the same length. if numWithdrawals != len(payloadWithdrawals) { return errors.Wrapf( ErrNumWithdrawalsMismatch, @@ -260,13 +262,19 @@ func (sp *StateProcessor[ // Ensure the withdrawals match the local state. if !wd.Equals(payloadWithdrawals[i]) { return errors.Wrapf( - ErrNumWithdrawalsMismatch, - "withdrawals do not match expected %s, got %s", - spew.Sdump(wd), spew.Sdump(payloadWithdrawals[i]), + ErrWithdrawalMismatch, + "withdrawal at index %d does not match expected %s, got %s", + i, spew.Sdump(wd), spew.Sdump(payloadWithdrawals[i]), ) } - // Then we process the withdrawal. + // The first withdrawal is the EVM inflation withdrawal. No processing + // is needed. + if i == 0 { + continue + } + + // Process the validator withdrawal. if err = st.DecreaseBalance( wd.GetValidatorIndex(), wd.GetAmount(), ); err != nil { @@ -275,8 +283,9 @@ func (sp *StateProcessor[ } // Update the next withdrawal index if this block contained withdrawals - if numWithdrawals != 0 { - // Next sweep starts after the latest withdrawal's validator index + // (excluding the EVM inflation withdrawal). + if numWithdrawals > 1 { + // Next sweep starts after the latest withdrawal's validator index. if err = st.SetNextWithdrawalIndex( (expectedWithdrawals[numWithdrawals-1].GetIndex() + 1).Unwrap(), ); err != nil { @@ -289,16 +298,16 @@ func (sp *StateProcessor[ return err } - // Update the next validator index to start the next withdrawal sweep + // Update the next validator index to start the next withdrawal sweep. //#nosec:G701 // won't overflow in practice. if numWithdrawals == int(sp.cs.MaxWithdrawalsPerPayload()) { - // Next sweep starts after the latest withdrawal's validator index + // Next sweep starts after the latest withdrawal's validator index. nextValidatorIndex = - (expectedWithdrawals[len(expectedWithdrawals)-1].GetIndex() + 1) % - math.U64(totalValidators) + (expectedWithdrawals[numWithdrawals-1].GetValidatorIndex() + 1) % + math.ValidatorIndex(totalValidators) } else { - // Advance sweep by the max length of the sweep if there was not - // a full set of withdrawals + // Advance sweep by the max length of the sweep if there was not a full + // set of withdrawals. nextValidatorIndex, err = st.GetNextWithdrawalValidatorIndex() if err != nil { return err diff --git a/mod/state-transition/pkg/core/state_processor_staking_test.go b/mod/state-transition/pkg/core/state_processor_staking_test.go index 8ed6c99b9c..52daf4e321 100644 --- a/mod/state-transition/pkg/core/state_processor_staking_test.go +++ b/mod/state-transition/pkg/core/state_processor_staking_test.go @@ -23,50 +23,26 @@ package core_test import ( "testing" + "github.com/berachain/beacon-kit/mod/chain-spec/pkg/chain" "github.com/berachain/beacon-kit/mod/config/pkg/spec" "github.com/berachain/beacon-kit/mod/consensus-types/pkg/types" engineprimitives "github.com/berachain/beacon-kit/mod/engine-primitives/pkg/engine-primitives" - "github.com/berachain/beacon-kit/mod/primitives/pkg/bytes" + "github.com/berachain/beacon-kit/mod/node-core/pkg/components" "github.com/berachain/beacon-kit/mod/primitives/pkg/common" - cryptomocks "github.com/berachain/beacon-kit/mod/primitives/pkg/crypto/mocks" "github.com/berachain/beacon-kit/mod/primitives/pkg/math" - "github.com/berachain/beacon-kit/mod/primitives/pkg/transition" "github.com/berachain/beacon-kit/mod/primitives/pkg/version" - "github.com/berachain/beacon-kit/mod/state-transition/pkg/core/mocks" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) func TestTransitionUpdateValidators(t *testing.T) { - // Create state processor to test - cs := spec.BetnetChainSpec() - execEngine := mocks.NewExecutionEngine[ - *types.ExecutionPayload, - *types.ExecutionPayloadHeader, - engineprimitives.Withdrawals, - ](t) - mocksSigner := &cryptomocks.BLSSigner{} - dummyProposerAddr := []byte{0xff} - - sp := createStateProcessor( - cs, - execEngine, - mocksSigner, - func(bytes.B48) ([]byte, error) { - return dummyProposerAddr, nil - }, - ) - - kvStore, err := initStore() - require.NoError(t, err) - beaconState := new(TestBeaconStateT).NewFromDB(kvStore, cs) + cs := setupChain(t, components.BetnetChainSpecType) + sp, st, ctx := setupState(t, cs) var ( maxBalance = math.Gwei(cs.MaxEffectiveBalance()) minBalance = math.Gwei(cs.EffectiveBalanceIncrement()) - emptyAddress = common.ExecutionAddress{} emptyCredentials = types.NewCredentialsFromExecutionAddress( - emptyAddress, + common.ExecutionAddress{}, ) ) @@ -75,61 +51,47 @@ func TestTransitionUpdateValidators(t *testing.T) { var ( genDeposits = []*types.Deposit{ { - Pubkey: [48]byte{0x01}, + Pubkey: [48]byte{0x00}, Credentials: emptyCredentials, Amount: maxBalance - 3*minBalance, - Index: uint64(0), + Index: 0, }, { - Pubkey: [48]byte{0x02}, + Pubkey: [48]byte{0x01}, Credentials: emptyCredentials, Amount: maxBalance - 6*minBalance, - Index: uint64(1), + Index: 1, }, } genPayloadHeader = new(types.ExecutionPayloadHeader).Empty() genVersion = version.FromUint32[common.Version](version.Deneb) ) - - mocksSigner.On( - "VerifySignature", - mock.Anything, mock.Anything, mock.Anything, - ).Return(nil) - - _, err = sp.InitializePreminedBeaconStateFromEth1( - beaconState, - genDeposits, - genPayloadHeader, - genVersion, + _, err := sp.InitializePreminedBeaconStateFromEth1( + st, genDeposits, genPayloadHeader, genVersion, ) require.NoError(t, err) // create test inputs - var ( - ctx = &transition.Context{ - SkipPayloadVerification: true, - SkipValidateResult: true, - ProposerAddress: dummyProposerAddr, - } - blkDeposits = []*types.Deposit{ - { - Pubkey: genDeposits[0].Pubkey, - Credentials: emptyCredentials, - Amount: minBalance, // avoid breaching maxBalance - Index: genDeposits[0].Index, - }, - } - ) - + blkDeposits := []*types.Deposit{ + { + Pubkey: genDeposits[0].Pubkey, + Credentials: emptyCredentials, + Amount: minBalance, // avoid breaching maxBalance + Index: genDeposits[0].Index, + }, + } blk := buildNextBlock( t, - beaconState, + st, &types.BeaconBlockBody{ ExecutionPayload: &types.ExecutionPayload{ - Timestamp: 10, - ExtraData: []byte("testing"), - Transactions: [][]byte{}, - Withdrawals: []*engineprimitives.Withdrawal{}, // no withdrawals + Timestamp: 10, + ExtraData: []byte("testing"), + Transactions: [][]byte{}, + Withdrawals: []*engineprimitives.Withdrawal{ + // The first withdrawal is always for EVM inflation. + st.EVMInflationWithdrawal(), + }, BaseFeePerGas: math.NewU256(0), }, Eth1Data: &types.Eth1Data{}, @@ -138,7 +100,7 @@ func TestTransitionUpdateValidators(t *testing.T) { ) // run the test - vals, err := sp.Transition(ctx, beaconState, blk) + vals, err := sp.Transition(ctx, st, blk) // check outputs require.NoError(t, err) @@ -146,22 +108,246 @@ func TestTransitionUpdateValidators(t *testing.T) { // check validator is duly updated expectedValBalance := genDeposits[0].Amount + blkDeposits[0].Amount - idx, err := beaconState.ValidatorIndexByPubkey(genDeposits[0].Pubkey) + idx, err := st.ValidatorIndexByPubkey(genDeposits[0].Pubkey) require.NoError(t, err) require.Equal(t, math.U64(genDeposits[0].Index), idx) - val, err := beaconState.ValidatorByIndex(idx) + val, err := st.ValidatorByIndex(idx) require.NoError(t, err) require.Equal(t, genDeposits[0].Pubkey, val.Pubkey) require.Equal(t, expectedValBalance, val.EffectiveBalance) // check validator balance is updated - valBal, err := beaconState.GetBalance(idx) + valBal, err := st.GetBalance(idx) require.NoError(t, err) require.Equal(t, expectedValBalance, valBal) // check that validator index is duly set (1-indexed here, to be fixed) - latestValIdx, err := beaconState.GetEth1DepositIndex() + latestValIdx, err := st.GetEth1DepositIndex() require.NoError(t, err) require.Equal(t, uint64(len(genDeposits)), latestValIdx) } + +func TestTransitionWithdrawals(t *testing.T) { + cs := setupChain(t, components.BetnetChainSpecType) + sp, st, ctx := setupState(t, cs) + + var ( + maxBalance = math.Gwei(cs.MaxEffectiveBalance()) + minBalance = math.Gwei(cs.EffectiveBalanceIncrement()) + credentials0 = types.NewCredentialsFromExecutionAddress( + common.ExecutionAddress{}, + ) + address1 = common.ExecutionAddress{0x01} + credentials1 = types.NewCredentialsFromExecutionAddress(address1) + ) + + // Setup initial state so that validator 1 is partially withdrawable. + var ( + genDeposits = []*types.Deposit{ + { + Pubkey: [48]byte{0x00}, + Credentials: credentials0, + Amount: maxBalance - 3*minBalance, + Index: 0, + }, + { + Pubkey: [48]byte{0x01}, + Credentials: credentials1, + Amount: maxBalance + minBalance, + Index: 1, + }, + } + genPayloadHeader = new(types.ExecutionPayloadHeader).Empty() + genVersion = version.FromUint32[common.Version](version.Deneb) + ) + _, err := sp.InitializePreminedBeaconStateFromEth1( + st, genDeposits, genPayloadHeader, genVersion, + ) + require.NoError(t, err) + + // Assert validator 1 balance before withdrawal. + val1Bal, err := st.GetBalance(math.U64(1)) + require.NoError(t, err) + require.Equal(t, maxBalance+minBalance, val1Bal) + + // Create test inputs. + blk := buildNextBlock( + t, + st, + &types.BeaconBlockBody{ + ExecutionPayload: &types.ExecutionPayload{ + Timestamp: 10, + ExtraData: []byte("testing"), + Transactions: [][]byte{}, + Withdrawals: []*engineprimitives.Withdrawal{ + // The first withdrawal is always for EVM inflation. + st.EVMInflationWithdrawal(), + // Partially withdraw validator 1 by minBalance. + { + Index: 0, + Validator: 1, + Amount: minBalance, + Address: address1, + }, + }, + BaseFeePerGas: math.NewU256(0), + }, + Eth1Data: &types.Eth1Data{}, + Deposits: []*types.Deposit{}, + }, + ) + + // Run the test. + vals, err := sp.Transition(ctx, st, blk) + + // Check outputs and ensure withdrawals in payload is consistent with + // statedb expected withdrawals. + require.NoError(t, err) + require.Zero(t, vals) + + // Assert validator 1 balance after withdrawal. + val1BalAfter, err := st.GetBalance(math.U64(1)) + require.NoError(t, err) + require.Equal(t, maxBalance, val1BalAfter) +} + +func TestTransitionMaxWithdrawals(t *testing.T) { + // Use custom chain spec with max withdrawals set to 2. + csData := spec.BaseSpec() + csData.DepositEth1ChainID = spec.BetnetEth1ChainID + csData.MaxWithdrawalsPerPayload = 2 + cs, err := chain.NewChainSpec(csData) + require.NoError(t, err) + + sp, st, ctx := setupState(t, cs) + + var ( + maxBalance = math.Gwei(cs.MaxEffectiveBalance()) + minBalance = math.Gwei(cs.EffectiveBalanceIncrement()) + address0 = common.ExecutionAddress{} + credentials0 = types.NewCredentialsFromExecutionAddress(address0) + address1 = common.ExecutionAddress{0x01} + credentials1 = types.NewCredentialsFromExecutionAddress(address1) + ) + + // Setup initial state so that both validators are partially withdrawable. + var ( + genDeposits = []*types.Deposit{ + { + Pubkey: [48]byte{0x00}, + Credentials: credentials0, + Amount: maxBalance + minBalance, + Index: 0, + }, + { + Pubkey: [48]byte{0x01}, + Credentials: credentials1, + Amount: maxBalance + minBalance, + Index: 1, + }, + } + genPayloadHeader = new(types.ExecutionPayloadHeader).Empty() + genVersion = version.FromUint32[common.Version](version.Deneb) + ) + _, err = sp.InitializePreminedBeaconStateFromEth1( + st, genDeposits, genPayloadHeader, genVersion, + ) + require.NoError(t, err) + + // Assert validator balances before withdrawal. + val0Bal, err := st.GetBalance(math.U64(0)) + require.NoError(t, err) + require.Equal(t, maxBalance+minBalance, val0Bal) + + val1Bal, err := st.GetBalance(math.U64(1)) + require.NoError(t, err) + require.Equal(t, maxBalance+minBalance, val1Bal) + + // Create test inputs. + blk := buildNextBlock( + t, + st, + &types.BeaconBlockBody{ + ExecutionPayload: &types.ExecutionPayload{ + Timestamp: 10, + ExtraData: []byte("testing"), + Transactions: [][]byte{}, + Withdrawals: []*engineprimitives.Withdrawal{ + // The first withdrawal is always for EVM inflation. + st.EVMInflationWithdrawal(), + // Partially withdraw validator 0 by minBalance. + { + Index: 0, + Validator: 0, + Amount: minBalance, + Address: address0, + }, + }, + BaseFeePerGas: math.NewU256(0), + }, + Eth1Data: &types.Eth1Data{}, + Deposits: []*types.Deposit{}, + }, + ) + + // Run the test. + vals, err := sp.Transition(ctx, st, blk) + + // Check outputs and ensure withdrawals in payload is consistent with + // statedb expected withdrawals. + require.NoError(t, err) + require.Zero(t, vals) + + // Assert validator balances after withdrawal, ensuring only validator 0 is + // withdrawn from. + val0BalAfter, err := st.GetBalance(math.U64(0)) + require.NoError(t, err) + require.Equal(t, maxBalance, val0BalAfter) + + val1BalAfter, err := st.GetBalance(math.U64(1)) + require.NoError(t, err) + require.Equal(t, maxBalance+minBalance, val1BalAfter) + + // Process the next block, ensuring that validator 1 is also withdrawn from, + // also ensuring that the state's next withdrawal (validator) index is + // appropriately incremented. + blk = buildNextBlock( + t, + st, + &types.BeaconBlockBody{ + ExecutionPayload: &types.ExecutionPayload{ + Timestamp: 11, + ExtraData: []byte("testing"), + Transactions: [][]byte{}, + Withdrawals: []*engineprimitives.Withdrawal{ + // The first withdrawal is always for EVM inflation. + st.EVMInflationWithdrawal(), + // Partially withdraw validator 1 by minBalance. + { + Index: 1, + Validator: 1, + Amount: minBalance, + Address: address1, + }, + }, + BaseFeePerGas: math.NewU256(0), + }, + Eth1Data: &types.Eth1Data{}, + Deposits: []*types.Deposit{}, + }, + ) + + // Run the test. + vals, err = sp.Transition(ctx, st, blk) + + // Check outputs and ensure withdrawals in payload is consistent with + // statedb expected withdrawals. + require.NoError(t, err) + require.Zero(t, vals) + + // Validator 1 is now withdrawn from. + val1BalAfter, err = st.GetBalance(math.U64(1)) + require.NoError(t, err) + require.Equal(t, maxBalance, val1BalAfter) +} diff --git a/testing/e2e/config/defaults.go b/testing/e2e/config/defaults.go index 5cb40423d0..4ab358c787 100644 --- a/testing/e2e/config/defaults.go +++ b/testing/e2e/config/defaults.go @@ -31,3 +31,9 @@ const ( DepositContractAddress = "0x4242424242424242424242424242424242424242" NumDepositsLoad = 500 ) + +// EVM Inflation. +const ( + EVMInflationAddress = "0x6942069420694206942069420694206942069420" + EVMInflationPerBlockWei = 10e18 +) diff --git a/testing/e2e/e2e_startup_test.go b/testing/e2e/e2e_startup_test.go index 8ec0de8872..52665490af 100644 --- a/testing/e2e/e2e_startup_test.go +++ b/testing/e2e/e2e_startup_test.go @@ -21,7 +21,11 @@ package e2e_test import ( + "math/big" + + "github.com/berachain/beacon-kit/testing/e2e/config" "github.com/berachain/beacon-kit/testing/e2e/suite" + gethcommon "github.com/ethereum/go-ethereum/common" ) // BeaconE2ESuite is a suite of tests simulating a fully function beacon-kit @@ -31,8 +35,32 @@ type BeaconKitE2ESuite struct { } // TestBasicStartup tests the basic startup of the beacon-kit network. -// TODO: Should check all clients, opposed to the load balancer. +// TODO: Should check all clients, opposed to just the load balancer. func (s *BeaconKitE2ESuite) TestBasicStartup() { err := s.WaitForFinalizedBlockNumber(10) s.Require().NoError(err) } + +// TestEVMInflation checks that the EVM inflation address receives the correct +// amount of EVM inflation per block. +func (s *BeaconKitE2ESuite) TestEVMInflation() { + evmInflationPerBlockWei, _ := big.NewFloat( + config.EVMInflationPerBlockWei).Int(nil) + + // Check over the next 10 EVM blocks, that after every block, the balance + // of the EVM inflation address increases by EVMInflationPerBlockWei. + for i := int64(0); i <= 10; i++ { + err := s.WaitForFinalizedBlockNumber(uint64(i)) + s.Require().NoError(err) + + balance, err := s.JSONRPCBalancer().BalanceAt( + s.Ctx(), + gethcommon.HexToAddress(config.EVMInflationAddress), + big.NewInt(i), + ) + s.Require().NoError(err) + s.Require().Zero(balance.Cmp(new(big.Int).Mul( + evmInflationPerBlockWei, big.NewInt(i)), + )) + } +}