Bahamut Execution and Consensus

CertiKFiles named in the report

CertiK reviewed Fastex (Bahamut Execution and Consensus) on 13 Aug 2023: 30 findings, 0 critical; 25 marked fixed, 5 acknowledged.

No open critical findings
Open the reportOpen Fastex’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings30

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical000
High010
Medium005
Low0420
MediumIncorrect Generation of `randomByte` in FunctionFixed

Files: - `beacon-chain/core/altair/sync_committee.go` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) The incorrect generation of `randByte` allows any active validator to be selected in the sync committee regardless of their effective balances, which does not align with the consensus algorithm. The function `NextSyncCommitte()` is used to select the sync committee members from the active validators. In the current code, Fasttoken implements two algorithms according to the version. ```go=61 func NextSyncCommittee(ctx context.Context, s state.BeaconState) (*ethpb.SyncCommittee, error) { var indices []primitives.ValidatorIndex var err error if s.Version() < version.FastexPhase1 { indices, err = NextSyncCommitteeIndices(ctx, s) } else { indices, err = NextSyncCommitteeIndicesFastexPhase1(ctx, s) } ... ``` If the version is less than the `FastexPhase1`, it uses the custom algorithm that applies the validator power by invoking the function `NextSyncCommitteeIndices()`. On the other hand, if the version is in post `FastexPhase1`, the algorithm inherits the original one from Ethereum Proof of Stake, which is implemented in the function `NextSyncCommitteeIndicesFastexPhase1()`. Both algorithms use the same randomness generation and the same `maxRandomByte` (= 65535). In the function `NextSyncCommitteeIndices()`, the `randomBytes` is generated by two bytes, so `randomBytes` is in the range of 0 and 65535. **NextSyncCommitteeIndices()** ```go=127 b := append(seed[:], bytesutil.Bytes8(uint64(i.Div(16)))...) hash := hashFunc(b) bytes2 := append([]byte{}, hash[i%16], hash[16+i%16]) randomBytes := new(big.Float).SetUint64(uint64(bytesutil.FromBytes2(bytes2))) ``` However, the `randomByte` in the function `NextSyncCommitteeIndicesFastexPhase1()` only has one byte, which is in the range of 0 and 255. In this case, the ratio `randomByte`/`maxRandomByte` is too small which allows almost all validators to be selected regardless of their effective balances. In other words, the effective balance does not affect the chance of a validator to be selected. **NextSyncCommitteeIndicesFastexPhase1()** ```go=200 b := append(seed[:], bytesutil.Bytes8(uint64(i.Div(32)))...) randomByte := hashFunc(b)[i%32] cIndex := indices[sIndex] v, err := s.ValidatorAtIndexReadOnly(cIndex) if err != nil { return nil, err } effectiveBal := v.EffectiveBalance() if effectiveBal*maxRandomByte >= cfg.MaxEffectiveBalance*uint64(randomByte) { cIndices = append(cIndices, cIndex) } ```

MediumLogical Flaw in Function `filter()` Could InvokeFixed

Files: - `beacon-chain/rpc/prysm/v1alpha1/validator/proposer_attestations.go` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) The function `filter()` is intended to filter the attestation list into valid and invalid attestations separately, which has different implementations according to different versions. However, there is a logical flaw introduced in lines 91-108 due to the fact that `version.Altair < version.FastexPhase1` (i.e., 1 < 3): ```go=91 } else if st.Version() >= version.Altair { // Use a wrapper here, as go needs strong typing for the function signature. attestationProcessor = func(ctx context.Context, st state.BeaconState, attestation *ethpb.Attestation) (state.BeaconState, error) { totalBalance, err := helpers.TotalActiveBalance(st) if err != nil { return nil, err } return altair.ProcessAttestationNoVerifySignature(ctx, st, attestation, totalBalance) } } else if st.Version() >= version.FastexPhase1 { // Use a wrapper here, as go needs strong typing for the function signature. attestationProcessor = func(ctx context.Context, st state.BeaconState, attestation *ethpb.Attestation) (state.BeaconState, error) { totalBalance, err := helpers.TotalActiveBalance(st) if err != nil { return nil, err } return fastexphase1.ProcessAttestationNoVerifySignature(ctx, st, attestation, totalBalance) } ``` The branch `st.Version() >= version.FastexPhase1` is unreachable because any version not less than `version.Altair` will enter the branch `st.Version() >= version.Altair` in line 91. In this case, if the current version is in the post-FastexPhase1, it will use the function `altair.ProcessAttestationNoVerifySignature()` instead of the function `fastexphase1.ProcessAttestationNoVerifySignature()` as the `attestationProcessor`, which could lead to an unexpected result. For example, different implementations of the function `RewardProposer()` will be invoked.

MediumMissing `Contract` When Processing Deposit LogFixed

Files: - `beacon-chain/execution/log_processing.go` - `contracts/deposit/logs.go` Commit: - [`3226f8330911cb8df77e775f0155b335ba771bd8`](https://github.com/fastexlabs/bahamut-consensus/tree/3226f8330911cb8df77e775f0155b335ba771bd8) The `ProcessDepositLog()` function in is responsible for handling the received log from the `eth1` chain and generating the deposit data object. However, there is currently no logic implemented to handle the `Contract` attribute or include it in the deposit data object. **beacon-chain/execution/log_processing.go** ```go=133 depositData := &ethpb.Deposit_Data{ Amount: bytesutil.FromBytes8(amount), PublicKey: pubkey, Signature: signature, WithdrawalCredentials: withdrawalCredentials, } ``` **contracts/deposit/logs.go** ```go=11 func UnpackDepositLogData(data []byte) (pubkey, withdrawalCredentials, amount, signature, index []byte, err error) { reader := bytes.NewReader([]byte(DepositContractABI)) contractAbi, err := abi.JSON(reader) if err != nil { return nil, nil, nil, nil, nil, errors.Wrap(err, "unable to generate contract abi") } unpackedLogs, err := contractAbi.Unpack("DepositEvent", data) if err != nil { return nil, nil, nil, nil, nil, errors.Wrap(err, "unable to unpack logs") } return unpackedLogs[0].([]byte), unpackedLogs[1].([]byte), unpackedLogs[2].([]byte), unpackedLogs[3].([]byte), unpackedLogs[4].([]byte), nil } ```

MediumMissing Memory Gas Usage in Activity When AddingFixed

Files: - `core/vm/evm.go` Commit: - [`af75d5f6c6ab5a33f6a1ac86c5c443e7be943cf1`](https://github.com/fastexlabs/bahamut-execution/tree/af75d5f6c6ab5a33f6a1ac86c5c443e7be943cf1) In the execution layer, the invocation of the function `CallCode()` changes the address's activity based on gas usage, one of which is the memory gas usage: **CallCode()** ```go=341 memGas, err := evm.memoryGas(input) if err != nil { return nil, gas, err } if caller.Address() != evm.Origin { memGas = 0 } evm.StateDB.AddActivity(addrCopy, initialGas-contract.Gas-contract.OthersGas+memGas) evm.StateDB.AddActivities(&types.Activity{ Address: addrCopy, Activity: evm.StateDB.GetActivity(addrCopy), DeltaActivity: initialGas - contract.Gas - contract.OthersGas, }) ``` However, an inconsistency occurs when adding the activity to the `evm.StateDB`. In line 349, the added activity is calculated as `initialGas-contract.Gas-contract.OthersGas+memGas`, while the `memGas` is missing in line 353 in the call of `evm.StateDB.AddActivities()`, which only accepts `initialGas - contract.Gas - contract.OthersGas` as an input.

MediumPotentially Override The Current Owner Of ContracFixed

Files: - `beacon-chain/core/blocks/deposit.go` Commit: - [`3226f8330911cb8df77e775f0155b335ba771bd8`](https://github.com/fastexlabs/bahamut-consensus/tree/3226f8330911cb8df77e775f0155b335ba771bd8) If a contract is associated with a previous validator, and a new validator registers the same contract, then the previous owner's contract will be set to a zero address. This creates an issue that any validator can occupy the contract with high activities, thereby gaining more power and increasing their chances of being selected as the block proposer. ```go=192 if contractExist { // Set zero-contract to the old owner of the contract // if the contract is already presented in beacon state. owner, err := beaconState.ValidatorAtIndex(contractOwner) if err != nil { return nil, newValidator, err } newVal := ethpb.CopyValidator(owner) newVal.Contract = params.BeaconConfig().ZeroContract[:] if err := beaconState.UpdateValidatorAtIndex(contractOwner, newVal); err != nil { return nil, newValidator, err } } ``` Moreover, in the new design, a validator owns at most one contract, and the current owner of a contract cannot update the contract because once the validator has been set, then it will not be able to enter the branch to update the contract. ```go=173 contractOwner, contractExist := beaconState.ValidatorIndexByContract(bytesutil.ToBytes20(contract)) index, ok := beaconState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey)) if !ok { ... ``` The auditing team would like to confirm with the Fasttoken team if the existing logic is in accordance with the design.

LowCode Simplification in Function `RandomBytes()`Fixed

Files: - `beacon-chain/core/helpers/validators.go` Commit: - [`3226f8330911cb8df77e775f0155b335ba771bd8`](https://github.com/fastexlabs/bahamut-consensus/tree/3226f8330911cb8df77e775f0155b335ba771bd8) The function `RandomBytes()` is intended to generate a pseudo-random number between 0 and `totalEffectivePower`-1. The `randomNumber` generated in line 424 with index 0 will be overwritten by the for loop in line 430, which can be merged into the for loop and start the index with 0. ```go=419 func RandomBytes(seed [32]byte, totalEffectivePower uint64) uint64 { maxRandomBytes := uint64(1<<64 - 1) hashFunc := hash.CustomSHA256Hasher() hash := hashFunc(append(seed[:], bytesutil.Bytes8(0)...)) randomBytes := hash[:8] randomNumber := bytesutil.FromBytes8(randomBytes) if totalEffectivePower == 0 { return 0 } for i := uint64(1); randomNumber > (maxRandomBytes/totalEffectivePower)*totalEffectivePower; i++ { hash = hashFunc(append(seed[:], bytesutil.Bytes8(i)...)) randomBytes = hash[:8] randomNumber = bytesutil.FromBytes8(randomBytes) } return randomNumber % totalEffectivePower } ```

LowCurrent Version Does Not Contain Patch for MEV-BoFixed

Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) MEV bots serves as a tool to frontrun a pending transaction to extract the value. To prevent being frontrun by themselves, MEV bots could use MEV-Boost/Relays as trusted mediator between block producers and block builders, which is an implementation of proposer-builder separation (PBS) built by Flashbots for the Ethereum Proof of Stake. Validators could run MEV-Boost to maximize their staking reward by selling blockspace to an open market of builders. Block proposers could bid on transactions, then builders create blocks that include the most valuable transactions, and validators sign the transactions. Normally, blocker proposers can not see the transactions in the block until they signed the block header, which makes it difficult to frontrun the transactions in the block. To identify transactions for exploit, the validator sent a signed, invalid block to MEV-Boost/Relay, which replied with the transactions that should have been included in that block. With the transactions in the block revealed, the validator could observe the transactions and manipulate the transactions. This critical vulnerability was exploited on April 3rd, 2023, which leads to ~20M asset loss of multiple sandwich bots. The patch has been released on the [`MEV-Boost Relay`](https://github.com/flashbots/mev-boost-relay/pull/330), but it requires the corresponding modification of the client, which has been released in the Prysm v4.0.2, but it is not included in the Prysm v3.2.2. Since the Bahamut is built on the Prysm v3.2.2 and the validators may also run the MEV-Boost/Relay, it is recommended to upgrade to the latest version to ensure the fix work properly. **Reference:** - [`Post mortem`](https://collective.flashbots.net/t/post-mortem-april-3rd-2023-mev-boost-relay-incident-and-related-timing-issue/1540) - [`MEV Bot Incident Analysis`](https://www.certik.com/resources/blog/30h7lDtiv9pJiwloeTPXgW-mev-bot-incident-analysis)

LowDiscussion on Contract Registration with ValidatoFixed

Files: - `beacon-chain/core/blocks/deposit.go` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) The Fasttoken consensus algorithm utilizes the gas usage of contracts that are associated with the validators as activity scores to compute the power of the validators. According to the current codebase, the registration of contracts with validators occurs in the contract `deposit_contract` in the execution layer where the validators are able to deposit the stake as well as the contract address for the registration process. In the current implementation, the validator only needs to pass an address for the registration. The auditing team would like to confirm with the Fasttoken team if the following points are taken into account: 1. The passed address has not been validated that is associated with an existing contract, which means it could be an EOA or a placeholder for future contract deployment. Since some classes of addresses (i.e., vanity addresses) are popular in the blockchain, the validators could register many such addresses. If some contract is deployed in the future with these addresses, the validator will own the activity generated by these contracts. 2. If the passed address comes from an existing contract, it could belong to other deployers and not necessarily be owned by this validator. Considering the blockchain is a dark forest, the contract address registration could also be front run by other validators.

LowDiscussion on Inconsistency Between Deposit ContrFixed

Files: - `contracts/deposit/contract.go` - `contracts/deposit/deposit_contract.sol` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) The contract `deposit_contract` serves as the entry point for the validator registration on the execution layer. In the current codebase, the `deposit_contract` has not been modified to align with Fasttoken's new design. Its binding `contract.go` seems to be updated as the ABI is different from the current `deposit_contract`, but the deposit event is not updated, as it does not contain `DeployedAddress` and `DeploymentNonce`: **contracts/deposit/contract.go** ```go=362 type DepositContractDepositEvent struct { Pubkey []byte WithdrawalCredentials []byte Amount []byte Signature []byte Index []byte Raw types.Log // Blockchain specific contextual infos } ``` Additionally, the function `Deposit()` is supposed to be only used for testing as it includes hardcoded address and nonce: **contracts/deposit/contract.go** ```go=269 func (_DepositContract *DepositContractTransactor) Deposit(opts *bind.TransactOpts, pubkey []byte, withdrawal_credentials []byte, signature []byte, deposit_data_root [32]byte) (*types.Transaction, error) { return _DepositContract.contract.Transact( opts, "deposit", pubkey, withdrawal_credentials, signature, deposit_data_root, common.HexToAddress("0x1111111111111111111111111111111111111111"), big.NewInt(1)) } ```

LowDiscussion on The Calculation of `BaseProposerRewFixed

Files: - `beacon-chain/core/fastex-phase1/reward.go` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) The fasttoken introduces a novel proposer base reward calculation based on the validator's power via the activity score associated with the validator's registered contracts. The function `BaseProposerReward()` is used to compute the base proposer reward as follows: $$BPR = \frac{(A+T)*bf}{W * n * gwei},$$ where $A = \sum_{i=1}^{n} ea_i$ is the total effective activities of the $n$ validators and $T$ is the transaction constant gas in the window of 1575 epochs. The $bf$ is the base fee of the block and $W$ is the epoch window size 1575, $n$ is the number of validators and $gwei$ is the constant $10^9$. Therefore, the base proposer reward in each epoch is $BPR * s$, where $s$ (= 32) is the number of slots in an epoch. On the other hand, the total validator base reward per epoch is given by: $$BR_{total} = \frac{B*f}{\sqrt{B}} = f*\sqrt{B},$$ where $B$ is the total active balance, and $f$ is the constant 156. Assume that the current number of validators is the target number 4096, and each of them has an effective balance of $8192 * 10^9$. Then the base reward is $$156 * \sqrt{4096 * 8192 * 10^9} = 0.9 * 10^9 gwei$$. According to the design, $7/8$ of the reward will be distributed to the validators for attestation rewards and participating sync committees. In the Ethereum PoS, the remaining $1/8$ of the reward (i.e., $0.1125 * 10^9$) is granted to the block proposers for proposing blocks. The fasttoken attempts to use the base proposer reward in each epoch (i.e., $32*BPR$) to replace the $1/8$ of the total validator base reward per epoch as the reward to the block proposers. In this case, assume that each block has a half load (15M gas consumed) and the base fee is $100 * gwei$, then the base proposer reward in each epoch is $$32 * \frac{(A+T)*bf}{W * n * gwei} = 32 * \frac{(32 *15M)* 100 * gwei}{4096 * gwei} = 0.395 * 10^9 gwei,$$ which is larger than the $1/8$ of the total validator base reward per epoch ( $0.1125 * 10^9$ ). In this case, the block proposers will be incentivized for their duties. However, the auditing team has the following points that would like to check with the fasttoken team: 1. The base proposer reward $BPR$ depends on the $A+T$. If the block activity is low for a long time, then the $BPR$ could be very small (because it is linear with $A+T$.) compared to the $1/8$ stable reward; 2. If the number of validators increases, then the $BR_{total}$ increases but $BPR$ decreases. Take the max number of validators, 20480 as an example, the $BPR$ will be $1/5$ of the previous one, which is $0.079 * 10^9$ but the $1/8$ of the total validator base reward is $0.25 * 10^9$. In both cases, the block proposer reward based on the new design could be less than the $1/8$ of the total validator base reward in the old design. As a result, the block proposers could possibly be disincentivized to participate in the consensus because the reward in the new design is not predictable and prone to change.

Show 10 more
LowDiscussion on The Use of The Sigmoid Function inFixed

Files: - `beacon-chain/core/helpers/validators.go` - `beacon-chain/core/altair/sync_committee.go` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) According to the current codebase, the block producer and sync committee member selection inherits the algorithm from the RANDAO randomness generation in the Ethereum Proof of Stake. Before the version `FastexPhase1`, the implementation contains some modifications that use the sigmoid function and validator's power defined by the validator's activity score. In Ethereum Proof of Stake, the selection is performed through a shuffle to make the list of active validators randomly, then for each validator, a random number `rand` is generated between 0 and `MaxRand` to check if the inequality $$\frac{s_i}{s} \geq \frac{rand}{MaxRand}$$ holds, where $s_i$ is the effective balance of the validator $i$ and $s$ is the max effective balance. In the Fasttoken, the same approach is adopted with the following changes in the inequality $$(2\cdot \frac{1}{1+e^{-1.5 \cdot \frac{P_i}{P}}} - 1) \cdot \frac{s_i}{s} \geq \frac{rand}{MaxRand} \cdot 0.62$$ The sigmoid function on the left is used to adjust the effective balance of the validator, where the $P_i$ is the power of the validator $i$ and $P$ is the max power of all the validators.

LowDiscussion on Two Implementations of Block ProposFixed

Files: - `beacon-chain/core/helpers/beacon_committee.go` - `beacon-chain/core/helpers/validators.go` - `beacon-chain/core/altair/sync_committee.go` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) In both functions `BeaconProposerIndex()` and `precomputeProposerIndices()`, an `if-else` logic is coded to select the function for computing the index of the proposer based on the `Version`. The function `BeaconProposerIndex()` in **beacon-chain/core/helpers/validators.go**: ```go=301 if state.Version() < version.FastexPhase1 { return ComputeProposerIndex(state, indices, seedWithSlotHash) } return ComputeProposerIndexFastexPhase1(state, indices, seedWithSlotHash) ``` The function `precomputeProposerIndices()` in **beacon-chain/core/helpers/beacon_committee.go**: ```go=453 if state.Version() < version.FastexPhase1 { index, err = ComputeProposerIndex(state, activeIndices, seedWithSlotHash) if err != nil { return nil, err } } else { index, err = ComputeProposerIndexFastexPhase1(state, activeIndices, seedWithSlotHash) if err != nil { return nil, err } } proposerIndices[i] = index ``` Based on our understanding, when the version is before the `FastexPhase1`, the consensus client should still be in `PoS` mode, and the logic for calculating the index of the proposer should be the same as in Ethereum. However, in the implementation, the `ComputeProposerIndex()` function takes not only the **effective balance** of the validator but also the **effective activity**, which should be calculated in the `PoSA` mode. The function `ComputeProposerIndex()` in **beacon-chain/core/helpers/validators.go**: ```go=384 // ComputeProposerIndex returns the index sampled by effective balance, which is used to calculate the proposer. func ComputeProposerIndex(bState state.ReadOnlyBeaconState, activeIndices []primitives.ValidatorIndex, seed [32]byte) (primitives.ValidatorIndex, error) { length := uint64(len(activeIndices)) if length == 0 { return 0, errors.New("empty active indices list") } maxRandomByte := new(big.Float).SetUint64(1<<16 - 1) hashFunc := hash.CustomSHA256Hasher() txGasPerPeriod := bState.TransactionsGasPerPeriod() var nonStakersGasPerPeriod uint64 // Ignore nonStakersGasPerPeriod in post-FastexPhase1 fork. if bState.Version() < version.FastexPhase1 { nonStakersGasPerPeriod = bState.NonStakersGasPerPeriod() } ... ``` In addition, a similar scenario occurs in the sync committee members selection: **beacon-chain/core/altair/sync_committee.go** ```go=61 func NextSyncCommittee(ctx context.Context, s state.BeaconState) (*ethpb.SyncCommittee, error) { var indices []primitives.ValidatorIndex var err error if s.Version() < version.FastexPhase1 { indices, err = NextSyncCommitteeIndices(ctx, s) } else { indices, err = NextSyncCommitteeIndicesFastexPhase1(ctx, s) } ``` Before the `FastexPhase1`, the `NextSyncCommittee()` calls the `NextSyncCommitteeIndices()` that needs the activity score to compute the validator power: **beacon-chain/core/altair/sync_committee.go** ```go=88 func NextSyncCommitteeIndices(ctx context.Context, s state.BeaconState) ([]primitives.ValidatorIndex, error) { epoch := coreTime.NextEpoch(s) indices, err := helpers.ActiveValidatorIndices(ctx, s, epoch) if err != nil { return nil, err } seed, err := helpers.Seed(s, epoch, params.BeaconConfig().DomainSyncCommittee) if err != nil { return nil, err } count := uint64(len(indices)) cfg := params.BeaconConfig() syncCommitteeSize := cfg.SyncCommitteeSize cIndices := make([]primitives.ValidatorIndex, 0, syncCommitteeSize) hashFunc := hash.CustomSHA256Hasher() txGasPerPeriod := s.TransactionsGasPerPeriod() var nonStakersGasPerPeriod uint64 // Ignore nonStakersGasPerPeriod in post-FastexPhase1 forks. if s.Version() < version.FastexPhase1 { nonStakersGasPerPeriod = s.NonStakersGasPerPeriod() } ... ``` In the post `FastexPhase1`, it invokes the `NextSyncCommitteeIndicesFastexPhase1()` that only utilizes the **effective balance**: ```go=174 func NextSyncCommitteeIndicesFastexPhase1(ctx context.Context, s state.BeaconState) ([]primitives.ValidatorIndex, error) { epoch := coreTime.NextEpoch(s) indices, err := helpers.ActiveValidatorIndices(ctx, s, epoch) if err != nil { return nil, err } seed, err := helpers.Seed(s, epoch, params.BeaconConfig().DomainSyncCommittee) if err != nil { return nil, err } count := uint64(len(indices)) cfg := params.BeaconConfig() syncCommitteeSize := cfg.SyncCommitteeSize cIndices := make([]primitives.ValidatorIndex, 0, syncCommitteeSize) hashFunc := hash.CustomSHA256Hasher() for i := primitives.ValidatorIndex(0); uint64(len(cIndices)) < params.BeaconConfig().SyncCommitteeSize; i++ { if ctx.Err() != nil { return nil, ctx.Err() } ... ```

LowDiscussion on Value of `SigmoidLimit`Fixed

Files: - `beacon-chain/core/altair/sync_committee.go` - `beacon-chain/core/helpers/validators.go` - `config/params/testnet_fastex_chain_config.go` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) The block producer and sync committee member selection inherits the algorithm from the RANDAO randomness generation in the Ethereum Proof of Stake. In Ethereum Proof of Stake, the selection is performed through a shuffle to make the list of active validators randomly, then for each validator, a random number `rand` is generated between 0 and `MaxRand` to check if the inequality $$\frac{s_i}{s} \geq \frac{rand}{MaxRand}$$ holds, where $s_i$ is the effective balance of the validator $i$ and $s$ is the max effective balance. In the Fasttoken, the same approach is adopted with the following modification in the inequality $$(2\cdot \frac{1}{1+e^{-1.5 \cdot \frac{P_i}{P}}} - 1) \cdot \frac{s_i}{s} \geq \frac{rand}{MaxRand} \cdot 0.62$$ The sigmoid function on the left is used to adjust the effective balance of the validator, where the $P_i$ is the voting power of the validator $i$ and $P$ is the max voting power of all the validators. The current value of `SigmoidLimit` is **0.62** on the right, but the maximum value of the sigmoid function $2\cdot \frac{1}{1+e^{-1.5 \cdot \frac{P_i}{P}}} - 1$ is around **0.635** when the $P_i$ equals to $P$. In this case, if the voting power of validator $i$, $P_i = P * 97\%$, then the value of the sigmoid is **0.62**. That means a validator only needs $97\%$ (not $100\%$) of the maximum voting power $P$ to obtain the same formula as the Ethereum Proof of Stake.

LowInconsistency Between Implementation and WhitepapFixed

Files: - `beacon-chain/core/altair/sync_committee.go` - `beacon-chain/core/helpers/validators.go` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) According to the Fasttoken consensus whitepaper, the voting power of validator $i$ is defined as: $$ P^e_i = T_e \cdot \frac{s_i}{S} + A^e_{V_i},$$ where $T_e$ is the sum of all transaction constant gas usage components, that is, $T_e = 21000 ∗ N$ where N is the number of all transactions which have been executed during the epoch $e$. However, in line 389 of the implementation: **beacon-chain/core/helpers/validators.go**, **beacon-chain/core/altair/sync_committee.go** ```go=343 func ComputeProposerIndex(bState state.ReadOnlyBeaconState, activeIndices []primitives.ValidatorIndex, seed [32]byte) (primitives.ValidatorIndex, error) { length := uint64(len(activeIndices)) if length == 0 { return 0, errors.New("empty active indices list") } maxRandomByte := new(big.Float).SetUint64(1<<16 - 1) hashFunc := hash.CustomSHA256Hasher() txGasPerPeriod := bState.TransactionsGasPerPeriod() var nonStakersGasPerPeriod uint64 // Ignore nonStakersGasPerPeriod in post-FastexPhase1 fork. if bState.Version() < version.FastexPhase1 { nonStakersGasPerPeriod = bState.NonStakersGasPerPeriod() } totalBalance := TotalBalance(bState, activeIndices) maxPower, err := MaxPower(bState, activeIndices, totalBalance, txGasPerPeriod, nonStakersGasPerPeriod) maxPowerFloat := new(big.Float).SetInt(maxPower) if err != nil { return 0, err } for i := uint64(0); ; i++ { candidateIndex, err := ComputeShuffledIndex(primitives.ValidatorIndex(i%length), length, seed, true /* shuffle */) if err != nil { return 0, err } candidateIndex = activeIndices[candidateIndex] if uint64(candidateIndex) >= uint64(bState.NumValidators()) { return 0, errors.New("active index out of range") } b := append(seed[:], bytesutil.Bytes8(i/16)...) hash := hashFunc(b) bytes2 := append([]byte{}, hash[i%16], hash[16+i%16]) randomBytes := new(big.Float).SetUint64(uint64(bytesutil.FromBytes2(bytes2))) v, err := bState.ValidatorAtIndexReadOnly(candidateIndex) if err != nil { return 0, err } totalBalanceBig := new(big.Int).SetUint64(totalBalance / params.BeaconConfig().EffectiveBalanceIncrement) effectiveBalanceBig := new(big.Int).SetUint64(v.EffectiveBalance() / params.BeaconConfig().EffectiveBalanceIncrement) effectiveActivityBig := new(big.Int).SetUint64(v.EffectiveActivity()) txGasBig := new(big.Int).SetUint64(txGasPerPeriod) nonStakersGasBig := new(big.Int).SetUint64(nonStakersGasPerPeriod) var power *big.Int power = new(big.Int).Add(txGasBig, nonStakersGasBig) power = new(big.Int).Mul(power, effectiveBalanceBig) power = new(big.Int).Div(power, totalBalanceBig) power = new(big.Int).Add(power, effectiveActivityBig) ... ``` $$T_e = T + B$$, where $T$ is the aggregation of the constant gas usage argument equal to 21000, and $B$ is the gas usage of smart contracts not associated with any validator, both of which are calculated in the sliding window of 1575 epochs, not in each epoch. The $B$ part, `nonStakersGasBig` is zero only in the post-FastexPhase1 fork (shown in lines 354-356), which matches the formula in the whitepaper.

LowMainnet Could Possibly Be MisconfiguredFixed

Files: - `config/params/mainnet_config.go` - `config/params/minimal_config.go` Commit: - [`3b8da2895d7067405b54c0829eee7e044a0f978e`](https://github.com/fastexlabs/bahamut-consensus/tree/3b8da2895d7067405b54c0829eee7e044a0f978e) The parameters `MaxEffectiveBalance` and `EjectionBalance` were properly set in the configuration file `testnet_fastex_chain_config.go`. However, they were not updated in the configuration file `mannet_config.go` to accommodate the new features and functionality. A misconfiguration could cause errors or bugs that could negatively impact the functionality of the project. ```go=92 MaxEffectiveBalance: 32 * 1e9, EjectionBalance: 16 * 1e9, ``` In addition, the below parameters in the configuration file `minimal_config.go` are not properly set. ```go=20 MinGenesisTime: 1606824000, // Dec 1, 2020, 12pm UTC. ``` ```go=25 minimalConfig.MinDepositAmount = 1e9 minimalConfig.MaxEffectiveBalance = 32e9 minimalConfig.EjectionBalance = 16e9 minimalConfig.EffectiveBalanceIncrement = 1e9 ```

LowMissing Check of `proposerRewardDenominator` CoulFixed

Files: - `beacon-chain/core/fastex-phase1/attestation.go` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) The function `RewardProposer()` is intended to calculate the reward for the block proposer, which accepts the parameters, `proposerRewardNumerator` and `proposerRewardDenominator` from the return values of function `EpochParticipation()`. If the passed `indices` is empty in the loop of the `EpochParticipation()`, both `proposerRewardNumerator` and `proposerRewardDenominator` will be 0 and the returned error is nil. ```go=129 func EpochParticipation( beaconState state.BeaconState, indices []uint64, epochParticipation []byte, participatedFlags map[uint8]bool, totalBalance uint64, ) (uint64, uint64, []byte, error) { cfg := params.BeaconConfig() sourceFlagIndex := cfg.TimelySourceFlagIndex targetFlagIndex := cfg.TimelyTargetFlagIndex headFlagIndex := cfg.TimelyHeadFlagIndex proposerRewardNumerator := uint64(0) proposerRewardDenominator := uint64(0) for _, index := range indices { ... } return proposerRewardNumerator, proposerRewardDenominator, epochParticipation, nil } ``` In this case, error handling in lines 99-101 and 109-110 of the function `SetParticipationAndRewardProposer()` will be bypassed. ```go=84 func SetParticipationAndRewardProposer( ctx context.Context, beaconState state.BeaconState, targetEpoch primitives.Epoch, indices []uint64, participatedFlags map[uint8]bool, totalBalance uint64, ) (state.BeaconState, error) { var proposerRewardNumerator uint64 var proposerRewardDenominator uint64 currentEpoch := time.CurrentEpoch(beaconState) var stateErr error if targetEpoch == currentEpoch { stateErr = beaconState.ModifyCurrentParticipationBits(func(val []byte) ([]byte, error) { propRewardNum, propRewardDenom, epochParticipation, err := EpochParticipation(beaconState, indices, val, participatedFlags, totalBalance) if err != nil { return nil, err } proposerRewardNumerator = propRewardNum proposerRewardDenominator = propRewardDenom return epochParticipation, nil }) } else { stateErr = beaconState.ModifyPreviousParticipationBits(func(val []byte) ([]byte, error) { propRewardNum, propRewardDenom, epochParticipation, err := EpochParticipation(beaconState, indices, val, participatedFlags, totalBalance) if err != nil { return nil, err } proposerRewardNumerator = propRewardNum proposerRewardDenominator = propRewardDenom return epochParticipation, nil }) } if stateErr != nil { return nil, stateErr } if err := RewardProposer(ctx, beaconState, proposerRewardNumerator, proposerRewardDenominator); err != nil { return nil, err } return beaconState, nil } ``` In addition, the error handling in lines 117-119 will also be bypassed, allowing the function `RewardProposer()` with `proposerRewardDenominator` as 0 to be invoked in the function `SetParticipationAndRewardProposer()`. Therefore, the parameter `proposerRewardDenominator` passed in the `RewardProposer()` is 0.

LowMissing nil Check of Variable `Activity`Fixed

Files: - `beacon-chain/core/blocks/activities.go` Commit: - [`3b8da2895d7067405b54c0829eee7e044a0f978e`](https://github.com/fastexlabs/bahamut-consensus/tree/3b8da2895d7067405b54c0829eee7e044a0f978e) The function `ProcessBlockActivities()` is intended to process the activities in a block, which invokes the function `ProcessActivity()` with each `activity` from the block. Since the activities are fetched from the execution layer, they could possibly contain some nil value. If the `activity` is nil, then fetching `ContractAddress` from the `activity` in line 42 of the function `ProcessActivity()` will lead to a runtime panic. ```go=34 func ProcessActivity( ctx context.Context, beaconState state.BeaconState, activity *ethpb.ActivityChange, ) (state.BeaconState, error) { ctx, span := trace.StartSpan(ctx, "core.ProcessActivtiyNoVerifySignature") defer span.End() contract := bytesutil.ToBytes20(activity.ContractAddress) idx, ok := beaconState.ValidatorIndexByContractAddress(contract) if !ok { nonStakersGas := beaconState.NonStakersGasPerEpoch() if err := beaconState.SetNonStakersGasPerEpoch(nonStakersGas + activity.DeltaActivity); err != nil { return nil, err } return beaconState, nil } ... ```

LowPossibly Incorrect Calculation of Base Proposer RFixed

Files: - `beacon-chain/core/altair/reward.go` Commit: - [`3226f8330911cb8df77e775f0155b335ba771bd8`](https://github.com/fastexlabs/bahamut-consensus/tree/3226f8330911cb8df77e775f0155b335ba771bd8) - [`8198a02d28dee2b7485610279bcf24e4f0a2bf54`](https://github.com/fastexlabs/bahamut-consensus/commit/8198a02d28dee2b7485610279bcf24e4f0a2bf54) The fasttoken introduces a novel proposer base reward calculation based on the validator's power via the activity score associated with the validator's registered contracts. The function `BaseProposerReward()` is used to compute the base proposer reward defined in the whitepaper as follows: $$BPR = \frac{(A+T)*bf}{W * n * gwei},$$ where $A = \sum_{i=1}^{n} ea_i$ is the total effective activities of the $n$ validators and $T$ is the transaction constant gas in the window of 1575 epochs. The $bf$ is the base fee of the block and $W$ is the epoch window size 1575, $n$ is the number of validators and $gwei$ is the constant $10^9$. Therefore, the base proposer reward in each epoch is $BPR * s$, where $s$ (= 32) is the number of slots in an epoch. On the other hand, the total validator base reward per epoch is given by: $$BR_{total} = \frac{B*f}{\sqrt{B}} = f*\sqrt{B},$$ where $B$ is the total active balance, and $f$ is the constant 156 (according to the `config/params/mainnet_config.go` in the commit [`8198a02d28dee2b7485610279bcf24e4f0a2bf54`](https://github.com/fastexlabs/bahamut-consensus/commit/8198a02d28dee2b7485610279bcf24e4f0a2bf54)). Assume that the current number of validators is the target number 4096, and each of them has an effective balance of $8192 * 10^9$. Then the base reward is $$156 * \sqrt{4096 * 8192 * 10^9} = 0.9 * 10^9 gwei$$. According to the design, this reward will be distributed to the validators for attestation rewards and participating sync committees. In the Ethereum PoS, $1/7$ of the reward (i.e., $0.13 * 10^9$) is granted to the block proposers for proposing blocks. The fasttoken attempts to use the base proposer reward in each epoch (i.e., $32*BPR$) to replace the $1/7$ of the total validator base reward per epoch as the reward to the block proposers. In this case, assume that each block has a half load (15M gas consumed) and the base fee is $100 * gwei$, then the base proposer reward in each epoch is $$32 * \frac{(A+T)*bf}{W * n * gwei} = 32 * \frac{(32 *15M)* 100 * gwei}{4096 * gwei} = 0.395 * 10^9 gwei,$$ Therefore, the calculated reward is close to the value in the new design. However, in the implementation of the function `BaseProposerReward()`: ```go=65 func BaseProposerReward(s state.ReadOnlyBeaconState, totalPower, totalEffectivePower uint64) (uint64, error) { activity, err := helpers.TotalEffectiveActivity(s) if err != nil { return 0, errors.Wrap(err, "could not calculate total effective activity") } sharedActivity := s.SharedActivity() if sharedActivity == nil { return 0, errors.New("nil shared activity in state") } period := uint64(params.BeaconConfig().EpochsPerActivityPeriod) slotsPerEpoch := uint64(params.BeaconConfig().SlotsPerEpoch) denominator := period * period * slotsPerEpoch * slotsPerEpoch transactionsGas := sharedActivity.TransactionsGasPerPeriod baseFee := sharedActivity.BaseFeePerPeriod reward := baseFee * (activity + transactionsGas) / denominator if totalPower == 0 { return reward, nil } return reward * totalEffectivePower / totalPower, nil } ``` The reward does not align with the formula in the whitepaper as the reward is not divided by the number of active validators. Actually, the formula derived from the above code on average is as follows: $$ (A+T)*bf = 15 M * 100 gwei = 1.5 * 10^9 gwei$$.

LowPotential Overflow And UnderflowFixed

Files: - `core/state/state_object.go` - `core/state_transition.go` Commit: - [`af75d5f6c6ab5a33f6a1ac86c5c443e7be943cf1`](https://github.com/fastexlabs/bahamut-execution/tree/af75d5f6c6ab5a33f6a1ac86c5c443e7be943cf1) There are no overflow and underflow protections in the following functions, making it possible for overflow/underflow to occur and could possibly lead to inaccurate calculations. **core/state/state_object.go** ```go=400 func (s *stateObject) AddActivity(amount uint64) { if amount == 0 { if s.empty() { s.touch() } return } s.SetActivity(s.Activity() + amount) } // SubActivity remove some amount of activity to s's activity func (s *stateObject) SubActivity(amount uint64) { if amount == 0 { if s.empty() { s.touch() } return } s.SetActivity(s.Activity() - amount) } ``` **core/state_transition.go** ```go=396 func (st *StateTransition) refundActivity(refund uint64) { if refund == 0 { return } totalRefund := refund totalActivityByContract := make(map[common.Address]uint64) totalRefundsByContracts := make(map[common.Address]uint64) currentActivities := st.state.GetCurrentActivities() for _, act := range currentActivities { totalActivityByContract[act.Address] += act.DeltaActivity } var proportion []float64 for _, act := range currentActivities { proportion = append(proportion, float64(act.DeltaActivity)/float64(totalActivityByContract[act.Address])) } for i, act := range currentActivities { if i == len(currentActivities)-1 { totalRefundsByContracts[act.Address] += refund act.DeltaActivity -= refund act.Activity -= totalRefundsByContracts[act.Address] st.state.SubActivity(act.Address, refund) log.Debug("Refunded contract activity", "activity", refund, "addr", act.Address) } else { totalRefundByContract := float64(totalRefund*st.state.GetRefundsByContract(act.Address)) / float64(st.state.GetRefund()) refundAct := uint64(totalRefundByContract * proportion[i]) totalRefundsByContracts[act.Address] += refundAct act.DeltaActivity -= refundAct act.Activity -= totalRefundsByContracts[act.Address] st.state.SubActivity(act.Address, refundAct) refund -= refundAct log.Debug("Refunded contract activity", "activity", totalRefundByContract, "addr", act.Address) } } } ```

LowThe Output Block Does Not Contain `ActivityChangeFixed

Files: - `beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go` Commit: - [`3226f8330911cb8df77e775f0155b335ba771bd8`](https://github.com/fastexlabs/bahamut-consensus/tree/3226f8330911cb8df77e775f0155b335ba771bd8) The function `unblindBuilderBlock()` retrieves the full payload block using the input blind block. However, the output block does not contain the fields `ActivityChanges`, `TransactionsCount`, `BaseFee`, And `ExecutionHeight`. ```go=306 Body: &ethpb.BeaconBlockBodyBellatrix{ RandaoReveal: psb.Block.Body.RandaoReveal, Eth1Data: psb.Block.Body.Eth1Data, Graffiti: psb.Block.Body.Graffiti, ProposerSlashings: psb.Block.Body.ProposerSlashings, AttesterSlashings: psb.Block.Body.AttesterSlashings, Attestations: psb.Block.Body.Attestations, Deposits: psb.Block.Body.Deposits, VoluntaryExits: psb.Block.Body.VoluntaryExits, SyncAggregate: agg, ExecutionPayload: pbPayload, }, ```

Show 5 more
LowTypo in Error MessagesFixed

Files: - `beacon-chain/core/blocks/activities.go` - `beacon-chain/core/fastex-phase1/attestation.go` - `beacon-chain/p2p/pubsub_filter.go` - `beacon-chain/rpc/apimiddleware/custom_hooks.go` - `beacon-chain/node/node.go` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) There are some typos in the error messages in the current codebase: **beacon-chain/core/blocks/activities.go** - in line 26, `activties` should be `activities`; - in line 39, `core.ProcessActivtiyNoVerifySignature` should be `core.ProcessActivityNoVerifySignature`. **beacon-chain/core/fastex-phase1/attestation.go** - in line 55, `altair.ProcessAttestationNoVerifySignature` should be `fastexphase1.ProcessAttestationNoVerifySignature`; **beacon-chain/p2p/pubsub_filter.go** - in line 57, `Could not determine Bellatrix fork digest` should be `Could not determine fastexPhase1 fork digest`. **beacon-chain/rpc/apimiddleware/custom_hooks.go** - in line 849, `4 unsupported block version '%s'` should be `unsupported block version '%s'`. **beacon-chain/node/node.go** - in line 229, `Registering Determinstic Genesis Service` should be `Registering Deterministic Genesis Service`; - in line 239, `Registering Intial Sync Service` should be `Registering Initial Sync Service`.

LowTypo in Function Name `isEligibileForActivationQuFixed

Files: - `beacon-chain/core/helpers/validators.go` Commit: - [`3226f8330911cb8df77e775f0155b335ba771bd8`](https://github.com/fastexlabs/bahamut-consensus/tree/3226f8330911cb8df77e775f0155b335ba771bd8) In the line 498, 504 and 508 of file `beacon-chain/core/helpers/validators.go`, the function name `isEligibileForActivationQueue()` is supposed to be `isEligibleForActivationQueue()`.

LowTypo in The Codebase of Consensus LayerFixed

Files: - `beacon-chain/state/stateutil/activity_changes_root.go` - `beacon-chain/state/stateutil/field_root_shared_activity.go` - `beacon-chain/state/stateutil/shared_activity_root.go` Commit: - [`cffbd04e743737989e44cf0ebae70fd353c5a539`](https://github.com/fastexlabs/bahamut-consensus/commit/cffbd04e743737989e44cf0ebae70fd353c5a539) **beacon-chain/state/stateutil/activity_changes_root.go** - in lines 17 and 23, `merkleiztion` should be `merkleization`. **beacon-chain/state/stateutil/field_root_shared_activity.go** - in line 15, function name `SharedActivityRootWithHaher()` should be `SharedActivityRootWithHasher()`. **beacon-chain/state/stateutil/shared_activity_root.go** - in line 13, function name `SharedActivityRootWithHaher()` should be `SharedActivityRootWithHasher()`.

LowTypo in The Codebase of Execution LayerFixed

Files: - `core/vm/stateful_contracts.go` Commit: - [`716ea69939139eab9f45b4c68347eb67de492bea`](https://github.com/fastexlabs/bahamut-execution/tree/716ea69939139eab9f45b4c68347eb67de492bea) In the aforementioned places, `statefulPrecomiledContractWithSelectors` should be `statefulPrecompiledContractWithSelectors`.

LowTypo in Variable Names And Function NamesFixed

Files: - `config/params/config.go` - `config/params/mainnet_config.go` - `config/features/config.go` - `config/features/flags.go` - `beacon-chain/rpc/prysm/v1alpha1/validator/proposer_eth1data.go` - `validator/keymanager/remote-web3signer/v1/custom_mappers.go` - `validator/keymanager/remote-web3signer/v1/web3signer_types.go` - `beacon-chain/execution/activities_processing.go` - `beacon-chain/rpc/apimiddleware/structs.go` - `beacon-chain/node/node.go` Commit: - [`33b75d4e162179d360e60ac88bb4289293b530a6`](https://github.com/fastexlabs/bahamut-consensus/tree/33b75d4e162179d360e60ac88bb4289293b530a6) **Variable Names** According to the context, the variable `EpochsPerAcrivityUpdate` should be **`EpochsPerActivityUpdate`** in the following two places: 1. line 145 in the file `config/params/config.go`; 2. line 202 in the file `config/params/mainnet_config.go`. The variable `DisableStakinContractCheck` (`disableStakinContractCheck`) should be **`DisableStakingContractCheck`** (**`disableStakingContractCheck`**) in the following places: 1. line 73, 209, 210, and 211 in the file `config/features/config.go`; 2. line 85 and 176 in the file `config/features/flags.go`; 3. line 119 in the file `beacon-chain/rpc/prysm/v1alpha1/validator/proposer_eth1data.go`. The variable `ContractAddres` should be **`ContractAddress`** in the following places: 1. line 288 in the file `validator/keymanager/remote-web3signer/v1/custom_mappers.go`; 2. line 183 in the file `validator/keymanager/remote-web3signer/v1/web3signer_types.go`. The variable `activiyChanges` should be **`activityChanges`** in the following place: - line 62 in the file `beacon-chain/execution/activities_processing.go`. The variable `EffectivtActivity` should be **`EffectiveActivity`** in the following place: - line 1019 in the file `beacon-chain/rpc/apimiddleware/structs.go`. ---- **Function Names** The function name `registerDeterminsticGenesisService()` should be **registerDeterministicGenesisService()** in the following places: - line 230 and 920 in the file `beacon-chain/node/node.go`.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (174)

  • config/features/config.go
  • validator/keymanager/remote-web3signer/v1/custom_mappers.go
  • validator/rpc/wallet.go
  • beacon-chain/state/state-native/types/types.go
  • beacon-chain/core/transition/stateutils/contracts_index_map.go
  • eth/backend.go
  • beacon-chain/rpc/prysm/v1alpha1/validator/proposer_execution_payload.go
  • beacon-chain/rpc/prysm/v1alpha1/validator/proposer_eth1data.go
  • cmd/geth/snapshot.go
  • beacon-chain/state/stateutil/field_root_shared_activity.go
  • beacon-chain/state/stateutil/contracts_map_handler.go
  • beacon-chain/state/stategen/getter.go
  • validator/accounts/wallet_create.go
  • beacon-chain/sync/initial-sync/blocks_fetcher_utils.go
  • beacon-chain/blockchain/head.go
  • core/state/snapshot/account.go
  • beacon-chain/rpc/prysm/v1alpha1/validator/blocks.go
  • beacon-chain/rpc/eth/beacon/blocks.go
  • beacon-chain/blockchain/log.go
  • beacon-chain/state/stateutil/contracts_root.go
  • beacon-chain/state/state-native/hasher.go
  • beacon-chain/p2p/options.go
  • validator/accounts/cli_manager.go
  • beacon-chain/state/state-native/getters_state.go
  • beacon-chain/state/stateutil/field_root_contracts.go
  • proto/migration/v1alpha1_to_v1.go
  • beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go
  • beacon-chain/p2p/utils.go
  • api/client/builder/client.go
  • core/mkalloc.go
  • core/state/state_object.go
  • beacon-chain/db/kv/finalized_block_roots.go
  • validator/client/propose.go
  • config/params/testnet_fastex_chain_config.go
  • core/state/dump.go
  • beacon-chain/forkchoice/doubly-linked-tree/node.go
  • beacon-chain/core/blocks/signature.go
  • core/state_transition.go
  • beacon-chain/state/state-native/getters_validator.go
  • beacon-chain/blockchain/process_block.go
  • config/features/deprecated_flags.go
  • validator/rpc/health.go
  • beacon-chain/state/genesis/genesis.go
  • trie/trie.go
  • beacon-chain/core/transition/state.go
  • proto/prysm/v1alpha1/cloners.go
  • beacon-chain/blockchain/service.go
  • core/vm/gas_table.go
  • beacon-chain/p2p/pubsub.go
  • beacon-chain/core/helpers/contracts.go
  • eth/protocols/snap/sync.go
  • beacon-chain/state/stateutil/activity_changes_root.go
  • beacon-chain/core/altair/upgrade.go
  • beacon-chain/core/helpers/validators.go
  • beacon-chain/core/altair/sync_committee.go
  • core/vm/interface.go
  • core/state/statedb.go
  • core/vm/evm.go
  • core/vm/instructions.go
  • beacon-chain/sync/validate_beacon_blocks.go
  • beacon-chain/core/blocks/deposit.go
  • consensus-types/blocks/getters.go
  • config/params/config.go
  • beacon-chain/db/kv/blocks.go
  • beacon-chain/forkchoice/interfaces.go
  • beacon-chain/state/state-native/setters_eth1.go
  • beacon-chain/execution/options.go
  • beacon-chain/core/blocks/randao.go
  • params/version.go
  • cmd/geth/main.go
  • cmd/utils/flags.go
  • beacon-chain/core/altair/reward.go
  • beacon-chain/state/state-native/setters_validator.go
  • beacon-chain/rpc/prysm/v1alpha1/validator/proposer_altair.go
  • core/state/journal.go
  • beacon-chain/rpc/statefetcher/fetcher.go
  • beacon-chain/execution/check_transition_config.go
  • cmd/validator/wallet/create.go
  • config/params/minimal_config.go
  • beacon-chain/blockchain/execution_engine.go
  • params/bootnodes.go
  • beacon-chain/core/transition/transition_no_verify_sig.go
  • beacon-chain/state/state-native/readonly_contracts.go
  • beacon-chain/state/interfaces.go
  • beacon-chain/core/epoch/epoch_processing.go
  • core/vm/contract.go
  • beacon-chain/sync/pending_blocks_queue.go
  • beacon-chain/state/state-native/readonly_validator.go
  • encoding/ssz/detect/configfork.go
  • proto/engine/v1/json_marshal_unmarshal.go
  • core/genesis_alloc.go
  • beacon-chain/core/altair/transition.go
  • core/vm/operations_acl.go
  • validator/accounts/cli_options.go
  • proto/prysm/v1alpha1/json_marshal_unmarshal.go
  • beacon-chain/state/stateutil/validator_root.go
  • consensus-types/blocks/factory.go
  • beacon-chain/rpc/prysm/v1alpha1/beacon/blocks.go
  • beacon-chain/blockchain/merge_ascii_art.go
  • runtime/interop/generate_genesis_state.go
  • beacon-chain/state/state-native/types.go
  • params/config.go
  • config/features/flags.go
  • cmd/validator/wallet/wallet.go
  • beacon-chain/rpc/apimiddleware/structs.go
  • beacon-chain/rpc/service.go
  • beacon-chain/execution/service.go
  • contracts/deposit/contract.go
  • consensus-types/blocks/proto.go
  • beacon-chain/sync/metrics.go
  • beacon-chain/state/fieldtrie/field_trie_helpers.go
  • beacon-chain/db/kv/state.go
  • beacon-chain/state/stategen/history.go
  • beacon-chain/state/stateutil/field_root_validator.go
  • proto/migration/v1alpha1_to_v2.go
  • api/client/builder/types.go
  • beacon-chain/core/blocks/header.go
  • core/vm/contracts.go
  • core/vm/stateful_contracts.go
  • cmd/validator/accounts/accounts.go
  • bahamut/FTNVault.sol
  • core/genesis.go
  • tools/benchmark-files-gen/main.go
  • beacon-chain/monitor/process_block.go
  • beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification.go
  • config/params/values.go
  • beacon-chain/state/state-native/state_trie.go
  • beacon-chain/execution/activities_processing.go
  • beacon-chain/sync/rpc_beacon_blocks_by_range.go
  • core/vm/errors.go
  • beacon-chain/rpc/prysm/v1alpha1/validator/proposer_activities.go
  • beacon-chain/builder/service.go
  • beacon-chain/core/blocks/activities.go
  • beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go
  • beacon-chain/blockchain/process_block_helpers.go
  • beacon-chain/execution/log_processing.go
  • beacon-chain/rpc/prysm/v1alpha1/validator/server.go
  • beacon-chain/forkchoice/doubly-linked-tree/on_tick.go
  • beacon-chain/core/epoch/precompute/justification_finalization.go
  • beacon-chain/blockchain/metrics.go
  • consensus-types/interfaces/beacon_block.go
  • cmd/validator/accounts/import.go
  • beacon-chain/rpc/eth/validator/validator.go
  • contracts/deposit/logs.go
  • validator/keymanager/remote-web3signer/v1/web3signer_types.go
  • config/params/mainnet_config.go
  • beacon-chain/forkchoice/doubly-linked-tree/types.go
  • beacon-chain/cache/activitycache/activity_change_cache.go
  • beacon-chain/deterministic-genesis/service.go
  • beacon-chain/state/state-native/getters_misc.go
  • beacon-chain/state/stateutil/shared_activity_root.go
  • encoding/bytesutil/bytes.go
  • beacon-chain/state/state-native/getters_eth1.go
  • beacon-chain/db/kv/genesis.go
  • beacon-chain/state/state-native/beacon_state_mainnet.go
  • beacon-chain/db/kv/execution_chain.go
  • beacon-chain/core/transition/transition.go
  • beacon-chain/builder/option.go
  • beacon-chain/node/node.go
  • api/client/beacon/checkpoint.go
  • beacon-chain/cache/activitycache/log.go
  • beacon-chain/sync/initial-sync/round_robin.go
  • internal/ethapi/api.go
  • core/state/snapshot/generate.go
  • consensus-types/interfaces/utils.go
  • beacon-chain/forkchoice/doubly-linked-tree/store.go
  • tools/blocktree/main.go
  • consensus-types/blocks/types.go
  • beacon-chain/state/state-native/beacon_state_minimal.go
  • validator/client/runner.go
  • beacon-chain/rpc/eth/beacon/validator.go
  • contracts/deposit/deposit.go
  • beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go
  • cmd/validator/slashing-protection/slashing-protection.go

Part of Fastex’s security record

Fastex
Audits4 audits by 2 firmsLast audit Jan 2024, by CertiK.
Critical findingsNo open critical findingsAcross the 4 reports, 1 critical finding was reported. It is marked fixed by the firm that reported it.
Open Fastex’s security record

Security firm

CertiK

1,044 projects reviewed · 1,375 reports · latest May 2025

See CertiK’s audits

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us