diff --git a/.DS_Store b/.DS_Store index c7c4f490..ea3f943d 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/data/goerli_block_header_6397852.json b/data/goerli_block_header_6397852.json new file mode 100644 index 00000000..b62819d3 --- /dev/null +++ b/data/goerli_block_header_6397852.json @@ -0,0 +1,18 @@ +{ + "data": { + "root": "0x602079191479eb9a5dce271b11aa16c5035795f44e3deb4bb02bc6f7f4fd15a6", + "canonical": true, + "header": { + "message": { + "slot": "6397852", + "proposer_index": "116105", + "parent_root": "0xf47faccb99743c068d9cf2e3528892b5c98db26f6c7d63cb77250b5f2d2c1440", + "state_root": "0x5d2a64141d76a2f0c7e3afefcb7e21137bec0061599e3215ef939c8afbfc48a3", + "body_root": "0xdd67808f0e882c262e6f6b888ad01cad9e27cd7eda7c48702d0af80a09ea2d48" + }, + "signature": "0xb9a6ba33eb9bb5f5a39f5870200acb54a5585dd105f350cd80dd4b7a0aa261cc69bb9e48cbd017ba05c40f44eee2b94a0e66a324a63a74b26b8861316af4ddd697beffe72968fefcc7d4bf6175a88c8ecaf0fadde43b9a89c404160b491b9634" + } + }, + "execution_optimistic": false, + "finalized": true +} \ No newline at end of file diff --git a/merkle_util_test.go b/merkle_util_test.go index faa2a605..cd16f3ef 100644 --- a/merkle_util_test.go +++ b/merkle_util_test.go @@ -11,6 +11,7 @@ import ( ssz "github.com/ferranbt/fastssz" "github.com/stretchr/testify/assert" + "github.com/attestantio/go-eth2-client/spec/capella" "github.com/attestantio/go-eth2-client/spec/deneb" "github.com/attestantio/go-eth2-client/spec/phase0" ) @@ -273,24 +274,24 @@ func TestGetHistoricalSummariesBlockRootsProofProofCapellaAgainstDeneb(t *testin //this is not the beacon state of the slot containing the old withdrawal we want to proof but rather // its the state that was merklized to create a historical summary containing the slot that has that withdrawal, ie, 7421952 mod 8192 = 0 - oldBeaconStateJSON, err := parseJSONFile("data/goerli_slot_6397952.json.json") + oldBeaconStateJSON, err := parseJSONFileCapella("data/goerli_slot_6397952.json") if err != nil { - fmt.Println("error parsing oldBeaconStateJSON") + fmt.Println("error parsing oldBeaconStateJSON", err) } var blockHeader phase0.BeaconBlockHeader //blockHeader, err = ExtractBlockHeader("data/goerli_block_header_6397852.json") - blockHeader, err = ExtractBlockHeader("data/deneb_goerli_block_header_7421951.json") + blockHeader, err = ExtractBlockHeader("data/goerli_block_header_6397852.json") if err != nil { fmt.Println("blockHeader.UnmarshalJSON error", err) } var currentBeaconState deneb.BeaconState - var oldBeaconState deneb.BeaconState + var oldBeaconState capella.BeaconState ParseDenebBeaconStateFromJSON(*currentBeaconStateJSON, ¤tBeaconState) - ParseDenebBeaconStateFromJSON(*oldBeaconStateJSON, &oldBeaconState) + ParseCapellaBeaconStateFromJSON(*oldBeaconStateJSON, &oldBeaconState) fmt.Println("currentBeacon state historical summary lentgh is", len(currentBeaconState.HistoricalSummaries)) currentBeaconStateTopLevelRoots, _ := ComputeBeaconStateTopLevelRoots(¤tBeaconState) @@ -300,8 +301,8 @@ func TestGetHistoricalSummariesBlockRootsProofProofCapellaAgainstDeneb(t *testin fmt.Println("error") } - historicalSummaryIndex := uint64(271) - beaconBlockHeaderToVerifyIndex = 8191 //(7421951 mod 8192) + historicalSummaryIndex := uint64(146) + beaconBlockHeaderToVerifyIndex = 8092 //(7421951 mod 8192) beaconBlockHeaderToVerify, err := blockHeader.HashTreeRoot() if err != nil { fmt.Println("error", err) @@ -775,3 +776,22 @@ func parseJSONFile(filePath string) (*beaconStateJSON, error) { actualData := beaconState.Data return &actualData, nil } + +func parseJSONFileCapella(filePath string) (*beaconStateJSONCapella, error) { + data, err := os.ReadFile(filePath) + + if err != nil { + fmt.Println("error with reading file") + return nil, err + } + + var beaconState beaconStateVersionCapella + err = json.Unmarshal(data, &beaconState) + if err != nil { + fmt.Println("error with beaconState JSON unmarshalling") + return nil, err + } + + actualData := beaconState.Data + return &actualData, nil +} diff --git a/proof_utils.go b/proof_utils.go index 6626d141..1b2f21cd 100644 --- a/proof_utils.go +++ b/proof_utils.go @@ -88,10 +88,45 @@ type beaconStateJSON struct { HistoricalSummaries []*capella.HistoricalSummary `json:"historical_summaries"` } +type beaconStateJSONCapella struct { + GenesisTime string `json:"genesis_time"` + GenesisValidatorsRoot string `json:"genesis_validators_root"` + Slot string `json:"slot"` + Fork *phase0.Fork `json:"fork"` + LatestBlockHeader *phase0.BeaconBlockHeader `json:"latest_block_header"` + BlockRoots []string `json:"block_roots"` + StateRoots []string `json:"state_roots"` + HistoricalRoots []string `json:"historical_roots"` + ETH1Data *phase0.ETH1Data `json:"eth1_data"` + ETH1DataVotes []*phase0.ETH1Data `json:"eth1_data_votes"` + ETH1DepositIndex string `json:"eth1_deposit_index"` + Validators []*phase0.Validator `json:"validators"` + Balances []string `json:"balances"` + RANDAOMixes []string `json:"randao_mixes"` + Slashings []string `json:"slashings"` + PreviousEpochParticipation []string `json:"previous_epoch_participation"` + CurrentEpochParticipation []string `json:"current_epoch_participation"` + JustificationBits string `json:"justification_bits"` + PreviousJustifiedCheckpoint *phase0.Checkpoint `json:"previous_justified_checkpoint"` + CurrentJustifiedCheckpoint *phase0.Checkpoint `json:"current_justified_checkpoint"` + FinalizedCheckpoint *phase0.Checkpoint `json:"finalized_checkpoint"` + InactivityScores []string `json:"inactivity_scores"` + CurrentSyncCommittee *altair.SyncCommittee `json:"current_sync_committee"` + NextSyncCommittee *altair.SyncCommittee `json:"next_sync_committee"` + LatestExecutionPayloadHeader *capella.ExecutionPayloadHeader `json:"latest_execution_payload_header"` + NextWithdrawalIndex string `json:"next_withdrawal_index"` + NextWithdrawalValidatorIndex string `json:"next_withdrawal_validator_index"` + HistoricalSummaries []*capella.HistoricalSummary `json:"historical_summaries"` +} + type beaconStateVersion struct { Data beaconStateJSON `json:"data"` } +type beaconStateVersionCapella struct { + Data beaconStateJSONCapella `json:"data"` +} + type InputDataBlockHeader struct { Data struct { Header struct { @@ -232,6 +267,25 @@ func ParseStateJSONFile(filePath string) (*beaconStateJSON, error) { return &actualData, nil } +func ParseCapellaStateJSONFile(filePath string) (*beaconStateJSONCapella, error) { + data, err := ioutil.ReadFile(filePath) + + if err != nil { + log.Debug().Str("file", filePath).Msg("error with reading file") + return nil, err + } + + var beaconState beaconStateVersionCapella + err = json.Unmarshal(data, &beaconState) + if err != nil { + log.Debug().Msg("error with JSON unmarshalling") + return nil, err + } + + actualData := beaconState.Data + return &actualData, nil +} + // nolint:gocyclo func ParseDenebBeaconStateFromJSON(data beaconStateJSON, s *deneb.BeaconState) error { var err error @@ -447,3 +501,218 @@ func ParseDenebBeaconStateFromJSON(data beaconStateJSON, s *deneb.BeaconState) e return nil } + +func ParseCapellaBeaconStateFromJSON(data beaconStateJSONCapella, s *capella.BeaconState) error { + var err error + + if data.GenesisTime == "" { + return errors.New("genesis time missing") + } + if s.GenesisTime, err = strconv.ParseUint(data.GenesisTime, 10, 64); err != nil { + return errors.Wrap(err, "invalid value for genesis time") + } + if data.GenesisValidatorsRoot == "" { + return errors.New("genesis validators root missing") + } + genesisValidatorsRoot, err := hex.DecodeString(strings.TrimPrefix(data.GenesisValidatorsRoot, "0x")) + if err != nil { + return errors.Wrap(err, "invalid value for genesis validators root") + } + if len(genesisValidatorsRoot) != phase0.RootLength { + return fmt.Errorf("incorrect length %d for genesis validators root", len(genesisValidatorsRoot)) + } + copy(s.GenesisValidatorsRoot[:], genesisValidatorsRoot) + if data.Slot == "" { + return errors.New("slot missing") + } + slot, err := strconv.ParseUint(data.Slot, 10, 64) + if err != nil { + return errors.Wrap(err, "invalid value for slot") + } + s.Slot = phase0.Slot(slot) + if data.Fork == nil { + return errors.New("fork missing") + } + s.Fork = data.Fork + if data.LatestBlockHeader == nil { + return errors.New("latest block header missing") + } + s.LatestBlockHeader = data.LatestBlockHeader + if len(data.BlockRoots) == 0 { + return errors.New("block roots missing") + } + s.BlockRoots = make([]phase0.Root, len(data.BlockRoots)) + for i := range data.BlockRoots { + if data.BlockRoots[i] == "" { + return fmt.Errorf("block root %d missing", i) + } + blockRoot, err := hex.DecodeString(strings.TrimPrefix(data.BlockRoots[i], "0x")) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for block root %d", i)) + } + if len(blockRoot) != phase0.RootLength { + return fmt.Errorf("incorrect length %d for block root %d", len(blockRoot), i) + } + copy(s.BlockRoots[i][:], blockRoot) + } + s.StateRoots = make([]phase0.Root, len(data.StateRoots)) + for i := range data.StateRoots { + if data.StateRoots[i] == "" { + return fmt.Errorf("state root %d missing", i) + } + stateRoot, err := hex.DecodeString(strings.TrimPrefix(data.StateRoots[i], "0x")) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for state root %d", i)) + } + if len(stateRoot) != phase0.RootLength { + return fmt.Errorf("incorrect length %d for state root %d", len(stateRoot), i) + } + copy(s.StateRoots[i][:], stateRoot) + } + s.HistoricalRoots = make([]phase0.Root, len(data.HistoricalRoots)) + for i := range data.HistoricalRoots { + if data.HistoricalRoots[i] == "" { + return fmt.Errorf("historical root %d missing", i) + } + historicalRoot, err := hex.DecodeString(strings.TrimPrefix(data.HistoricalRoots[i], "0x")) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for historical root %d", i)) + } + if len(historicalRoot) != phase0.RootLength { + return fmt.Errorf("incorrect length %d for historical root %d", len(historicalRoot), i) + } + copy(s.HistoricalRoots[i][:], historicalRoot) + } + if data.ETH1Data == nil { + return errors.New("eth1 data missing") + } + s.ETH1Data = data.ETH1Data + // ETH1DataVotes can be empty. + s.ETH1DataVotes = data.ETH1DataVotes + if data.Validators == nil { + return errors.New("validators missing") + } + if data.ETH1DepositIndex == "" { + return errors.New("eth1 deposit index missing") + } + if s.ETH1DepositIndex, err = strconv.ParseUint(data.ETH1DepositIndex, 10, 64); err != nil { + return errors.Wrap(err, "invalid value for eth1 deposit index") + } + s.Validators = data.Validators + s.Balances = make([]phase0.Gwei, len(data.Balances)) + for i := range data.Balances { + if data.Balances[i] == "" { + return fmt.Errorf("balance %d missing", i) + } + balance, err := strconv.ParseUint(data.Balances[i], 10, 64) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for balance %d", i)) + } + s.Balances[i] = phase0.Gwei(balance) + } + s.RANDAOMixes = make([]phase0.Root, len(data.RANDAOMixes)) + for i := range data.RANDAOMixes { + if data.RANDAOMixes[i] == "" { + return fmt.Errorf("RANDAO mix %d missing", i) + } + randaoMix, err := hex.DecodeString(strings.TrimPrefix(data.RANDAOMixes[i], "0x")) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for RANDAO mix %d", i)) + } + if len(randaoMix) != phase0.RootLength { + return fmt.Errorf("incorrect length %d for RANDAO mix %d", len(randaoMix), i) + } + copy(s.RANDAOMixes[i][:], randaoMix) + } + s.Slashings = make([]phase0.Gwei, len(data.Slashings)) + for i := range data.Slashings { + if data.Slashings[i] == "" { + return fmt.Errorf("slashing %d missing", i) + } + slashings, err := strconv.ParseUint(data.Slashings[i], 10, 64) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for slashing %d", i)) + } + s.Slashings[i] = phase0.Gwei(slashings) + } + s.PreviousEpochParticipation = make([]altair.ParticipationFlags, len(data.PreviousEpochParticipation)) + for i := range data.PreviousEpochParticipation { + if data.PreviousEpochParticipation[i] == "" { + return fmt.Errorf("previous epoch attestation %d missing", i) + } + previousEpochAttestation, err := strconv.ParseUint(data.PreviousEpochParticipation[i], 10, 8) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for previous epoch attestation %d", i)) + } + s.PreviousEpochParticipation[i] = altair.ParticipationFlags(previousEpochAttestation) + } + s.CurrentEpochParticipation = make([]altair.ParticipationFlags, len(data.CurrentEpochParticipation)) + for i := range data.CurrentEpochParticipation { + if data.CurrentEpochParticipation[i] == "" { + return fmt.Errorf("current epoch attestation %d missing", i) + } + currentEpochAttestation, err := strconv.ParseUint(data.CurrentEpochParticipation[i], 10, 8) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for current epoch attestation %d", i)) + } + s.CurrentEpochParticipation[i] = altair.ParticipationFlags(currentEpochAttestation) + } + if data.JustificationBits == "" { + return errors.New("justification bits missing") + } + if s.JustificationBits, err = hex.DecodeString(strings.TrimPrefix(data.JustificationBits, "0x")); err != nil { + return errors.Wrap(err, "invalid value for justification bits") + } + if data.PreviousJustifiedCheckpoint == nil { + return errors.New("previous justified checkpoint missing") + } + s.PreviousJustifiedCheckpoint = data.PreviousJustifiedCheckpoint + if data.CurrentJustifiedCheckpoint == nil { + return errors.New("current justified checkpoint missing") + } + s.CurrentJustifiedCheckpoint = data.CurrentJustifiedCheckpoint + if data.FinalizedCheckpoint == nil { + return errors.New("finalized checkpoint missing") + } + s.FinalizedCheckpoint = data.FinalizedCheckpoint + s.InactivityScores = make([]uint64, len(data.InactivityScores)) + for i := range data.InactivityScores { + if data.InactivityScores[i] == "" { + return fmt.Errorf("inactivity score %d missing", i) + } + if s.InactivityScores[i], err = strconv.ParseUint(data.InactivityScores[i], 10, 64); err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for inactivity score %d", i)) + } + } + if data.CurrentSyncCommittee == nil { + return errors.New("current sync committee missing") + } + s.CurrentSyncCommittee = data.CurrentSyncCommittee + if data.NextSyncCommittee == nil { + return errors.New("next sync committee missing") + } + s.NextSyncCommittee = data.NextSyncCommittee + s.LatestExecutionPayloadHeader = data.LatestExecutionPayloadHeader + if data.NextWithdrawalIndex == "" { + return errors.New("next withdrawal index missing") + } + nextWithdrawalIndex, err := strconv.ParseUint(data.NextWithdrawalIndex, 10, 64) + if err != nil { + return errors.Wrap(err, "invalid value for next withdrawal index") + } + s.NextWithdrawalIndex = capella.WithdrawalIndex(nextWithdrawalIndex) + if data.NextWithdrawalValidatorIndex == "" { + return errors.New("next validator validator index missing") + } + nextWithdrawalValidatorIndex, err := strconv.ParseUint(data.NextWithdrawalValidatorIndex, 10, 64) + if err != nil { + return errors.Wrap(err, "invalid value for next withdrawal validator index") + } + s.NextWithdrawalValidatorIndex = phase0.ValidatorIndex(nextWithdrawalValidatorIndex) + if data.HistoricalSummaries == nil { + return errors.New("historical summaries missing") + } + s.HistoricalSummaries = data.HistoricalSummaries + + return nil +}