Advance Beacon State Transition Part 2: Simulate Block Randao at Every Slot (#1252)

This commit is contained in:
Raul Jordan
2019-01-11 18:55:01 +08:00
committed by GitHub
parent 1f11b821ab
commit 5258f3d3d1
15 changed files with 272 additions and 142 deletions

1
.gitignore vendored
View File

@@ -2,7 +2,6 @@
bazel-*
.DS_Store
.gitattributes
# IntelliJ
.idea

View File

@@ -24,8 +24,5 @@ go_test(
srcs = ["yaml_test.go"],
data = glob(["tests/**"]),
embed = [":go_default_library"],
deps = [
"//beacon-chain/chaintest/backend:go_default_library",
"@com_github_go_yaml_yaml//:go_default_library",
],
deps = ["//beacon-chain/chaintest/backend:go_default_library"],
)

View File

@@ -8,79 +8,49 @@ The test suite opts for YAML due to wide language support and support for inline
The testing format follows the official ETH2.0 Specification created [here](https://github.com/ethereum/eth2.0-specs/blob/master/specs/test-format.md)
### Core, Chain Tests
### Stateful Tests
Chain tests check for conformity of a certain client to the beacon chain specification for items such as the fork choice rule and Casper FFG validator rewards & penalties. Stateful tests need to specify a certain configuration of a beacon chain, with items such as the number validators, in the YAML file. Sample tests will all required fields are shown below.
**Fork Choice and Chain Updates**
**State Transition**
The most important use case for this test format is to verify the ins and outs of the Ethereum Phase 0 Beacon Chain state advancement. The specification details very strict guidelines for blocks to successfully trigger a state transition, including items such as Casper Proof of Stake slashing conditions of validators, pseudorandomness in the form of RANDAO, and attestation on shard blocks being processed all inside each incoming beacon block. The YAML configuration for this test type allows for configuring a state transition run over N slots, triggering slashing conditions, processing deposits of new validators, and more.
An example state transition test for testing slot and block processing will look as follows:
```yaml
title: Sample Ethereum 2.0 Beacon Chain Test
summary: Basic, functioning fork choice rule for Ethereum 2.0
title: Sample Ethereum Serenity State Transition Tests
summary: Testing state transitions occurring over N slots with varying deposit sizes and proposal skips
test_suite: prysm
fork: tchaikovsky
version: 1.0
test_cases:
- config:
validator_count: 100
cycle_length: 8
shard_count: 32
min_committee_size: 8
slots:
# "slot_number" has a minimum of 1
- slot_number: 1
new_block:
id: A
# "*" is used for the genesis block
parent: "*"
attestations:
- block: A
# the following is a shorthand string for [0, 1, 2, 3, 4, 5]
validators: "0-5"
- slot_number: 2
new_block:
id: B
parent: A
attestations:
- block: B
validators: "0-5"
- slot_number: 3
new_block:
id: C
parent: A
attestations:
# attestation "committee_slot" defaults to the slot during which the attestation occurs
- block: C
validators: "2-7"
# default "committee_slot" can be directly overridden
- block: C
committee_slot: 2
validators: "6, 7"
- slot_number: 4
new_block:
id: D
parent: C
attestations:
- block: D
validators: "1-4"
# slots can be skipped entirely (5 in this case)
- slot_number: 6
new_block:
id: E
parent: D
attestations:
- block: E
validators: "0-4"
- block: B
validators: "5, 6, 7"
epoch_length: 64
deposits_for_chain_start: 1000
num_slots: 32 # Testing advancing state to slot < EpochLength
results:
head: E
last_justified_block: "*"
last_finalized_block: "*"
slot: 32
- config:
epoch_length: 64
deposits_for_chain_start: 16384
num_slots: 64 # Testing advancing state to exactly slot == EpochLength
results:
slot: 64
- config:
skip_slots: [10, 20, 30]
epoch_length: 64
deposits_for_chain_start: 1000
num_slots: 128 # Testing advancing state's slot == 2*EpochLength
results:
slot: 128
```
**Casper FFG Rewards/Penalties**
TODO
The following configuration options are available for state transition tests:
- **skip_slots**: `[int]` determines which slot numbers to simulate a proposer not submitting a block in the state transition TODO
- **epoch_length**: `int` the number of slots in an epoch
- **deposits_for_chain_start**: `int` the number of eth deposits needed for the beacon chain to initialize (this simulates an initial validator registry based on this number in the test)
- **num_slots**: `int` the number of times we run a state transition in the test
### Stateless Tests

View File

@@ -3,7 +3,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"chain_test_format.go",
"fork_choice_test_format.go",
"helpers.go",
"setup_db.go",
"shuffle_test_format.go",
"simulated_backend.go",
@@ -20,6 +21,7 @@ go_library(
"//proto/beacon/p2p/v1:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/slices:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",

View File

@@ -1,37 +1,37 @@
package backend
// ChainTest --
type ChainTest struct {
// ForkChoiceTest --
type ForkChoiceTest struct {
Title string
Summary string
TestSuite string `yaml:"test_suite"`
TestCases []*ChainTestCase `yaml:"test_cases"`
TestSuite string `yaml:"test_suite"`
TestCases []*ForkChoiceTestCase `yaml:"test_cases"`
}
// ChainTestCase --
type ChainTestCase struct {
Config *ChainTestConfig `yaml:"config"`
Slots []*ChainTestSlot `yaml:"slots,flow"`
Results *ChainTestResults `yaml:"results"`
// ForkChoiceTestCase --
type ForkChoiceTestCase struct {
Config *ForkChoiceTestConfig `yaml:"config"`
Slots []*ForkChoiceTestSlot `yaml:"slots,flow"`
Results *ForkChoiceTestResult `yaml:"results"`
}
// ChainTestConfig --
type ChainTestConfig struct {
// ForkChoiceTestConfig --
type ForkChoiceTestConfig struct {
ValidatorCount uint64 `yaml:"validator_count"`
CycleLength uint64 `yaml:"cycle_length"`
ShardCount uint64 `yaml:"shard_count"`
MinCommitteeSize uint64 `yaml:"min_committee_size"`
}
// ChainTestSlot --
type ChainTestSlot struct {
// ForkChoiceTestSlot --
type ForkChoiceTestSlot struct {
SlotNumber uint64 `yaml:"slot_number"`
NewBlock *TestBlock `yaml:"new_block"`
Attestations []*TestAttestation `yaml:",flow"`
}
// ChainTestResults --
type ChainTestResults struct {
// ForkChoiceTestResult --
type ForkChoiceTestResult struct {
Head string
LastJustifiedBlock string `yaml:"last_justified_block"`
LastFinalizedBlock string `yaml:"last_finalized_block"`

View File

@@ -0,0 +1,122 @@
package backend
import (
"fmt"
"strconv"
"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,
) (*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{},
},
}
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
}

View File

@@ -7,7 +7,6 @@ import (
"context"
"fmt"
"reflect"
"strconv"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -20,6 +19,7 @@ import (
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/slices"
log "github.com/sirupsen/logrus"
)
@@ -28,7 +28,7 @@ import (
// and other e2e use cases.
type SimulatedBackend struct {
chainService *blockchain.ChainService
db *db.BeaconDB
beaconDB *db.BeaconDB
}
// NewSimulatedBackend creates an instance by initializing a chain service
@@ -49,15 +49,15 @@ func NewSimulatedBackend() (*SimulatedBackend, error) {
}
return &SimulatedBackend{
chainService: cs,
db: db,
beaconDB: db,
}, nil
}
// RunChainTest uses a parsed set of chaintests from a YAML file
// RunForkChoiceTest uses a parsed set of chaintests from a YAML file
// according to the ETH 2.0 client chain test specification and runs them
// against the simulated backend.
func (sb *SimulatedBackend) RunChainTest(testCase *ChainTestCase) error {
defer teardownDB(sb.db)
func (sb *SimulatedBackend) RunForkChoiceTest(testCase *ForkChoiceTestCase) error {
defer teardownDB(sb.beaconDB)
// Utilize the config parameters in the test case to setup
// the DB and set global config parameters accordingly.
// Config parameters include: ValidatorCount, ShardCount,
@@ -76,7 +76,6 @@ func (sb *SimulatedBackend) RunChainTest(testCase *ChainTestCase) error {
for i := uint64(0); i < testCase.Config.ValidatorCount; i++ {
validators[i] = &pb.ValidatorRecord{
ExitSlot: params.BeaconConfig().EntryExitDelay,
Balance: c.MaxDeposit * c.Gwei,
Pubkey: []byte{},
RandaoCommitmentHash32: randaoReveal[:],
}
@@ -92,8 +91,7 @@ func (sb *SimulatedBackend) RunChainTest(testCase *ChainTestCase) error {
// RunShuffleTest uses validator set specified from a YAML file, runs the validator shuffle
// algorithm, then compare the output with the expected output from the YAML file.
func (sb *SimulatedBackend) RunShuffleTest(testCase *ShuffleTestCase) error {
defer teardownDB(sb.db)
defer teardownDB(sb.beaconDB)
seed := common.BytesToHash([]byte(testCase.Seed))
output, err := utils.ShuffleIndices(seed, testCase.Input)
if err != nil {
@@ -109,6 +107,7 @@ func (sb *SimulatedBackend) RunShuffleTest(testCase *ShuffleTestCase) error {
// slots from a genesis state, with a block being processed at every iteration
// of the state transition function.
func (sb *SimulatedBackend) RunStateTransitionTest(testCase *StateTestCase) error {
defer teardownDB(sb.beaconDB)
// We setup the initial configuration for running state
// transition tests below.
c := params.BeaconConfig()
@@ -116,27 +115,23 @@ func (sb *SimulatedBackend) RunStateTransitionTest(testCase *StateTestCase) erro
c.DepositsForChainStart = testCase.Config.DepositsForChainStart
params.OverrideBeaconConfig(c)
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: []byte("simulated"),
}
depositData, err := b.EncodeDepositData(
depositInput,
params.BeaconConfig().MaxDepositInGwei,
genesisTime,
)
if err != nil {
return fmt.Errorf("could not encode initial block deposits: %v", err)
}
deposits[i] = &pb.Deposit{DepositData: depositData}
// We create a list of randao hash onions for the given number of slots
// the simulation will attempt.
hashOnions := generateSimulatedRandaoHashOnions(testCase.Config.NumSlots)
// We then generate initial validator deposits for initializing the
// beacon state based where every validator will use the last layer in the randao
// onions list as the commitment in the deposit instance.
lastRandaoLayer := hashOnions[len(hashOnions)-1]
initialDeposits, err := generateInitialSimulatedDeposits(lastRandaoLayer)
if err != nil {
return fmt.Errorf("could not simulate initial validator deposits: %v", err)
}
beaconState, err := state.InitialBeaconState(deposits, uint64(genesisTime), nil)
genesisTime := params.BeaconConfig().GenesisTime.Unix()
beaconState, err := state.InitialBeaconState(initialDeposits, uint64(genesisTime), nil)
if err != nil {
return err
return fmt.Errorf("could not initialize simulated beacon state")
}
// We do not expect hashing initial beacon state and genesis block to
@@ -147,16 +142,63 @@ func (sb *SimulatedBackend) RunStateTransitionTest(testCase *StateTestCase) erro
genesisBlock := b.NewGenesisBlock(stateRoot[:])
// #nosec G104
encodedGenesisBlock, _ := proto.Marshal(genesisBlock)
prevBlockRoot := hashutil.Hash(encodedGenesisBlock)
genesisBlockRoot := hashutil.Hash(encodedGenesisBlock)
// We now keep track of generated blocks for each state transition in
// a slice.
prevBlockRoots := [][32]byte{genesisBlockRoot}
// We keep track of the randao layers peeled for each proposer index in a map.
layersPeeledForProposer := make(map[uint32]int, len(beaconState.ValidatorRegistry))
for idx := range beaconState.ValidatorRegistry {
layersPeeledForProposer[uint32(idx)] = 0
}
startTime := time.Now()
for i := uint64(0); i < testCase.Config.NumSlots; i++ {
newState, err := state.ExecuteStateTransition(beaconState, nil, prevBlockRoot)
prevBlockRoot := prevBlockRoots[len(prevBlockRoots)-1]
proposerIndex, err := findNextSlotProposerIndex(beaconState)
if err != nil {
return fmt.Errorf("could not fetch beacon proposer index: %v", err)
}
// If the slot is marked as skipped in the configuration options,
// we simply run the state transition with a nil block argument.
if slices.IsInUint64(i, testCase.Config.SkipSlots) {
newState, err := state.ExecuteStateTransition(beaconState, nil, prevBlockRoot)
if err != nil {
return fmt.Errorf("could not execute state transition: %v", err)
}
beaconState = newState
layersPeeledForProposer[proposerIndex]++
continue
}
layersPeeled := layersPeeledForProposer[proposerIndex]
blockRandaoReveal := determineSimulatedBlockRandaoReveal(layersPeeled, hashOnions)
// We generate a new block to pass into the state transition.
newBlock, newBlockRoot, err := generateSimulatedBlock(
beaconState,
prevBlockRoot,
blockRandaoReveal,
)
if err != nil {
return fmt.Errorf("could not generate simulated beacon block %v", err)
}
newState, err := state.ExecuteStateTransition(beaconState, newBlock, prevBlockRoot)
if err != nil {
return fmt.Errorf("could not execute state transition: %v", err)
}
// We then keep track of information about the state after the
// state transition was applied.
beaconState = newState
prevBlockRoots = append(prevBlockRoots, newBlockRoot)
layersPeeledForProposer[proposerIndex]++
}
endTime := time.Now()
log.Infof(
"%d state transitions with %d deposits finished in %v",

View File

@@ -18,11 +18,12 @@ type StateTestCase struct {
// StateTestConfig --
type StateTestConfig struct {
PublishBlocks bool `yaml:"publish_blocks"`
EpochLength uint64 `yaml:"epoch_length"`
ShardCount uint64 `yaml:"shard_count"`
DepositsForChainStart uint64 `yaml:"deposits_for_chain_start"`
NumSlots uint64 `yaml:"num_slots"`
SkipSlots []uint64 `yaml:"skip_slots"`
PublishBlocks bool `yaml:"publish_blocks"`
EpochLength uint64 `yaml:"epoch_length"`
ShardCount uint64 `yaml:"shard_count"`
DepositsForChainStart uint64 `yaml:"deposits_for_chain_start"`
NumSlots uint64 `yaml:"num_slots"`
}
// StateTestResults --

View File

@@ -14,7 +14,7 @@ import (
)
func readTestsFromYaml(yamlDir string) ([]interface{}, error) {
const chainTestsFolderName = "chain-tests"
const forkChoiceTestsFolderName = "fork-choice-tests"
const shuffleTestsFolderName = "shuffle-tests"
const stateTestsFolderName = "state-tests"
@@ -37,8 +37,8 @@ func readTestsFromYaml(yamlDir string) ([]interface{}, error) {
return nil, fmt.Errorf("could not read yaml file: %v", err)
}
switch dir.Name() {
case chainTestsFolderName:
decoded := &backend.ChainTest{}
case forkChoiceTestsFolderName:
decoded := &backend.ForkChoiceTest{}
if err := yaml.Unmarshal(data, decoded); err != nil {
return nil, fmt.Errorf("could not unmarshal YAML file into test struct: %v", err)
}
@@ -64,12 +64,12 @@ func readTestsFromYaml(yamlDir string) ([]interface{}, error) {
func runTests(tests []interface{}, sb *backend.SimulatedBackend) error {
for _, tt := range tests {
switch typedTest := tt.(type) {
case *backend.ChainTest:
case *backend.ForkChoiceTest:
log.Infof("Title: %v", typedTest.Title)
log.Infof("Summary: %v", typedTest.Summary)
log.Infof("Test Suite: %v", typedTest.TestSuite)
for _, testCase := range typedTest.TestCases {
if err := sb.RunChainTest(testCase); err != nil {
if err := sb.RunForkChoiceTest(testCase); err != nil {
return fmt.Errorf("chain test failed: %v", err)
}
}

View File

@@ -5,21 +5,19 @@ fork: tchaikovsky
version: 1.0
test_cases:
- config:
publish_blocks: false
epoch_length: 64
deposits_for_chain_start: 1000
num_slots: 32 # Testing advancing state to slot < EpochLength
results:
slot: 32
- config:
publish_blocks: false
epoch_length: 64
deposits_for_chain_start: 16384
num_slots: 64 # Testing advancing state to exactly slot == EpochLength
results:
slot: 64
- config:
publish_blocks: false
skip_slots: [10, 20, 30]
epoch_length: 64
deposits_for_chain_start: 1000
num_slots: 128 # Testing advancing state's slot == 2*EpochLength

View File

@@ -1,10 +1,8 @@
package main
import (
"io/ioutil"
"testing"
"github.com/go-yaml/yaml"
"github.com/prysmaticlabs/prysm/beacon-chain/chaintest/backend"
)
@@ -25,28 +23,20 @@ func TestFromYaml(t *testing.T) {
}
func BenchmarkStateTestFromYaml(b *testing.B) {
file, err := ioutil.ReadFile("./tests/state-tests/no-blocks.yaml")
tests, err := readTestsFromYaml("./tests")
if err != nil {
b.Fatal(err)
}
test := &backend.StateTest{}
if err := yaml.Unmarshal(file, test); err != nil {
b.Fatal(err)
b.Fatalf("Failed to read yaml files: %v", err)
}
sb, err := backend.NewSimulatedBackend()
if err != nil {
b.Fatalf("Failed to setup simulated backend: %v", err)
b.Fatalf("Could not create backend: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, testCase := range test.TestCases {
if err := sb.RunStateTransitionTest(testCase); err != nil {
b.Error(err)
}
if err := runTests(tests, sb); err != nil {
b.Errorf("Failed to run yaml tests %v", err)
}
}
}

View File

@@ -13,7 +13,6 @@ import (
// UpdateRandaoLayers increments the randao layer of the block proposer at the given slot.
func UpdateRandaoLayers(state *pb.BeaconState, slot uint64) (*pb.BeaconState, error) {
vreg := state.ValidatorRegistry
proposerIndex, err := v.BeaconProposerIndex(state, slot)
if err != nil {
return nil, fmt.Errorf("unable to retrieve proposer index %v", err)

View File

@@ -190,7 +190,7 @@ var demoBeaconConfig = &BeaconChainConfig{
SyncPollingInterval: 2 * 4, // Query nodes over the network every 4 slots for sync status.
GenesisTime: time.Now(),
MaxNumLog2Validators: defaultBeaconConfig.MaxNumLog2Validators,
SimulatedBlockRandao: [32]byte{'S', 'I', 'M', 'U', 'L', 'A', 'T', 'E', 'R'},
SimulatedBlockRandao: [32]byte{'S', 'I', 'M', 'U', 'L', 'A', 'T', 'O', 'R'},
}
var defaultShardConfig = &ShardChainConfig{

View File

@@ -67,3 +67,13 @@ func IsIn(a uint32, b []uint32) bool {
}
return false
}
// IsInUint64 returns true if a is in b and False otherwise.
func IsInUint64(a uint64, b []uint64) bool {
for _, v := range b {
if a == v {
return true
}
}
return false
}