prysm-pulse/beacon-chain/chaintest/backend/helpers.go
Raul Jordan 3c5f5cfcd3
Advance Beacon State Transition Part 4: Simulate Proposer Slashings (#1297)
* deposit definition according to latest spec

* ssz decode input data tests

* fix todo

* ignore XXX fields in struct

* fix

* timestamp

* gazelle run processing

* process deposit complete

* all logic complete

* verify merkle branch

* gazelle

* process deposit func

* diff cov 1005

* add todo"

* all test cases written down

* most tests complete

* ttl timestamp fail

* 100% code coverage in deposits

* fix params

* encode deposit data helper func

* state transition with no slots failing with panic at calcnewblockhashes

* smaller deposits for chain start

* state advancement benches

* ran go tests

* bazel

* improve the thing

* lint

* works works works

* all conflicts fixed

* edit readme to specify tests format

* edit readme to specify tests format

* skip slots works yay

* gazelle

* edit readme to specify tests format

* wrapped up all randao simulation

* fix

* passing

* goimports

* move to slices pkg

* deadcode

* deposit yaml tests

* created deposit trie implementation in Go

* created deposit trie implementation in Go

* gazelle

* merkle branch generation

* merkle branch generation

* more merkle debugging

* fix deposit trie

* include new merkle trie functions

* update all deposit operations

* capitalize

* advancing deposits fully works, grows the validator set

* wrap up time formatting

* lint fix

* include all information in the README

* edit conf

* revert

* clean up before merge

* successfully e2e test proposer slashings

* fix comments
2019-01-12 10:11:43 +08:00

170 lines
6.3 KiB
Go

package backend
import (
"fmt"
"strconv"
"time"
"github.com/prysmaticlabs/prysm/shared/trie"
"github.com/gogo/protobuf/proto"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
// Generates a simulated beacon block to use
// in the next state transition given the current state,
// the previous beacon block, and previous beacon block root.
func generateSimulatedBlock(
beaconState *pb.BeaconState,
prevBlockRoot [32]byte,
randaoReveal [32]byte,
depositRandaoCommit [32]byte,
simulatedDeposit *StateTestDeposit,
depositsTrie *trie.DepositTrie,
simulatedProposerSlashing *StateTestProposerSlashing,
) (*pb.BeaconBlock, [32]byte, error) {
encodedState, err := proto.Marshal(beaconState)
if err != nil {
return nil, [32]byte{}, fmt.Errorf("could not marshal beacon state: %v", err)
}
stateRoot := hashutil.Hash(encodedState)
block := &pb.BeaconBlock{
Slot: beaconState.Slot + 1,
RandaoRevealHash32: randaoReveal[:],
ParentRootHash32: prevBlockRoot[:],
StateRootHash32: stateRoot[:],
Body: &pb.BeaconBlockBody{
ProposerSlashings: []*pb.ProposerSlashing{},
CasperSlashings: []*pb.CasperSlashing{},
Attestations: []*pb.Attestation{},
Deposits: []*pb.Deposit{},
Exits: []*pb.Exit{},
},
}
if simulatedDeposit != nil {
depositInput := &pb.DepositInput{
Pubkey: []byte(simulatedDeposit.Pubkey),
WithdrawalCredentialsHash32: []byte{},
ProofOfPossession: []byte{},
RandaoCommitmentHash32: depositRandaoCommit[:],
CustodyCommitmentHash32: []byte{},
}
data, err := b.EncodeDepositData(depositInput, simulatedDeposit.Amount, time.Now().Unix())
if err != nil {
return nil, [32]byte{}, fmt.Errorf("could not encode deposit data: %v", err)
}
// We then update the deposits Merkle trie with the deposit data and return
// its Merkle branch leading up to the root of the trie.
depositsTrie.UpdateDepositTrie(data)
merkleBranch := depositsTrie.GenerateMerkleBranch(simulatedDeposit.MerkleIndex)
block.Body.Deposits = append(block.Body.Deposits, &pb.Deposit{
DepositData: data,
MerkleBranchHash32S: merkleBranch,
MerkleTreeIndex: simulatedDeposit.MerkleIndex,
})
}
if simulatedProposerSlashing != nil {
block.Body.ProposerSlashings = append(block.Body.ProposerSlashings, &pb.ProposerSlashing{
ProposerIndex: simulatedProposerSlashing.ProposerIndex,
ProposalData_1: &pb.ProposalSignedData{
Slot: simulatedProposerSlashing.Proposal1Slot,
Shard: simulatedProposerSlashing.Proposal1Shard,
BlockRootHash32: []byte(simulatedProposerSlashing.Proposal1Root),
},
ProposalData_2: &pb.ProposalSignedData{
Slot: simulatedProposerSlashing.Proposal2Slot,
Shard: simulatedProposerSlashing.Proposal2Shard,
BlockRootHash32: []byte(simulatedProposerSlashing.Proposal2Root),
},
})
}
encodedBlock, err := proto.Marshal(block)
if err != nil {
return nil, [32]byte{}, fmt.Errorf("could not marshal new block: %v", err)
}
return block, hashutil.Hash(encodedBlock), nil
}
// Given a number of slots, we create a list of hash onions from an underlying randao reveal. For example,
// if we have N slots, we create a list of [secret, hash(secret), hash(hash(secret)), hash(...(prev N-1 hashes))].
func generateSimulatedRandaoHashOnions(numSlots uint64) [][32]byte {
// We create a list of randao hash onions for the given number of epochs
// we run the state transition.
numEpochs := numSlots % params.BeaconConfig().EpochLength
hashOnions := [][32]byte{params.BeaconConfig().SimulatedBlockRandao}
// We make the length of the hash onions list equal to the number of epochs + 10 to be safe.
for i := uint64(0); i < numEpochs+10; i++ {
prevHash := hashOnions[i]
hashOnions = append(hashOnions, hashutil.Hash(prevHash[:]))
}
return hashOnions
}
// This function determines the block randao reveal assuming there are no skipped slots,
// given a list of randao hash onions such as [pre-image, 0x01, 0x02, 0x03], for the
// 0th epoch, the block randao reveal will be 0x02 and the proposer commitment 0x03.
// The next epoch, the block randao reveal will be 0x01 and the commitment 0x02,
// so on and so forth until all randao layers are peeled off.
func determineSimulatedBlockRandaoReveal(layersPeeled int, hashOnions [][32]byte) [32]byte {
if layersPeeled == 0 {
return hashOnions[len(hashOnions)-2]
}
return hashOnions[len(hashOnions)-layersPeeled-2]
}
// Generates initial deposits for creating a beacon state in the simulated
// backend based on the yaml configuration.
func generateInitialSimulatedDeposits(randaoCommit [32]byte) ([]*pb.Deposit, error) {
genesisTime := params.BeaconConfig().GenesisTime.Unix()
deposits := make([]*pb.Deposit, params.BeaconConfig().DepositsForChainStart)
for i := 0; i < len(deposits); i++ {
depositInput := &pb.DepositInput{
Pubkey: []byte(strconv.Itoa(i)),
RandaoCommitmentHash32: randaoCommit[:],
}
depositData, err := b.EncodeDepositData(
depositInput,
params.BeaconConfig().MaxDepositInGwei,
genesisTime,
)
if err != nil {
return nil, fmt.Errorf("could not encode initial block deposits: %v", err)
}
deposits[i] = &pb.Deposit{DepositData: depositData}
}
return deposits, nil
}
// Finds the index of the next slot's proposer in the beacon state's
// validator set.
func findNextSlotProposerIndex(beaconState *pb.BeaconState) (uint32, error) {
nextSlot := beaconState.Slot + 1
epochLength := params.BeaconConfig().EpochLength
var earliestSlot uint64
// If the state slot is less than epochLength, then the earliestSlot would
// result in a negative number. Therefore we should default to
// earliestSlot = 0 in this case.
if nextSlot > epochLength {
earliestSlot = nextSlot - (nextSlot % epochLength) - epochLength
}
if nextSlot < earliestSlot || nextSlot >= earliestSlot+(epochLength*2) {
return 0, fmt.Errorf("slot %d out of bounds: %d <= slot < %d",
nextSlot,
earliestSlot,
earliestSlot+(epochLength*2),
)
}
committeeArray := beaconState.ShardCommitteesAtSlots[nextSlot-earliestSlot]
firstCommittee := committeeArray.ArrayShardCommittee[0].Committee
return firstCommittee[nextSlot%uint64(len(firstCommittee))], nil
}