github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go (about) 1 package validator 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/hex" 7 "fmt" 8 "math/big" 9 "reflect" 10 "time" 11 12 fastssz "github.com/ferranbt/fastssz" 13 "github.com/pkg/errors" 14 types "github.com/prysmaticlabs/eth2-types" 15 "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" 16 "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" 17 blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block" 18 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 19 "github.com/prysmaticlabs/prysm/beacon-chain/core/state" 20 "github.com/prysmaticlabs/prysm/beacon-chain/core/state/interop" 21 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 22 dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" 23 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 24 "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper" 25 "github.com/prysmaticlabs/prysm/proto/interfaces" 26 attaggregation "github.com/prysmaticlabs/prysm/shared/aggregation/attestations" 27 "github.com/prysmaticlabs/prysm/shared/bytesutil" 28 "github.com/prysmaticlabs/prysm/shared/featureconfig" 29 "github.com/prysmaticlabs/prysm/shared/hashutil" 30 "github.com/prysmaticlabs/prysm/shared/params" 31 "github.com/prysmaticlabs/prysm/shared/rand" 32 "github.com/prysmaticlabs/prysm/shared/trieutil" 33 "github.com/sirupsen/logrus" 34 "go.opencensus.io/trace" 35 "google.golang.org/grpc/codes" 36 "google.golang.org/grpc/status" 37 ) 38 39 // eth1DataNotification is a latch to stop flooding logs with the same warning. 40 var eth1DataNotification bool 41 42 const eth1dataTimeout = 2 * time.Second 43 44 type eth1DataSingleVote struct { 45 eth1Data *ethpb.Eth1Data 46 blockHeight *big.Int 47 } 48 49 type eth1DataAggregatedVote struct { 50 data eth1DataSingleVote 51 votes int 52 } 53 54 // GetBlock is called by a proposer during its assigned slot to request a block to sign 55 // by passing in the slot and the signed randao reveal of the slot. 56 func (vs *Server) GetBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb.BeaconBlock, error) { 57 ctx, span := trace.StartSpan(ctx, "ProposerServer.GetBlock") 58 defer span.End() 59 span.AddAttributes(trace.Int64Attribute("slot", int64(req.Slot))) 60 61 if vs.SyncChecker.Syncing() { 62 return nil, status.Errorf(codes.Unavailable, "Syncing to latest head, not ready to respond") 63 } 64 65 // Retrieve the parent block as the current head of the canonical chain. 66 parentRoot, err := vs.HeadFetcher.HeadRoot(ctx) 67 if err != nil { 68 return nil, status.Errorf(codes.Internal, "Could not retrieve head root: %v", err) 69 } 70 71 head, err := vs.HeadFetcher.HeadState(ctx) 72 if err != nil { 73 return nil, status.Errorf(codes.Internal, "Could not get head state %v", err) 74 } 75 76 if featureconfig.Get().EnableNextSlotStateCache { 77 head, err = state.ProcessSlotsUsingNextSlotCache(ctx, head, parentRoot, req.Slot) 78 if err != nil { 79 return nil, status.Errorf(codes.Internal, "Could not advance slots to calculate proposer index: %v", err) 80 } 81 } else { 82 head, err = state.ProcessSlots(ctx, head, req.Slot) 83 if err != nil { 84 return nil, status.Errorf(codes.Internal, "Could not advance slot to calculate proposer index: %v", err) 85 } 86 } 87 88 eth1Data, err := vs.eth1DataMajorityVote(ctx, head) 89 if err != nil { 90 return nil, status.Errorf(codes.Internal, "Could not get ETH1 data: %v", err) 91 } 92 93 // Pack ETH1 deposits which have not been included in the beacon chain. 94 deposits, err := vs.deposits(ctx, head, eth1Data) 95 if err != nil { 96 return nil, status.Errorf(codes.Internal, "Could not get ETH1 deposits: %v", err) 97 } 98 99 // Pack aggregated attestations which have not been included in the beacon chain. 100 atts, err := vs.packAttestations(ctx, head) 101 if err != nil { 102 return nil, status.Errorf(codes.Internal, "Could not get attestations to pack into block: %v", err) 103 } 104 105 // Use zero hash as stub for state root to compute later. 106 stateRoot := params.BeaconConfig().ZeroHash[:] 107 108 graffiti := bytesutil.ToBytes32(req.Graffiti) 109 110 // Calculate new proposer index. 111 idx, err := helpers.BeaconProposerIndex(head) 112 if err != nil { 113 return nil, status.Errorf(codes.Internal, "Could not calculate proposer index %v", err) 114 } 115 116 blk := ðpb.BeaconBlock{ 117 Slot: req.Slot, 118 ParentRoot: parentRoot, 119 StateRoot: stateRoot, 120 ProposerIndex: idx, 121 Body: ðpb.BeaconBlockBody{ 122 Eth1Data: eth1Data, 123 Deposits: deposits, 124 Attestations: atts, 125 RandaoReveal: req.RandaoReveal, 126 ProposerSlashings: vs.SlashingsPool.PendingProposerSlashings(ctx, head, false /*noLimit*/), 127 AttesterSlashings: vs.SlashingsPool.PendingAttesterSlashings(ctx, head, false /*noLimit*/), 128 VoluntaryExits: vs.ExitPool.PendingExits(head, req.Slot, false /*noLimit*/), 129 Graffiti: graffiti[:], 130 }, 131 } 132 133 // Compute state root with the newly constructed block. 134 stateRoot, err = vs.computeStateRoot(ctx, wrapper.WrappedPhase0SignedBeaconBlock(ðpb.SignedBeaconBlock{Block: blk, Signature: make([]byte, 96)})) 135 if err != nil { 136 interop.WriteBlockToDisk(wrapper.WrappedPhase0SignedBeaconBlock(ðpb.SignedBeaconBlock{Block: blk}), true /*failed*/) 137 return nil, status.Errorf(codes.Internal, "Could not compute state root: %v", err) 138 } 139 blk.StateRoot = stateRoot 140 141 return blk, nil 142 } 143 144 // ProposeBlock is called by a proposer during its assigned slot to create a block in an attempt 145 // to get it processed by the beacon node as the canonical head. 146 func (vs *Server) ProposeBlock(ctx context.Context, rBlk *ethpb.SignedBeaconBlock) (*ethpb.ProposeResponse, error) { 147 blk := wrapper.WrappedPhase0SignedBeaconBlock(rBlk) 148 root, err := blk.Block().HashTreeRoot() 149 if err != nil { 150 return nil, status.Errorf(codes.Internal, "Could not tree hash block: %v", err) 151 } 152 153 // Do not block proposal critical path with debug logging or block feed updates. 154 defer func() { 155 log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf( 156 "Block proposal received via RPC") 157 vs.BlockNotifier.BlockFeed().Send(&feed.Event{ 158 Type: blockfeed.ReceivedBlock, 159 Data: &blockfeed.ReceivedBlockData{SignedBlock: blk}, 160 }) 161 }() 162 163 // Broadcast the new block to the network. 164 if err := vs.P2P.Broadcast(ctx, blk.Proto()); err != nil { 165 return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err) 166 } 167 log.WithFields(logrus.Fields{ 168 "blockRoot": hex.EncodeToString(root[:]), 169 }).Debug("Broadcasting block") 170 171 if err := vs.BlockReceiver.ReceiveBlock(ctx, blk, root); err != nil { 172 return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err) 173 } 174 175 return ðpb.ProposeResponse{ 176 BlockRoot: root[:], 177 }, nil 178 } 179 180 // eth1DataMajorityVote determines the appropriate eth1data for a block proposal using 181 // an algorithm called Voting with the Majority. The algorithm works as follows: 182 // - Determine the timestamp for the start slot for the eth1 voting period. 183 // - Determine the earliest and latest timestamps that a valid block can have. 184 // - Determine the first block not before the earliest timestamp. This block is the lower bound. 185 // - Determine the last block not after the latest timestamp. This block is the upper bound. 186 // - If the last block is too early, use current eth1data from the beacon state. 187 // - Filter out votes on unknown blocks and blocks which are outside of the range determined by the lower and upper bounds. 188 // - If no blocks are left after filtering votes, use eth1data from the latest valid block. 189 // - Otherwise: 190 // - Determine the vote with the highest count. Prefer the vote with the highest eth1 block height in the event of a tie. 191 // - This vote's block is the eth1 block to use for the block proposal. 192 func (vs *Server) eth1DataMajorityVote(ctx context.Context, beaconState iface.BeaconState) (*ethpb.Eth1Data, error) { 193 ctx, cancel := context.WithTimeout(ctx, eth1dataTimeout) 194 defer cancel() 195 196 slot := beaconState.Slot() 197 votingPeriodStartTime := vs.slotStartTime(slot) 198 199 if vs.MockEth1Votes { 200 return vs.mockETH1DataVote(ctx, slot) 201 } 202 if !vs.Eth1InfoFetcher.IsConnectedToETH1() { 203 return vs.randomETH1DataVote(ctx) 204 } 205 eth1DataNotification = false 206 207 eth1FollowDistance := params.BeaconConfig().Eth1FollowDistance 208 earliestValidTime := votingPeriodStartTime - 2*params.BeaconConfig().SecondsPerETH1Block*eth1FollowDistance 209 latestValidTime := votingPeriodStartTime - params.BeaconConfig().SecondsPerETH1Block*eth1FollowDistance 210 211 lastBlockByEarliestValidTime, err := vs.Eth1BlockFetcher.BlockByTimestamp(ctx, earliestValidTime) 212 if err != nil { 213 log.WithError(err).Error("Could not get last block by earliest valid time") 214 return vs.randomETH1DataVote(ctx) 215 } 216 // Increment the earliest block if the original block's time is before valid time. 217 // This is very likely to happen because BlockTimeByHeight returns the last block AT OR BEFORE the specified time. 218 if lastBlockByEarliestValidTime.Time < earliestValidTime { 219 lastBlockByEarliestValidTime.Number = big.NewInt(0).Add(lastBlockByEarliestValidTime.Number, big.NewInt(1)) 220 } 221 222 lastBlockByLatestValidTime, err := vs.Eth1BlockFetcher.BlockByTimestamp(ctx, latestValidTime) 223 if err != nil { 224 log.WithError(err).Error("Could not get last block by latest valid time") 225 return vs.randomETH1DataVote(ctx) 226 } 227 if lastBlockByLatestValidTime.Time < earliestValidTime { 228 return vs.HeadFetcher.HeadETH1Data(), nil 229 } 230 231 lastBlockDepositCount, lastBlockDepositRoot := vs.DepositFetcher.DepositsNumberAndRootAtHeight(ctx, lastBlockByLatestValidTime.Number) 232 if lastBlockDepositCount == 0 { 233 return vs.ChainStartFetcher.ChainStartEth1Data(), nil 234 } 235 236 if lastBlockDepositCount >= vs.HeadFetcher.HeadETH1Data().DepositCount { 237 hash, err := vs.Eth1BlockFetcher.BlockHashByHeight(ctx, lastBlockByLatestValidTime.Number) 238 if err != nil { 239 log.WithError(err).Error("Could not get hash of last block by latest valid time") 240 return vs.randomETH1DataVote(ctx) 241 } 242 return ðpb.Eth1Data{ 243 BlockHash: hash.Bytes(), 244 DepositCount: lastBlockDepositCount, 245 DepositRoot: lastBlockDepositRoot[:], 246 }, nil 247 } 248 return vs.HeadFetcher.HeadETH1Data(), nil 249 } 250 251 func (vs *Server) slotStartTime(slot types.Slot) uint64 { 252 startTime, _ := vs.Eth1InfoFetcher.Eth2GenesisPowchainInfo() 253 return helpers.VotingPeriodStartTime(startTime, slot) 254 } 255 256 func (vs *Server) inRangeVotes(ctx context.Context, 257 beaconState iface.ReadOnlyBeaconState, 258 firstValidBlockNumber, lastValidBlockNumber *big.Int) ([]eth1DataSingleVote, error) { 259 260 currentETH1Data := vs.HeadFetcher.HeadETH1Data() 261 262 var inRangeVotes []eth1DataSingleVote 263 for _, eth1Data := range beaconState.Eth1DataVotes() { 264 exists, height, err := vs.BlockFetcher.BlockExistsWithCache(ctx, bytesutil.ToBytes32(eth1Data.BlockHash)) 265 if err != nil { 266 log.Warningf("Could not fetch eth1data height for received eth1data vote: %v", err) 267 } 268 // Make sure we don't "undo deposit progress". See https://github.com/ethereum/eth2.0-specs/pull/1836 269 if eth1Data.DepositCount < currentETH1Data.DepositCount { 270 continue 271 } 272 // firstValidBlockNumber.Cmp(height) < 1 filters out all blocks before firstValidBlockNumber 273 // lastValidBlockNumber.Cmp(height) > -1 filters out all blocks after lastValidBlockNumber 274 // These filters result in the range [firstValidBlockNumber, lastValidBlockNumber] 275 if exists && firstValidBlockNumber.Cmp(height) < 1 && lastValidBlockNumber.Cmp(height) > -1 { 276 inRangeVotes = append(inRangeVotes, eth1DataSingleVote{eth1Data: eth1Data, blockHeight: height}) 277 } 278 } 279 280 return inRangeVotes, nil 281 } 282 283 func chosenEth1DataMajorityVote(votes []eth1DataSingleVote) eth1DataAggregatedVote { 284 var voteCount []eth1DataAggregatedVote 285 for _, singleVote := range votes { 286 newVote := true 287 for i, aggregatedVote := range voteCount { 288 aggregatedData := aggregatedVote.data 289 if reflect.DeepEqual(singleVote.eth1Data, aggregatedData.eth1Data) { 290 voteCount[i].votes++ 291 newVote = false 292 break 293 } 294 } 295 296 if newVote { 297 voteCount = append(voteCount, eth1DataAggregatedVote{data: singleVote, votes: 1}) 298 } 299 } 300 if len(voteCount) == 0 { 301 return eth1DataAggregatedVote{} 302 } 303 currentVote := voteCount[0] 304 for _, aggregatedVote := range voteCount[1:] { 305 // Choose new eth1data if it has more votes or the same number of votes with a bigger block height. 306 if aggregatedVote.votes > currentVote.votes || 307 (aggregatedVote.votes == currentVote.votes && 308 aggregatedVote.data.blockHeight.Cmp(currentVote.data.blockHeight) == 1) { 309 currentVote = aggregatedVote 310 } 311 } 312 313 return currentVote 314 } 315 316 func (vs *Server) mockETH1DataVote(ctx context.Context, slot types.Slot) (*ethpb.Eth1Data, error) { 317 if !eth1DataNotification { 318 log.Warn("Beacon Node is no longer connected to an ETH1 chain, so ETH1 data votes are now mocked.") 319 eth1DataNotification = true 320 } 321 // If a mock eth1 data votes is specified, we use the following for the 322 // eth1data we provide to every proposer based on https://github.com/ethereum/eth2.0-pm/issues/62: 323 // 324 // slot_in_voting_period = current_slot % SLOTS_PER_ETH1_VOTING_PERIOD 325 // Eth1Data( 326 // DepositRoot = hash(current_epoch + slot_in_voting_period), 327 // DepositCount = state.eth1_deposit_index, 328 // BlockHash = hash(hash(current_epoch + slot_in_voting_period)), 329 // ) 330 slotInVotingPeriod := slot.ModSlot(params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().EpochsPerEth1VotingPeriod))) 331 headState, err := vs.HeadFetcher.HeadState(ctx) 332 if err != nil { 333 return nil, err 334 } 335 var enc []byte 336 enc = fastssz.MarshalUint64(enc, uint64(helpers.SlotToEpoch(slot))+uint64(slotInVotingPeriod)) 337 depRoot := hashutil.Hash(enc) 338 blockHash := hashutil.Hash(depRoot[:]) 339 return ðpb.Eth1Data{ 340 DepositRoot: depRoot[:], 341 DepositCount: headState.Eth1DepositIndex(), 342 BlockHash: blockHash[:], 343 }, nil 344 } 345 346 func (vs *Server) randomETH1DataVote(ctx context.Context) (*ethpb.Eth1Data, error) { 347 if !eth1DataNotification { 348 log.Warn("Beacon Node is no longer connected to an ETH1 chain, so ETH1 data votes are now random.") 349 eth1DataNotification = true 350 } 351 headState, err := vs.HeadFetcher.HeadState(ctx) 352 if err != nil { 353 return nil, err 354 } 355 356 // set random roots and block hashes to prevent a majority from being 357 // built if the eth1 node is offline 358 randGen := rand.NewGenerator() 359 depRoot := hashutil.Hash(bytesutil.Bytes32(randGen.Uint64())) 360 blockHash := hashutil.Hash(bytesutil.Bytes32(randGen.Uint64())) 361 return ðpb.Eth1Data{ 362 DepositRoot: depRoot[:], 363 DepositCount: headState.Eth1DepositIndex(), 364 BlockHash: blockHash[:], 365 }, nil 366 } 367 368 // computeStateRoot computes the state root after a block has been processed through a state transition and 369 // returns it to the validator client. 370 func (vs *Server) computeStateRoot(ctx context.Context, block interfaces.SignedBeaconBlock) ([]byte, error) { 371 beaconState, err := vs.StateGen.StateByRoot(ctx, bytesutil.ToBytes32(block.Block().ParentRoot())) 372 if err != nil { 373 return nil, errors.Wrap(err, "could not retrieve beacon state") 374 } 375 root, err := state.CalculateStateRoot( 376 ctx, 377 beaconState, 378 block, 379 ) 380 if err != nil { 381 return nil, errors.Wrapf(err, "could not calculate state root at slot %d", beaconState.Slot()) 382 } 383 384 log.WithField("beaconStateRoot", fmt.Sprintf("%#x", root)).Debugf("Computed state root") 385 return root[:], nil 386 } 387 388 // deposits returns a list of pending deposits that are ready for inclusion in the next beacon 389 // block. Determining deposits depends on the current eth1data vote for the block and whether or not 390 // this eth1data has enough support to be considered for deposits inclusion. If current vote has 391 // enough support, then use that vote for basis of determining deposits, otherwise use current state 392 // eth1data. 393 func (vs *Server) deposits( 394 ctx context.Context, 395 beaconState iface.BeaconState, 396 currentVote *ethpb.Eth1Data, 397 ) ([]*ethpb.Deposit, error) { 398 ctx, span := trace.StartSpan(ctx, "ProposerServer.deposits") 399 defer span.End() 400 401 if vs.MockEth1Votes || !vs.Eth1InfoFetcher.IsConnectedToETH1() { 402 return []*ethpb.Deposit{}, nil 403 } 404 // Need to fetch if the deposits up to the state's latest eth 1 data matches 405 // the number of all deposits in this RPC call. If not, then we return nil. 406 canonicalEth1Data, canonicalEth1DataHeight, err := vs.canonicalEth1Data(ctx, beaconState, currentVote) 407 if err != nil { 408 return nil, err 409 } 410 411 _, genesisEth1Block := vs.Eth1InfoFetcher.Eth2GenesisPowchainInfo() 412 if genesisEth1Block.Cmp(canonicalEth1DataHeight) == 0 { 413 return []*ethpb.Deposit{}, nil 414 } 415 416 // If there are no pending deposits, exit early. 417 allPendingContainers := vs.PendingDepositsFetcher.PendingContainers(ctx, canonicalEth1DataHeight) 418 if len(allPendingContainers) == 0 { 419 return []*ethpb.Deposit{}, nil 420 } 421 422 depositTrie, err := vs.depositTrie(ctx, canonicalEth1Data, canonicalEth1DataHeight) 423 if err != nil { 424 return nil, errors.Wrap(err, "could not retrieve deposit trie") 425 } 426 427 // Deposits need to be received in order of merkle index root, so this has to make sure 428 // deposits are sorted from lowest to highest. 429 var pendingDeps []*dbpb.DepositContainer 430 for _, dep := range allPendingContainers { 431 if uint64(dep.Index) >= beaconState.Eth1DepositIndex() && uint64(dep.Index) < canonicalEth1Data.DepositCount { 432 pendingDeps = append(pendingDeps, dep) 433 } 434 } 435 436 for i := range pendingDeps { 437 // Don't construct merkle proof if the number of deposits is more than max allowed in block. 438 if uint64(i) == params.BeaconConfig().MaxDeposits { 439 break 440 } 441 pendingDeps[i].Deposit, err = constructMerkleProof(depositTrie, int(pendingDeps[i].Index), pendingDeps[i].Deposit) 442 if err != nil { 443 return nil, err 444 } 445 } 446 // Limit the return of pending deposits to not be more than max deposits allowed in block. 447 var pendingDeposits []*ethpb.Deposit 448 for i := uint64(0); i < uint64(len(pendingDeps)) && i < params.BeaconConfig().MaxDeposits; i++ { 449 pendingDeposits = append(pendingDeposits, pendingDeps[i].Deposit) 450 } 451 return pendingDeposits, nil 452 } 453 454 // canonicalEth1Data determines the canonical eth1data and eth1 block height to use for determining deposits. 455 func (vs *Server) canonicalEth1Data( 456 ctx context.Context, 457 beaconState iface.BeaconState, 458 currentVote *ethpb.Eth1Data) (*ethpb.Eth1Data, *big.Int, error) { 459 460 var eth1BlockHash [32]byte 461 462 // Add in current vote, to get accurate vote tally 463 if err := beaconState.AppendEth1DataVotes(currentVote); err != nil { 464 return nil, nil, errors.Wrap(err, "could not append eth1 data votes to state") 465 } 466 hasSupport, err := blocks.Eth1DataHasEnoughSupport(beaconState, currentVote) 467 if err != nil { 468 return nil, nil, errors.Wrap(err, "could not determine if current eth1data vote has enough support") 469 } 470 var canonicalEth1Data *ethpb.Eth1Data 471 if hasSupport { 472 canonicalEth1Data = currentVote 473 eth1BlockHash = bytesutil.ToBytes32(currentVote.BlockHash) 474 } else { 475 canonicalEth1Data = beaconState.Eth1Data() 476 eth1BlockHash = bytesutil.ToBytes32(beaconState.Eth1Data().BlockHash) 477 } 478 _, canonicalEth1DataHeight, err := vs.Eth1BlockFetcher.BlockExists(ctx, eth1BlockHash) 479 if err != nil { 480 return nil, nil, errors.Wrap(err, "could not fetch eth1data height") 481 } 482 return canonicalEth1Data, canonicalEth1DataHeight, nil 483 } 484 485 func (vs *Server) depositTrie(ctx context.Context, canonicalEth1Data *ethpb.Eth1Data, canonicalEth1DataHeight *big.Int) (*trieutil.SparseMerkleTrie, error) { 486 ctx, span := trace.StartSpan(ctx, "ProposerServer.depositTrie") 487 defer span.End() 488 489 var depositTrie *trieutil.SparseMerkleTrie 490 491 finalizedDeposits := vs.DepositFetcher.FinalizedDeposits(ctx) 492 depositTrie = finalizedDeposits.Deposits 493 upToEth1DataDeposits := vs.DepositFetcher.NonFinalizedDeposits(ctx, canonicalEth1DataHeight) 494 insertIndex := finalizedDeposits.MerkleTrieIndex + 1 495 496 for _, dep := range upToEth1DataDeposits { 497 depHash, err := dep.Data.HashTreeRoot() 498 if err != nil { 499 return nil, errors.Wrap(err, "could not hash deposit data") 500 } 501 depositTrie.Insert(depHash[:], int(insertIndex)) 502 insertIndex++ 503 } 504 valid, err := vs.validateDepositTrie(depositTrie, canonicalEth1Data) 505 // Log a warning here, as the cached trie is invalid. 506 if !valid { 507 log.Warnf("Cached deposit trie is invalid, rebuilding it now: %v", err) 508 return vs.rebuildDepositTrie(ctx, canonicalEth1Data, canonicalEth1DataHeight) 509 } 510 511 return depositTrie, nil 512 } 513 514 // rebuilds our deposit trie by recreating it from all processed deposits till 515 // specified eth1 block height. 516 func (vs *Server) rebuildDepositTrie(ctx context.Context, canonicalEth1Data *ethpb.Eth1Data, canonicalEth1DataHeight *big.Int) (*trieutil.SparseMerkleTrie, error) { 517 ctx, span := trace.StartSpan(ctx, "ProposerServer.rebuildDepositTrie") 518 defer span.End() 519 520 deposits := vs.DepositFetcher.AllDeposits(ctx, canonicalEth1DataHeight) 521 trieItems := make([][]byte, 0, len(deposits)) 522 for _, dep := range deposits { 523 depHash, err := dep.Data.HashTreeRoot() 524 if err != nil { 525 return nil, errors.Wrap(err, "could not hash deposit data") 526 } 527 trieItems = append(trieItems, depHash[:]) 528 } 529 depositTrie, err := trieutil.GenerateTrieFromItems(trieItems, params.BeaconConfig().DepositContractTreeDepth) 530 if err != nil { 531 return nil, err 532 } 533 534 valid, err := vs.validateDepositTrie(depositTrie, canonicalEth1Data) 535 // Log an error here, as even with rebuilding the trie, it is still invalid. 536 if !valid { 537 log.Errorf("Rebuilt deposit trie is invalid: %v", err) 538 } 539 return depositTrie, nil 540 } 541 542 // validate that the provided deposit trie matches up with the canonical eth1 data provided. 543 func (vs *Server) validateDepositTrie(trie *trieutil.SparseMerkleTrie, canonicalEth1Data *ethpb.Eth1Data) (bool, error) { 544 if trie.NumOfItems() != int(canonicalEth1Data.DepositCount) { 545 return false, errors.Errorf("wanted the canonical count of %d but received %d", canonicalEth1Data.DepositCount, trie.NumOfItems()) 546 } 547 rt := trie.HashTreeRoot() 548 if !bytes.Equal(rt[:], canonicalEth1Data.DepositRoot) { 549 return false, errors.Errorf("wanted the canonical deposit root of %#x but received %#x", canonicalEth1Data.DepositRoot, rt) 550 } 551 return true, nil 552 } 553 554 // in case no vote for new eth1data vote considered best vote we 555 // default into returning the latest deposit root and the block 556 // hash of eth1 block hash that is FOLLOW_DISTANCE back from its 557 // latest block. 558 func (vs *Server) defaultEth1DataResponse(ctx context.Context, currentHeight *big.Int) (*ethpb.Eth1Data, error) { 559 if ctx.Err() != nil { 560 return nil, ctx.Err() 561 } 562 eth1FollowDistance := int64(params.BeaconConfig().Eth1FollowDistance) 563 ancestorHeight := big.NewInt(0).Sub(currentHeight, big.NewInt(eth1FollowDistance)) 564 blockHash, err := vs.Eth1BlockFetcher.BlockHashByHeight(ctx, ancestorHeight) 565 if err != nil { 566 return nil, errors.Wrap(err, "could not fetch ETH1_FOLLOW_DISTANCE ancestor") 567 } 568 // Fetch all historical deposits up to an ancestor height. 569 depositsTillHeight, depositRoot := vs.DepositFetcher.DepositsNumberAndRootAtHeight(ctx, ancestorHeight) 570 if depositsTillHeight == 0 { 571 return vs.ChainStartFetcher.ChainStartEth1Data(), nil 572 } 573 // // Make sure we don't "undo deposit progress". See https://github.com/ethereum/eth2.0-specs/pull/1836 574 currentETH1Data := vs.HeadFetcher.HeadETH1Data() 575 if depositsTillHeight < currentETH1Data.DepositCount { 576 return currentETH1Data, nil 577 } 578 return ðpb.Eth1Data{ 579 DepositRoot: depositRoot[:], 580 BlockHash: blockHash[:], 581 DepositCount: depositsTillHeight, 582 }, nil 583 } 584 585 // This filters the input attestations to return a list of valid attestations to be packaged inside a beacon block. 586 func (vs *Server) filterAttestationsForBlockInclusion(ctx context.Context, st iface.BeaconState, atts []*ethpb.Attestation) ([]*ethpb.Attestation, error) { 587 ctx, span := trace.StartSpan(ctx, "ProposerServer.filterAttestationsForBlockInclusion") 588 defer span.End() 589 590 validAtts, invalidAtts := proposerAtts(atts).filter(ctx, st) 591 if err := vs.deleteAttsInPool(ctx, invalidAtts); err != nil { 592 return nil, err 593 } 594 deduped, err := validAtts.dedup() 595 if err != nil { 596 return nil, err 597 } 598 sorted, err := deduped.sortByProfitability() 599 if err != nil { 600 return nil, err 601 } 602 return sorted.limitToMaxAttestations(), nil 603 } 604 605 // The input attestations are processed and seen by the node, this deletes them from pool 606 // so proposers don't include them in a block for the future. 607 func (vs *Server) deleteAttsInPool(ctx context.Context, atts []*ethpb.Attestation) error { 608 ctx, span := trace.StartSpan(ctx, "ProposerServer.deleteAttsInPool") 609 defer span.End() 610 611 for _, att := range atts { 612 if ctx.Err() != nil { 613 return ctx.Err() 614 } 615 if helpers.IsAggregated(att) { 616 if err := vs.AttPool.DeleteAggregatedAttestation(att); err != nil { 617 return err 618 } 619 } else { 620 if err := vs.AttPool.DeleteUnaggregatedAttestation(att); err != nil { 621 return err 622 } 623 } 624 } 625 return nil 626 } 627 628 func constructMerkleProof(trie *trieutil.SparseMerkleTrie, index int, deposit *ethpb.Deposit) (*ethpb.Deposit, error) { 629 proof, err := trie.MerkleProof(index) 630 if err != nil { 631 return nil, errors.Wrapf(err, "could not generate merkle proof for deposit at index %d", index) 632 } 633 // For every deposit, we construct a Merkle proof using the powchain service's 634 // in-memory deposits trie, which is updated only once the state's LatestETH1Data 635 // property changes during a state transition after a voting period. 636 deposit.Proof = proof 637 return deposit, nil 638 } 639 640 func (vs *Server) packAttestations(ctx context.Context, latestState iface.BeaconState) ([]*ethpb.Attestation, error) { 641 ctx, span := trace.StartSpan(ctx, "ProposerServer.packAttestations") 642 defer span.End() 643 644 atts := vs.AttPool.AggregatedAttestations() 645 atts, err := vs.filterAttestationsForBlockInclusion(ctx, latestState, atts) 646 if err != nil { 647 return nil, errors.Wrap(err, "could not filter attestations") 648 } 649 650 // If there is any room left in the block, consider unaggregated attestations as well. 651 numAtts := uint64(len(atts)) 652 if numAtts < params.BeaconConfig().MaxAttestations { 653 uAtts, err := vs.AttPool.UnaggregatedAttestations() 654 if err != nil { 655 return nil, errors.Wrap(err, "could not get unaggregated attestations") 656 } 657 uAtts, err = vs.filterAttestationsForBlockInclusion(ctx, latestState, uAtts) 658 if err != nil { 659 return nil, errors.Wrap(err, "could not filter attestations") 660 } 661 atts = append(atts, uAtts...) 662 663 attsByDataRoot := make(map[[32]byte][]*ethpb.Attestation, len(atts)) 664 for _, att := range atts { 665 attDataRoot, err := att.Data.HashTreeRoot() 666 if err != nil { 667 return nil, err 668 } 669 attsByDataRoot[attDataRoot] = append(attsByDataRoot[attDataRoot], att) 670 } 671 672 attsForInclusion := proposerAtts(make([]*ethpb.Attestation, 0)) 673 for _, as := range attsByDataRoot { 674 as, err := attaggregation.Aggregate(as) 675 if err != nil { 676 return nil, err 677 } 678 attsForInclusion = append(attsForInclusion, as...) 679 } 680 deduped, err := attsForInclusion.dedup() 681 if err != nil { 682 return nil, err 683 } 684 sorted, err := deduped.sortByProfitability() 685 if err != nil { 686 return nil, err 687 } 688 atts = sorted.limitToMaxAttestations() 689 } 690 return atts, nil 691 }