diff --git a/beacon-chain/core/blocks/BUILD.bazel b/beacon-chain/core/blocks/BUILD.bazel index 573ea4d14fc2..7cdf9b6fe3f8 100644 --- a/beacon-chain/core/blocks/BUILD.bazel +++ b/beacon-chain/core/blocks/BUILD.bazel @@ -31,6 +31,7 @@ go_library( "//shared/attestationutil:go_default_library", "//shared/bls:go_default_library", "//shared/bytesutil:go_default_library", + "//shared/depositutil:go_default_library", "//shared/hashutil:go_default_library", "//shared/mathutil:go_default_library", "//shared/params:go_default_library", diff --git a/beacon-chain/core/blocks/deposit.go b/beacon-chain/core/blocks/deposit.go index 2152b8d83dc2..500dbeab5f83 100644 --- a/beacon-chain/core/blocks/deposit.go +++ b/beacon-chain/core/blocks/deposit.go @@ -12,6 +12,7 @@ import ( pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/bytesutil" + "github.com/prysmaticlabs/prysm/shared/depositutil" "github.com/prysmaticlabs/prysm/shared/mathutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/trieutil" @@ -218,30 +219,7 @@ func verifyDeposit(beaconState *stateTrie.BeaconState, deposit *ethpb.Deposit) e // Deprecated: This method uses deprecated ssz.SigningRoot. func verifyDepositDataSigningRoot(obj *ethpb.Deposit_Data, pub []byte, signature []byte, domain []byte) error { - publicKey, err := bls.PublicKeyFromBytes(pub) - if err != nil { - return errors.Wrap(err, "could not convert bytes to public key") - } - sig, err := bls.SignatureFromBytes(signature) - if err != nil { - return errors.Wrap(err, "could not convert bytes to signature") - } - root, err := ssz.SigningRoot(obj) - if err != nil { - return errors.Wrap(err, "could not get signing root") - } - signingData := &pb.SigningData{ - ObjectRoot: root[:], - Domain: domain, - } - ctrRoot, err := signingData.HashTreeRoot() - if err != nil { - return errors.Wrap(err, "could not get container root") - } - if !sig.Verify(publicKey, ctrRoot[:]) { - return helpers.ErrSigFailedToVerify - } - return nil + return depositutil.VerifyDepositSignature(obj, domain) } func verifyDepositDataWithDomain(ctx context.Context, deps []*ethpb.Deposit, domain []byte) error { diff --git a/beacon-chain/rpc/validator/BUILD.bazel b/beacon-chain/rpc/validator/BUILD.bazel index 6fd646e3c4f8..91911e14b63f 100644 --- a/beacon-chain/rpc/validator/BUILD.bazel +++ b/beacon-chain/rpc/validator/BUILD.bazel @@ -40,6 +40,7 @@ go_library( "//proto/beacon/p2p/v1:go_default_library", "//shared/bls:go_default_library", "//shared/bytesutil:go_default_library", + "//shared/depositutil:go_default_library", "//shared/featureconfig:go_default_library", "//shared/hashutil:go_default_library", "//shared/params:go_default_library", diff --git a/beacon-chain/rpc/validator/status.go b/beacon-chain/rpc/validator/status.go index ea80a38ce860..fc59e3cd817f 100644 --- a/beacon-chain/rpc/validator/status.go +++ b/beacon-chain/rpc/validator/status.go @@ -10,6 +10,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state" "github.com/prysmaticlabs/prysm/shared/bytesutil" + "github.com/prysmaticlabs/prysm/shared/depositutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/traceutil" "go.opencensus.io/trace" @@ -159,11 +160,24 @@ func (vs *Server) validatorStatus( log.Warn("Not connected to ETH1. Cannot determine validator ETH1 deposit block number") return resp, nonExistentIndex } - _, eth1BlockNumBigInt := vs.DepositFetcher.DepositByPubkey(ctx, pubKey) + deposit, eth1BlockNumBigInt := vs.DepositFetcher.DepositByPubkey(ctx, pubKey) if eth1BlockNumBigInt == nil { // No deposit found in ETH1. return resp, nonExistentIndex } - + domain, err := helpers.ComputeDomain( + params.BeaconConfig().DomainDeposit, + nil, /*forkVersion*/ + nil, /*genesisValidatorsRoot*/ + ) + if err != nil { + log.Warn("Could not compute domain") + return resp, nonExistentIndex + } + if err := depositutil.VerifyDepositSignature(deposit.Data, domain); err != nil { + resp.Status = ethpb.ValidatorStatus_INVALID + log.Warn("Invalid Eth1 deposit") + return resp, nonExistentIndex + } // Mark a validator as DEPOSITED if their deposit is visible. resp.Status = ethpb.ValidatorStatus_DEPOSITED diff --git a/beacon-chain/rpc/validator/status_test.go b/beacon-chain/rpc/validator/status_test.go index 59fec300c8cd..84d9f17f45fe 100644 --- a/beacon-chain/rpc/validator/status_test.go +++ b/beacon-chain/rpc/validator/status_test.go @@ -27,16 +27,12 @@ import ( func TestValidatorStatus_DepositedEth1(t *testing.T) { db, _ := dbutil.SetupDB(t) ctx := context.Background() - - pubKey1 := pubKey(1) - depData := ðpb.Deposit_Data{ - PublicKey: pubKey1, - Signature: []byte("hi"), - WithdrawalCredentials: []byte("hey"), - } - deposit := ðpb.Deposit{ - Data: depData, + deposits, _, err := testutil.DeterministicDepositsAndKeys(1) + if err != nil { + t.Fatalf("Could not generate deposits and keys: %v", err) } + deposit := deposits[0] + pubKey1 := deposit.Data.PublicKey depositTrie, err := trieutil.NewTrie(int(params.BeaconConfig().DepositContractTreeDepth)) require.NoError(t, err, "Could not setup deposit trie") depositCache, err := depositcache.NewDepositCache() @@ -471,7 +467,8 @@ func TestActivationStatus_OK(t *testing.T) { db, _ := dbutil.SetupDB(t) ctx := context.Background() - pubKeys := [][]byte{pubKey(1), pubKey(2), pubKey(3), pubKey(4)} + deposits, _, err := testutil.DeterministicDepositsAndKeys(4) + pubKeys := [][]byte{deposits[0].Data.PublicKey, deposits[1].Data.PublicKey, deposits[2].Data.PublicKey, deposits[3].Data.PublicKey} stateObj, err := stateTrie.InitializeFromProtoUnsafe(&pbp2p.BeaconState{ Slot: 4000, Validators: []*ethpb.Validator{ @@ -495,32 +492,15 @@ func TestActivationStatus_OK(t *testing.T) { block := testutil.NewBeaconBlock() genesisRoot, err := stateutil.BlockRoot(block.Block) require.NoError(t, err, "Could not get signing root") - depData := ðpb.Deposit_Data{ - PublicKey: pubKey(1), - Signature: []byte("hi"), - WithdrawalCredentials: []byte("hey"), - Amount: 10, - } - - dep := ðpb.Deposit{ - Data: depData, - } + dep := deposits[0] depositTrie, err := trieutil.NewTrie(int(params.BeaconConfig().DepositContractTreeDepth)) require.NoError(t, err, "Could not setup deposit trie") depositCache, err := depositcache.NewDepositCache() require.NoError(t, err) depositCache.InsertDeposit(ctx, dep, 10 /*blockNum*/, 0, depositTrie.Root()) - depData = ðpb.Deposit_Data{ - PublicKey: pubKey(3), - Signature: []byte("hi"), - WithdrawalCredentials: []byte("hey"), - Amount: 10, - } - dep = ðpb.Deposit{ - Data: depData, - } + dep = deposits[2] depositTrie.Insert(dep.Data.Signature, 15) depositCache.InsertDeposit(context.Background(), dep, 0, 0, depositTrie.Root()) @@ -796,7 +776,8 @@ func TestMultipleValidatorStatus_Pubkeys(t *testing.T) { db, _ := dbutil.SetupDB(t) ctx := context.Background() - pubKeys := [][]byte{pubKey(1), pubKey(2), pubKey(3), pubKey(4)} + deposits, _, err := testutil.DeterministicDepositsAndKeys(4) + pubKeys := [][]byte{deposits[0].Data.PublicKey, deposits[1].Data.PublicKey, deposits[2].Data.PublicKey, deposits[3].Data.PublicKey} stateObj, err := stateTrie.InitializeFromProtoUnsafe(&pbp2p.BeaconState{ Slot: 4000, Validators: []*ethpb.Validator{ @@ -820,32 +801,14 @@ func TestMultipleValidatorStatus_Pubkeys(t *testing.T) { block := testutil.NewBeaconBlock() genesisRoot, err := stateutil.BlockRoot(block.Block) require.NoError(t, err, "Could not get signing root") - depData := ðpb.Deposit_Data{ - PublicKey: pubKey(1), - Signature: []byte("hi"), - WithdrawalCredentials: []byte("hey"), - Amount: 10, - } - - dep := ðpb.Deposit{ - Data: depData, - } + dep := deposits[0] depositTrie, err := trieutil.NewTrie(int(params.BeaconConfig().DepositContractTreeDepth)) require.NoError(t, err, "Could not setup deposit trie") depositCache, err := depositcache.NewDepositCache() require.NoError(t, err) depositCache.InsertDeposit(ctx, dep, 10 /*blockNum*/, 0, depositTrie.Root()) - depData = ðpb.Deposit_Data{ - PublicKey: pubKey(3), - Signature: []byte("hi"), - WithdrawalCredentials: []byte("hey"), - Amount: 10, - } - - dep = ðpb.Deposit{ - Data: depData, - } + dep = deposits[2] depositTrie.Insert(dep.Data.Signature, 15) depositCache.InsertDeposit(context.Background(), dep, 0, 0, depositTrie.Root()) @@ -972,3 +935,44 @@ func TestMultipleValidatorStatus_Indices(t *testing.T) { } } } + +func TestValidatorStatus_Invalid(t *testing.T) { + db, _ := dbutil.SetupDB(t) + ctx := context.Background() + deposits, _, err := testutil.DeterministicDepositsAndKeys(1) + if err != nil { + t.Fatalf("Could not generate deposits and keys: %v", err) + } + deposit := deposits[0] + pubKey1 := deposit.Data.PublicKey + deposit.Data.Signature = deposit.Data.Signature[1:] + depositTrie, err := trieutil.NewTrie(int(params.BeaconConfig().DepositContractTreeDepth)) + require.NoError(t, err, "Could not setup deposit trie") + depositCache, err := depositcache.NewDepositCache() + require.NoError(t, err) + + depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, depositTrie.Root()) + height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix() + p := &mockPOW.POWChain{ + TimesByHeight: map[int]uint64{ + 0: uint64(height), + }, + } + stateObj, err := stateTrie.InitializeFromProtoUnsafe(&pbp2p.BeaconState{}) + require.NoError(t, err) + vs := &Server{ + BeaconDB: db, + DepositFetcher: depositCache, + BlockFetcher: p, + HeadFetcher: &mockChain.ChainService{ + State: stateObj, + }, + Eth1InfoFetcher: p, + } + req := ðpb.ValidatorStatusRequest{ + PublicKey: pubKey1, + } + resp, err := vs.ValidatorStatus(context.Background(), req) + require.NoError(t, err, "Could not get validator status") + assert.Equal(t, ethpb.ValidatorStatus_INVALID, resp.Status) +} diff --git a/proto/beacon/p2p/v1/types.pb.go b/proto/beacon/p2p/v1/types.pb.go index 713d0969bac4..af2954e0b3de 100755 --- a/proto/beacon/p2p/v1/types.pb.go +++ b/proto/beacon/p2p/v1/types.pb.go @@ -366,61 +366,6 @@ func (m *PendingAttestation) GetProposerIndex() uint64 { return 0 } -type ValidatorLatestVote struct { - Epoch uint64 `protobuf:"varint,1,opt,name=epoch,proto3" json:"epoch,omitempty"` - Root []byte `protobuf:"bytes,2,opt,name=root,proto3" json:"root,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ValidatorLatestVote) Reset() { *m = ValidatorLatestVote{} } -func (m *ValidatorLatestVote) String() string { return proto.CompactTextString(m) } -func (*ValidatorLatestVote) ProtoMessage() {} -func (*ValidatorLatestVote) Descriptor() ([]byte, []int) { - return fileDescriptor_e719e7d82cfa7b0d, []int{3} -} -func (m *ValidatorLatestVote) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ValidatorLatestVote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ValidatorLatestVote.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *ValidatorLatestVote) XXX_Merge(src proto.Message) { - xxx_messageInfo_ValidatorLatestVote.Merge(m, src) -} -func (m *ValidatorLatestVote) XXX_Size() int { - return m.Size() -} -func (m *ValidatorLatestVote) XXX_DiscardUnknown() { - xxx_messageInfo_ValidatorLatestVote.DiscardUnknown(m) -} - -var xxx_messageInfo_ValidatorLatestVote proto.InternalMessageInfo - -func (m *ValidatorLatestVote) GetEpoch() uint64 { - if m != nil { - return m.Epoch - } - return 0 -} - -func (m *ValidatorLatestVote) GetRoot() []byte { - if m != nil { - return m.Root - } - return nil -} - type HistoricalBatch struct { BlockRoots [][]byte `protobuf:"bytes,1,rep,name=block_roots,json=blockRoots,proto3" json:"block_roots,omitempty" ssz-size:"8192,32"` StateRoots [][]byte `protobuf:"bytes,2,rep,name=state_roots,json=stateRoots,proto3" json:"state_roots,omitempty" ssz-size:"8192,32"` @@ -433,7 +378,7 @@ func (m *HistoricalBatch) Reset() { *m = HistoricalBatch{} } func (m *HistoricalBatch) String() string { return proto.CompactTextString(m) } func (*HistoricalBatch) ProtoMessage() {} func (*HistoricalBatch) Descriptor() ([]byte, []int) { - return fileDescriptor_e719e7d82cfa7b0d, []int{4} + return fileDescriptor_e719e7d82cfa7b0d, []int{3} } func (m *HistoricalBatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -488,7 +433,7 @@ func (m *StateSummary) Reset() { *m = StateSummary{} } func (m *StateSummary) String() string { return proto.CompactTextString(m) } func (*StateSummary) ProtoMessage() {} func (*StateSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_e719e7d82cfa7b0d, []int{5} + return fileDescriptor_e719e7d82cfa7b0d, []int{4} } func (m *StateSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -543,7 +488,7 @@ func (m *SigningData) Reset() { *m = SigningData{} } func (m *SigningData) String() string { return proto.CompactTextString(m) } func (*SigningData) ProtoMessage() {} func (*SigningData) Descriptor() ([]byte, []int) { - return fileDescriptor_e719e7d82cfa7b0d, []int{6} + return fileDescriptor_e719e7d82cfa7b0d, []int{5} } func (m *SigningData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -598,7 +543,7 @@ func (m *ForkData) Reset() { *m = ForkData{} } func (m *ForkData) String() string { return proto.CompactTextString(m) } func (*ForkData) ProtoMessage() {} func (*ForkData) Descriptor() ([]byte, []int) { - return fileDescriptor_e719e7d82cfa7b0d, []int{7} + return fileDescriptor_e719e7d82cfa7b0d, []int{6} } func (m *ForkData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -653,7 +598,7 @@ func (m *SignedAggregateAndProof) Reset() { *m = SignedAggregateAndProof func (m *SignedAggregateAndProof) String() string { return proto.CompactTextString(m) } func (*SignedAggregateAndProof) ProtoMessage() {} func (*SignedAggregateAndProof) Descriptor() ([]byte, []int) { - return fileDescriptor_e719e7d82cfa7b0d, []int{8} + return fileDescriptor_e719e7d82cfa7b0d, []int{7} } func (m *SignedAggregateAndProof) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -700,7 +645,6 @@ func init() { proto.RegisterType((*BeaconState)(nil), "ethereum.beacon.p2p.v1.BeaconState") proto.RegisterType((*Fork)(nil), "ethereum.beacon.p2p.v1.Fork") proto.RegisterType((*PendingAttestation)(nil), "ethereum.beacon.p2p.v1.PendingAttestation") - proto.RegisterType((*ValidatorLatestVote)(nil), "ethereum.beacon.p2p.v1.ValidatorLatestVote") proto.RegisterType((*HistoricalBatch)(nil), "ethereum.beacon.p2p.v1.HistoricalBatch") proto.RegisterType((*StateSummary)(nil), "ethereum.beacon.p2p.v1.StateSummary") proto.RegisterType((*SigningData)(nil), "ethereum.beacon.p2p.v1.SigningData") @@ -711,85 +655,84 @@ func init() { func init() { proto.RegisterFile("proto/beacon/p2p/v1/types.proto", fileDescriptor_e719e7d82cfa7b0d) } var fileDescriptor_e719e7d82cfa7b0d = []byte{ - // 1243 bytes of a gzipped FileDescriptorProto + // 1223 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcd, 0x8f, 0xdb, 0xc4, 0x1b, 0x96, 0xb7, 0xf9, 0xf5, 0x63, 0x92, 0x6e, 0xb6, 0xb3, 0xfd, 0x35, 0xa6, 0x2d, 0xeb, 0xc5, - 0x12, 0x6d, 0x41, 0xdd, 0xa4, 0xf6, 0x6e, 0x93, 0xdd, 0x56, 0xb4, 0x6a, 0xda, 0xa2, 0xb6, 0xa2, + 0x12, 0x6d, 0x41, 0xdd, 0xa4, 0xf6, 0x6e, 0x93, 0xdd, 0x56, 0x14, 0x35, 0x6d, 0x51, 0x5b, 0xa9, 0x52, 0xe5, 0x42, 0x25, 0x24, 0x84, 0x35, 0xb1, 0x27, 0xf6, 0x74, 0x6d, 0x8f, 0xe5, 0x99, 0x44, - 0xdd, 0x4a, 0x88, 0x03, 0x27, 0x4e, 0x70, 0xe0, 0xc2, 0x11, 0xfe, 0x0b, 0xe0, 0xc4, 0xc7, 0x81, - 0x23, 0x5f, 0x97, 0x72, 0x88, 0xd0, 0xde, 0xf8, 0xb8, 0x90, 0x23, 0x27, 0x34, 0xe3, 0xaf, 0x84, - 0x26, 0x10, 0x24, 0x6e, 0x9e, 0x99, 0xe7, 0x79, 0xde, 0x77, 0xde, 0xf7, 0xf5, 0x3b, 0x2f, 0xd0, - 0xe2, 0x84, 0x72, 0xda, 0xea, 0x61, 0xe4, 0xd0, 0xa8, 0x15, 0x9b, 0x71, 0x6b, 0x68, 0xb4, 0xf8, - 0x5e, 0x8c, 0x59, 0x53, 0x9e, 0xc0, 0x13, 0x98, 0xfb, 0x38, 0xc1, 0x83, 0xb0, 0x99, 0x62, 0x9a, - 0xb1, 0x19, 0x37, 0x87, 0xc6, 0xc9, 0x35, 0xcc, 0xfd, 0xd6, 0xd0, 0x40, 0x41, 0xec, 0x23, 0xa3, - 0x85, 0x38, 0xc7, 0x8c, 0x23, 0x4e, 0x04, 0x40, 0xf0, 0x4e, 0x6a, 0x53, 0xe7, 0x29, 0xd7, 0xee, - 0x05, 0xd4, 0xd9, 0xcd, 0x00, 0xa7, 0xa7, 0x00, 0x43, 0x14, 0x10, 0x17, 0x71, 0x9a, 0x64, 0xa7, - 0x1b, 0x1e, 0xe1, 0xfe, 0xa0, 0xd7, 0x74, 0x68, 0xd8, 0xf2, 0xa8, 0x47, 0x5b, 0x72, 0xbb, 0x37, - 0xe8, 0xcb, 0x55, 0xea, 0xb4, 0xf8, 0x4a, 0xe1, 0xfa, 0x93, 0x1a, 0xa8, 0x76, 0xa5, 0x8d, 0xfb, - 0x1c, 0x71, 0x0c, 0x75, 0x50, 0xf3, 0x70, 0x84, 0x19, 0x61, 0x36, 0x27, 0x21, 0x56, 0x7f, 0x3e, - 0xb4, 0xae, 0x9c, 0xab, 0x58, 0xd5, 0x6c, 0xf3, 0x55, 0x12, 0x62, 0x78, 0x07, 0x34, 0x72, 0x4c, - 0x61, 0x9d, 0xd9, 0x09, 0xa5, 0x5c, 0xfd, 0x45, 0xc0, 0x6b, 0xdd, 0x63, 0xe3, 0x91, 0x76, 0x94, - 0xb1, 0xc7, 0x1b, 0x8c, 0x3c, 0xc6, 0x97, 0xf4, 0x4d, 0x53, 0xb7, 0xfe, 0x9f, 0x51, 0x1e, 0x14, - 0x0c, 0x8b, 0x52, 0x0e, 0x57, 0x41, 0x85, 0x05, 0x94, 0xab, 0xbf, 0xa6, 0x76, 0xe4, 0x02, 0x1a, - 0xa0, 0xd2, 0xa7, 0xc9, 0xae, 0xfa, 0x9b, 0xd8, 0xac, 0x9a, 0xa7, 0x9b, 0xb3, 0x43, 0xd9, 0x7c, - 0x99, 0x26, 0xbb, 0x96, 0x84, 0xc2, 0xd7, 0xc1, 0x6a, 0x80, 0x44, 0x28, 0xd3, 0x50, 0xd9, 0x3e, - 0x46, 0x2e, 0x4e, 0xd4, 0x6f, 0xeb, 0x52, 0xe1, 0x5c, 0xa9, 0x80, 0xb9, 0xdf, 0xcc, 0x83, 0xd7, - 0x4c, 0x6f, 0xde, 0x15, 0x8c, 0x5b, 0x92, 0x60, 0x1d, 0x4b, 0x55, 0x26, 0xb6, 0xe0, 0x36, 0xa8, - 0xa6, 0x9a, 0xe2, 0x86, 0x4c, 0xfd, 0xae, 0xbe, 0x7e, 0xe0, 0x5c, 0xad, 0x7b, 0x62, 0x3c, 0xd2, - 0x60, 0x79, 0xc5, 0x6d, 0x63, 0xc7, 0x3c, 0x2f, 0xee, 0x09, 0x24, 0x56, 0xdc, 0x8d, 0x09, 0xa6, - 0xc8, 0x2d, 0xce, 0x98, 0xdf, 0xff, 0x03, 0x53, 0x62, 0x53, 0xa6, 0x05, 0x56, 0x7c, 0xc2, 0x38, - 0x4d, 0x88, 0x83, 0x82, 0x8c, 0xfe, 0x43, 0x4a, 0x3f, 0x33, 0x1e, 0x69, 0x7a, 0x49, 0xbf, 0x2a, - 0xb8, 0xeb, 0x62, 0x1d, 0xa2, 0x47, 0x97, 0x74, 0xa3, 0xdd, 0xe9, 0x74, 0x4c, 0xa3, 0xad, 0x5b, - 0xf5, 0x52, 0x20, 0xd5, 0x7c, 0x09, 0x1c, 0xc1, 0xdc, 0x37, 0x6c, 0x17, 0x71, 0xa4, 0x7e, 0xd2, - 0x90, 0x81, 0xd1, 0xe6, 0x04, 0xe6, 0x26, 0xf7, 0x8d, 0x1b, 0x88, 0x23, 0xeb, 0x30, 0xce, 0xbe, - 0xe0, 0x1b, 0xa0, 0x5e, 0xd0, 0xed, 0x21, 0xe5, 0x98, 0xa9, 0x9f, 0x36, 0xd6, 0x0f, 0x2c, 0x20, - 0xd2, 0x85, 0xe3, 0x91, 0xb6, 0x5c, 0xba, 0x78, 0xc1, 0xdc, 0xd2, 0xad, 0xa3, 0xb9, 0xf0, 0x03, - 0x21, 0x05, 0x37, 0x00, 0x4c, 0xd5, 0x71, 0x4c, 0x19, 0xe1, 0x36, 0x89, 0x5c, 0xfc, 0x48, 0xfd, - 0xac, 0x21, 0xab, 0x62, 0x45, 0x62, 0xd3, 0x93, 0xdb, 0xe2, 0x00, 0xbe, 0x09, 0x40, 0x59, 0x7a, - 0xea, 0x47, 0x9a, 0xf4, 0x63, 0x7d, 0x8e, 0x1f, 0x45, 0xc9, 0x75, 0x4f, 0x8d, 0x47, 0x5a, 0x63, - 0xc2, 0x91, 0x9d, 0x9d, 0x8b, 0x86, 0xd1, 0x36, 0x3b, 0x9d, 0x4e, 0x5b, 0xb7, 0x26, 0x14, 0xe1, - 0x36, 0x38, 0xdc, 0x43, 0x01, 0x8a, 0x1c, 0xcc, 0xd4, 0x8f, 0x85, 0x7a, 0xe5, 0xef, 0xb9, 0x05, - 0x1a, 0x5e, 0x06, 0xb5, 0x04, 0x45, 0x2e, 0xa2, 0x76, 0x48, 0x1e, 0x61, 0xa6, 0xbe, 0x7b, 0x56, - 0x66, 0xad, 0x31, 0x1e, 0x69, 0xab, 0x65, 0xd6, 0xda, 0x17, 0x2f, 0x6e, 0xb6, 0x65, 0xd6, 0xab, - 0x29, 0xfa, 0xae, 0x00, 0x43, 0x13, 0x1c, 0x61, 0x01, 0x62, 0x3e, 0x89, 0x3c, 0xa6, 0xfe, 0xde, - 0x94, 0x76, 0x57, 0xc7, 0x23, 0xad, 0x3e, 0x5d, 0x2e, 0xba, 0x55, 0xc2, 0xe0, 0xdb, 0xe0, 0x54, - 0x9c, 0xe0, 0x21, 0xa1, 0x03, 0x66, 0xe3, 0x98, 0x3a, 0xbe, 0x3d, 0xd1, 0x53, 0x98, 0xfa, 0xa4, - 0x2d, 0x63, 0xf3, 0xe2, 0xbc, 0x7f, 0xe8, 0x1e, 0x8e, 0x5c, 0x12, 0x79, 0xd7, 0x4a, 0xce, 0x5f, - 0xd2, 0xb5, 0x75, 0x61, 0xa7, 0xad, 0x5b, 0xcf, 0xe4, 0x36, 0x6e, 0x0a, 0x13, 0x13, 0x68, 0x06, - 0xdf, 0x02, 0x27, 0x9d, 0x41, 0x92, 0xe0, 0x88, 0xcf, 0xb2, 0xff, 0xe3, 0x7f, 0x63, 0x5f, 0xcd, - 0x4c, 0x3c, 0x6d, 0x9e, 0x01, 0xf8, 0x70, 0xc0, 0x38, 0xe9, 0x13, 0x47, 0xee, 0xd8, 0x3d, 0xc2, - 0x99, 0xfa, 0xf9, 0x15, 0xd9, 0x88, 0xae, 0x8f, 0x47, 0x5a, 0xad, 0x0c, 0x9e, 0xa1, 0xff, 0x31, - 0xd2, 0x5a, 0x13, 0x1d, 0x32, 0x4e, 0xf6, 0x58, 0x88, 0x38, 0x71, 0x02, 0xd4, 0x63, 0x2d, 0x8f, - 0x6e, 0xf4, 0x08, 0xef, 0x13, 0x1c, 0xb8, 0xcd, 0x2e, 0xe1, 0x43, 0xec, 0x70, 0x9a, 0x6c, 0x59, - 0xc7, 0xa6, 0xf4, 0xbb, 0x84, 0x33, 0xd8, 0x07, 0xcf, 0x16, 0x41, 0xcf, 0x4e, 0xb1, 0x6b, 0x3b, - 0x3e, 0x76, 0x76, 0x63, 0x4a, 0x22, 0xae, 0x7e, 0x71, 0x45, 0xfe, 0x5f, 0xcf, 0xcd, 0x29, 0xc9, - 0xeb, 0x05, 0xd2, 0x2a, 0xb2, 0x77, 0x27, 0xd7, 0x29, 0x0f, 0xa1, 0x0b, 0x4e, 0xe7, 0xb1, 0x9d, - 0x69, 0xe6, 0xcb, 0x85, 0xcd, 0xe4, 0x39, 0x9a, 0x65, 0xe5, 0x35, 0x70, 0xbc, 0x4f, 0x22, 0x14, - 0x90, 0xc7, 0xd3, 0xea, 0x5f, 0x2d, 0xac, 0xbe, 0x5a, 0xf0, 0xcb, 0x4d, 0xfd, 0x03, 0x05, 0x54, - 0x44, 0x8b, 0x86, 0x97, 0xc1, 0x4a, 0x11, 0xad, 0x21, 0x4e, 0x18, 0xa1, 0x91, 0xaa, 0xc8, 0xfc, - 0xac, 0x4c, 0xe7, 0x67, 0x4b, 0xb7, 0xea, 0x39, 0xf2, 0x41, 0x0a, 0x84, 0x3b, 0xa0, 0x9e, 0x87, - 0x20, 0xe7, 0x2e, 0xcd, 0xe1, 0x2e, 0x67, 0xc0, 0x9c, 0x7a, 0x1c, 0xfc, 0x4f, 0x56, 0xa4, 0x7a, - 0x40, 0xb6, 0x91, 0x74, 0xa1, 0xbf, 0xb7, 0x04, 0xe0, 0xd3, 0x55, 0x07, 0x43, 0xb0, 0x82, 0x3c, - 0x2f, 0xc1, 0xde, 0x44, 0x15, 0xa5, 0x4e, 0x76, 0xa7, 0xea, 0xd1, 0xbc, 0xb0, 0xb5, 0x2d, 0xca, - 0xe8, 0xfc, 0xa2, 0x65, 0x14, 0x10, 0xc6, 0xad, 0xfa, 0x84, 0xb6, 0xac, 0xa0, 0x4b, 0xa0, 0x22, - 0x1b, 0xf1, 0x92, 0x0c, 0xf1, 0x99, 0x39, 0x21, 0x9e, 0x70, 0x50, 0xb6, 0x63, 0xc9, 0x81, 0x67, - 0x41, 0x9d, 0x44, 0x4e, 0x30, 0x10, 0x97, 0xb4, 0x5d, 0x1c, 0xa0, 0xbd, 0xec, 0x86, 0xcb, 0xc5, - 0xf6, 0x0d, 0xb1, 0x0b, 0x9f, 0x07, 0xcb, 0x71, 0x42, 0x63, 0xca, 0x70, 0x92, 0x75, 0xd4, 0x8a, - 0xc4, 0x1d, 0xcd, 0x77, 0x65, 0x37, 0xd5, 0xaf, 0x82, 0xd5, 0xa2, 0x47, 0xbe, 0x22, 0xdf, 0x3f, - 0xd1, 0x94, 0xcb, 0xf0, 0x29, 0x13, 0xe1, 0x83, 0x10, 0x54, 0xe4, 0x53, 0x2f, 0x93, 0x60, 0xc9, - 0x6f, 0xfd, 0x1d, 0x05, 0xd4, 0x6f, 0x15, 0xcf, 0x4d, 0x17, 0x71, 0xc7, 0x87, 0x9d, 0xe9, 0x67, - 0x53, 0x59, 0xf8, 0xd5, 0xec, 0x4c, 0xbf, 0x9a, 0x4b, 0x8b, 0x3e, 0x9a, 0x7a, 0x1b, 0xd4, 0xe4, - 0x10, 0x73, 0x7f, 0x10, 0x86, 0x28, 0xd9, 0x13, 0x9e, 0xca, 0xd9, 0x42, 0x99, 0x18, 0x2d, 0x66, - 0x79, 0x1f, 0x80, 0xea, 0x7d, 0xe2, 0x45, 0x24, 0xf2, 0xe4, 0x43, 0x67, 0x82, 0x2a, 0xed, 0x3d, - 0xc4, 0x0e, 0x4f, 0x47, 0x1a, 0x65, 0xde, 0x44, 0x03, 0x52, 0x94, 0x1c, 0x63, 0x5e, 0x00, 0x07, - 0x5d, 0x1a, 0x22, 0x92, 0xd7, 0xe6, 0x0c, 0x78, 0x06, 0xd0, 0xdf, 0x57, 0xc0, 0x61, 0xf1, 0x57, - 0x48, 0x5b, 0x33, 0x8a, 0xbb, 0xb2, 0x60, 0x71, 0xdf, 0x9e, 0x3f, 0x85, 0x2d, 0xfd, 0xbb, 0x21, - 0x4c, 0xff, 0x50, 0x01, 0x0d, 0x11, 0x01, 0xec, 0x5e, 0xcb, 0xaa, 0x14, 0x5f, 0x8b, 0xdc, 0x7b, - 0x09, 0xa5, 0x7d, 0x78, 0x17, 0x1c, 0x0a, 0x31, 0x63, 0xc8, 0xc3, 0x32, 0x12, 0x55, 0x73, 0x73, - 0x5e, 0xa9, 0x16, 0xd4, 0xb2, 0x66, 0x73, 0x15, 0x2b, 0xd7, 0x80, 0x2d, 0x70, 0x84, 0x11, 0x2f, - 0x42, 0x7c, 0x90, 0xe0, 0xd9, 0x7e, 0x8a, 0x6e, 0x5f, 0x62, 0xba, 0xb5, 0xaf, 0xf7, 0xd7, 0x94, - 0x6f, 0xf6, 0xd7, 0x94, 0x9f, 0xf6, 0xd7, 0x94, 0xde, 0x41, 0x39, 0xb5, 0x6e, 0xfe, 0x19, 0x00, - 0x00, 0xff, 0xff, 0x54, 0xb8, 0x94, 0x7b, 0x7e, 0x0b, 0x00, 0x00, + 0xdd, 0x95, 0x10, 0x07, 0x4e, 0x9c, 0xe0, 0xc0, 0x85, 0x23, 0xfc, 0x17, 0xc0, 0x89, 0x8f, 0x03, + 0x47, 0xbe, 0x2e, 0xe5, 0x10, 0xa1, 0xbd, 0xf1, 0x71, 0x21, 0x47, 0x4e, 0x68, 0xc6, 0x5f, 0x09, + 0x4d, 0x20, 0x48, 0xdc, 0xe2, 0x99, 0xe7, 0x79, 0xde, 0x99, 0xf7, 0x7d, 0xf2, 0xce, 0x0b, 0xb4, + 0x38, 0xa1, 0x9c, 0xb6, 0x7a, 0x18, 0x39, 0x34, 0x6a, 0xc5, 0x66, 0xdc, 0x1a, 0x1a, 0x2d, 0xbe, + 0x17, 0x63, 0xd6, 0x94, 0x3b, 0xf0, 0x14, 0xe6, 0x3e, 0x4e, 0xf0, 0x20, 0x6c, 0xa6, 0x98, 0x66, + 0x6c, 0xc6, 0xcd, 0xa1, 0x71, 0x7a, 0x0d, 0x73, 0xbf, 0x35, 0x34, 0x50, 0x10, 0xfb, 0xc8, 0x68, + 0x21, 0xce, 0x31, 0xe3, 0x88, 0x13, 0x01, 0x10, 0xbc, 0xd3, 0xda, 0xd4, 0x7e, 0xca, 0xb5, 0x7b, + 0x01, 0x75, 0x76, 0x33, 0xc0, 0xd9, 0x29, 0xc0, 0x10, 0x05, 0xc4, 0x45, 0x9c, 0x26, 0xd9, 0xee, + 0x86, 0x47, 0xb8, 0x3f, 0xe8, 0x35, 0x1d, 0x1a, 0xb6, 0x3c, 0xea, 0xd1, 0x96, 0x5c, 0xee, 0x0d, + 0xfa, 0xf2, 0x2b, 0x3d, 0xb4, 0xf8, 0x95, 0xc2, 0xf5, 0x27, 0x35, 0x50, 0xed, 0xca, 0x18, 0x0f, + 0x38, 0xe2, 0x18, 0xea, 0xa0, 0xe6, 0xe1, 0x08, 0x33, 0xc2, 0x6c, 0x4e, 0x42, 0xac, 0xfe, 0x7c, + 0x64, 0x5d, 0xb9, 0x50, 0xb1, 0xaa, 0xd9, 0xe2, 0xab, 0x24, 0xc4, 0xf0, 0x2e, 0x68, 0xe4, 0x98, + 0x22, 0x3a, 0xb3, 0x13, 0x4a, 0xb9, 0xfa, 0x8b, 0x80, 0xd7, 0xba, 0x27, 0xc6, 0x23, 0xed, 0x38, + 0x63, 0xfb, 0x1b, 0x8c, 0xec, 0xe3, 0x2b, 0xfa, 0xa6, 0xa9, 0x5b, 0xff, 0xcf, 0x28, 0x0f, 0x0b, + 0x86, 0x45, 0x29, 0x87, 0xab, 0xa0, 0xc2, 0x02, 0xca, 0xd5, 0x5f, 0xd3, 0x38, 0xf2, 0x03, 0x1a, + 0xa0, 0xd2, 0xa7, 0xc9, 0xae, 0xfa, 0x9b, 0x58, 0xac, 0x9a, 0x67, 0x9b, 0xb3, 0x53, 0xd9, 0x7c, + 0x85, 0x26, 0xbb, 0x96, 0x84, 0xc2, 0xd7, 0xc1, 0x6a, 0x80, 0x44, 0x2a, 0xd3, 0x54, 0xd9, 0x3e, + 0x46, 0x2e, 0x4e, 0xd4, 0x6f, 0xeb, 0x52, 0xe1, 0x42, 0xa9, 0x80, 0xb9, 0xdf, 0xcc, 0x93, 0xd7, + 0x4c, 0x6f, 0xde, 0x15, 0x8c, 0xdb, 0x92, 0x60, 0x9d, 0x48, 0x55, 0x26, 0x96, 0xe0, 0x36, 0xa8, + 0xa6, 0x9a, 0xe2, 0x86, 0x4c, 0xfd, 0xae, 0xbe, 0x7e, 0xe8, 0x42, 0xad, 0x7b, 0x6a, 0x3c, 0xd2, + 0x60, 0x79, 0xc5, 0x6d, 0x63, 0xc7, 0xbc, 0x28, 0xee, 0x09, 0x24, 0x56, 0xdc, 0x8d, 0x09, 0xa6, + 0xa8, 0x2d, 0xce, 0x98, 0xdf, 0xff, 0x03, 0x53, 0x62, 0x53, 0xa6, 0x05, 0x56, 0x7c, 0xc2, 0x38, + 0x4d, 0x88, 0x83, 0x82, 0x8c, 0xfe, 0x43, 0x4a, 0x3f, 0x37, 0x1e, 0x69, 0x7a, 0x49, 0x7f, 0x59, + 0x70, 0xd7, 0xc5, 0x77, 0x88, 0x1e, 0x5f, 0xd1, 0x8d, 0x76, 0xa7, 0xd3, 0x31, 0x8d, 0xb6, 0x6e, + 0xd5, 0x4b, 0x81, 0x54, 0xf3, 0x25, 0x70, 0x0c, 0x73, 0xdf, 0xb0, 0x5d, 0xc4, 0x91, 0xfa, 0x49, + 0x43, 0x26, 0x46, 0x9b, 0x93, 0x98, 0x5b, 0xdc, 0x37, 0x6e, 0x22, 0x8e, 0xac, 0xa3, 0x38, 0xfb, + 0x05, 0xdf, 0x00, 0xf5, 0x82, 0x6e, 0x0f, 0x29, 0xc7, 0x4c, 0xfd, 0xb4, 0xb1, 0x7e, 0x68, 0x01, + 0x91, 0x2e, 0x1c, 0x8f, 0xb4, 0xe5, 0xf2, 0x88, 0x97, 0xcc, 0x2d, 0xdd, 0x3a, 0x9e, 0x0b, 0x3f, + 0x14, 0x52, 0x70, 0x03, 0xc0, 0x54, 0x1d, 0xc7, 0x94, 0x11, 0x6e, 0x93, 0xc8, 0xc5, 0x8f, 0xd5, + 0xcf, 0x1a, 0xd2, 0x15, 0x2b, 0x12, 0x9b, 0xee, 0xdc, 0x11, 0x1b, 0xf0, 0x4d, 0x00, 0x4a, 0xeb, + 0xa9, 0x1f, 0x69, 0xf2, 0x1c, 0xeb, 0x73, 0xce, 0x51, 0x58, 0xae, 0x7b, 0x66, 0x3c, 0xd2, 0x1a, + 0x13, 0x07, 0xd9, 0xd9, 0xb9, 0x6c, 0x18, 0x6d, 0xb3, 0xd3, 0xe9, 0xb4, 0x75, 0x6b, 0x42, 0x11, + 0x6e, 0x83, 0xa3, 0x3d, 0x14, 0xa0, 0xc8, 0xc1, 0x4c, 0xfd, 0x58, 0xa8, 0x57, 0xfe, 0x9e, 0x5b, + 0xa0, 0xe1, 0x55, 0x50, 0x4b, 0x50, 0xe4, 0x22, 0x6a, 0x87, 0xe4, 0x31, 0x66, 0xea, 0xbb, 0xe7, + 0x65, 0xd5, 0x1a, 0xe3, 0x91, 0xb6, 0x5a, 0x56, 0xad, 0x7d, 0xf9, 0xf2, 0x66, 0x5b, 0x56, 0xbd, + 0x9a, 0xa2, 0xef, 0x09, 0x30, 0x34, 0xc1, 0x31, 0x16, 0x20, 0xe6, 0x93, 0xc8, 0x63, 0xea, 0xef, + 0x4d, 0x19, 0x77, 0x75, 0x3c, 0xd2, 0xea, 0xd3, 0x76, 0xd1, 0xad, 0x12, 0x06, 0xdf, 0x06, 0x67, + 0xe2, 0x04, 0x0f, 0x09, 0x1d, 0x30, 0x1b, 0xc7, 0xd4, 0xf1, 0xed, 0x89, 0x9e, 0xc2, 0xd4, 0x27, + 0x6d, 0x99, 0x9b, 0x17, 0xe7, 0xfd, 0x87, 0xee, 0xe3, 0xc8, 0x25, 0x91, 0x77, 0xbd, 0xe4, 0xfc, + 0xa5, 0x5c, 0x5b, 0x97, 0x76, 0xda, 0xba, 0xf5, 0x4c, 0x1e, 0xe3, 0x96, 0x08, 0x31, 0x81, 0x66, + 0xf0, 0x2d, 0x70, 0xda, 0x19, 0x24, 0x09, 0x8e, 0xf8, 0xac, 0xf8, 0x3f, 0xfe, 0x37, 0xf1, 0xd5, + 0x2c, 0xc4, 0xd3, 0xe1, 0x19, 0x80, 0x8f, 0x06, 0x8c, 0x93, 0x3e, 0x71, 0xe4, 0x8a, 0xdd, 0x23, + 0x9c, 0xa9, 0x9f, 0x5f, 0x93, 0x8d, 0xe8, 0xc6, 0x78, 0xa4, 0xd5, 0xca, 0xe4, 0x19, 0xfa, 0x1f, + 0x23, 0xad, 0x35, 0xd1, 0x21, 0xe3, 0x64, 0x8f, 0x85, 0x88, 0x13, 0x27, 0x40, 0x3d, 0xd6, 0xf2, + 0xe8, 0x46, 0x8f, 0xf0, 0x3e, 0xc1, 0x81, 0xdb, 0xec, 0x12, 0x3e, 0xc4, 0x0e, 0xa7, 0xc9, 0x96, + 0x75, 0x62, 0x4a, 0xbf, 0x4b, 0x38, 0x83, 0x7d, 0xf0, 0x6c, 0x91, 0xf4, 0x6c, 0x17, 0xbb, 0xb6, + 0xe3, 0x63, 0x67, 0x37, 0xa6, 0x24, 0xe2, 0xea, 0x17, 0xd7, 0xe4, 0xff, 0xeb, 0xb9, 0x39, 0x96, + 0xbc, 0x51, 0x20, 0xad, 0xa2, 0x7a, 0x77, 0x73, 0x9d, 0x72, 0x13, 0xba, 0xe0, 0x6c, 0x9e, 0xdb, + 0x99, 0x61, 0xbe, 0x5c, 0x38, 0x4c, 0x5e, 0xa3, 0x59, 0x51, 0x5e, 0x03, 0x27, 0xfb, 0x24, 0x42, + 0x01, 0xd9, 0x9f, 0x56, 0xff, 0x6a, 0x61, 0xf5, 0xd5, 0x82, 0x5f, 0x2e, 0xea, 0x1f, 0x28, 0xa0, + 0x22, 0x5a, 0x34, 0xbc, 0x0a, 0x56, 0x8a, 0x6c, 0x0d, 0x71, 0xc2, 0x08, 0x8d, 0x54, 0x45, 0xd6, + 0x67, 0x65, 0xba, 0x3e, 0x5b, 0xba, 0x55, 0xcf, 0x91, 0x0f, 0x53, 0x20, 0xdc, 0x01, 0xf5, 0x3c, + 0x05, 0x39, 0x77, 0x69, 0x0e, 0x77, 0x39, 0x03, 0xe6, 0xd4, 0x93, 0xe0, 0x7f, 0xd2, 0x91, 0xea, + 0x21, 0xd9, 0x46, 0xd2, 0x0f, 0xfd, 0xbd, 0x25, 0x00, 0x9f, 0x76, 0x1d, 0x0c, 0xc1, 0x0a, 0xf2, + 0xbc, 0x04, 0x7b, 0x13, 0x2e, 0x4a, 0x0f, 0xd9, 0x9d, 0xf2, 0xa3, 0x79, 0x69, 0x6b, 0x5b, 0xd8, + 0xe8, 0xe2, 0xa2, 0x36, 0x0a, 0x08, 0xe3, 0x56, 0x7d, 0x42, 0x5b, 0x3a, 0xe8, 0x0a, 0xa8, 0xc8, + 0x46, 0xbc, 0x24, 0x53, 0x7c, 0x6e, 0x4e, 0x8a, 0x27, 0x0e, 0x28, 0xdb, 0xb1, 0xe4, 0xc0, 0xf3, + 0xa0, 0x4e, 0x22, 0x27, 0x18, 0x88, 0x4b, 0xda, 0x2e, 0x0e, 0xd0, 0x5e, 0x76, 0xc3, 0xe5, 0x62, + 0xf9, 0xa6, 0x58, 0x85, 0xcf, 0x83, 0xe5, 0x38, 0xa1, 0x31, 0x65, 0x38, 0xc9, 0x3a, 0x6a, 0x45, + 0xe2, 0x8e, 0xe7, 0xab, 0xb2, 0x9b, 0xea, 0xef, 0x28, 0xa0, 0x7e, 0xbb, 0x78, 0x2d, 0xba, 0x88, + 0x3b, 0x3e, 0xec, 0x4c, 0xbf, 0x7a, 0xca, 0xc2, 0x8f, 0x5e, 0x67, 0xfa, 0xd1, 0x5b, 0x5a, 0xf4, + 0xcd, 0xd3, 0xdb, 0xa0, 0x26, 0x67, 0x90, 0x07, 0x83, 0x30, 0x44, 0xc9, 0x1e, 0x84, 0xd9, 0x68, + 0xa0, 0x4c, 0x4c, 0x06, 0x10, 0x54, 0xe4, 0x9c, 0x21, 0x1d, 0x60, 0xc9, 0xdf, 0x7a, 0x00, 0xaa, + 0x0f, 0x88, 0x17, 0x91, 0xc8, 0x93, 0xef, 0x94, 0x09, 0xaa, 0xb4, 0xf7, 0x08, 0x3b, 0x3c, 0x9d, + 0x48, 0x94, 0x79, 0x03, 0x09, 0x48, 0x51, 0x72, 0x0a, 0x79, 0x01, 0x1c, 0x76, 0x69, 0x88, 0x48, + 0x6e, 0xad, 0x19, 0xf0, 0x0c, 0xa0, 0xbf, 0xaf, 0x80, 0xa3, 0xc2, 0xd4, 0x32, 0xd6, 0x0c, 0x6f, + 0x56, 0x16, 0xf4, 0xe6, 0x9d, 0xf9, 0x43, 0xd4, 0xd2, 0xbf, 0x9b, 0xa1, 0xf4, 0x0f, 0x15, 0xd0, + 0x10, 0x19, 0xc0, 0xee, 0xf5, 0xcc, 0x64, 0xf8, 0x7a, 0xe4, 0xde, 0x4f, 0x28, 0xed, 0xc3, 0x7b, + 0xe0, 0x48, 0x88, 0x19, 0x43, 0x1e, 0x96, 0x99, 0xa8, 0x9a, 0x9b, 0xf3, 0x9c, 0x56, 0x50, 0x4b, + 0xcb, 0xe5, 0x2a, 0x56, 0xae, 0x01, 0x5b, 0xe0, 0x18, 0x23, 0x5e, 0x84, 0xf8, 0x20, 0xc1, 0xb3, + 0xcf, 0x29, 0x9a, 0x75, 0x89, 0xe9, 0xd6, 0xbe, 0x3e, 0x58, 0x53, 0xbe, 0x39, 0x58, 0x53, 0x7e, + 0x3a, 0x58, 0x53, 0x7a, 0x87, 0xe5, 0xd0, 0xb9, 0xf9, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0xac, + 0x86, 0x1c, 0xa3, 0x3d, 0x0b, 0x00, 0x00, } func (m *BeaconState) Marshal() (dAtA []byte, err error) { @@ -1218,45 +1161,6 @@ func (m *PendingAttestation) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *ValidatorLatestVote) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ValidatorLatestVote) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ValidatorLatestVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if len(m.Root) > 0 { - i -= len(m.Root) - copy(dAtA[i:], m.Root) - i = encodeVarintTypes(dAtA, i, uint64(len(m.Root))) - i-- - dAtA[i] = 0x12 - } - if m.Epoch != 0 { - i = encodeVarintTypes(dAtA, i, uint64(m.Epoch)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - func (m *HistoricalBatch) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1644,25 +1548,6 @@ func (m *PendingAttestation) Size() (n int) { return n } -func (m *ValidatorLatestVote) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Epoch != 0 { - n += 1 + sovTypes(uint64(m.Epoch)) - } - l = len(m.Root) - if l > 0 { - n += 1 + l + sovTypes(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - func (m *HistoricalBatch) Size() (n int) { if m == nil { return 0 @@ -2886,113 +2771,6 @@ func (m *PendingAttestation) Unmarshal(dAtA []byte) error { } return nil } -func (m *ValidatorLatestVote) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ValidatorLatestVote: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ValidatorLatestVote: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Epoch", wireType) - } - m.Epoch = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Epoch |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Root", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthTypes - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthTypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Root = append(m.Root[:0], dAtA[iNdEx:postIndex]...) - if m.Root == nil { - m.Root = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipTypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthTypes - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthTypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *HistoricalBatch) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/shared/depositutil/BUILD.bazel b/shared/depositutil/BUILD.bazel index 8f100ca5e516..c96b559cbc83 100644 --- a/shared/depositutil/BUILD.bazel +++ b/shared/depositutil/BUILD.bazel @@ -11,6 +11,7 @@ go_library( "//contracts/deposit-contract:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/bls:go_default_library", + "//shared/featureconfig:go_default_library", "//shared/hashutil:go_default_library", "//shared/params:go_default_library", "@com_github_ethereum_go_ethereum//core/types:go_default_library", @@ -30,6 +31,7 @@ go_test( "//proto/beacon/p2p/v1:go_default_library", "//shared/bls:go_default_library", "//shared/params:go_default_library", + "//shared/testutil:go_default_library", "//shared/testutil/assert:go_default_library", "//shared/testutil/require:go_default_library", "@com_github_prysmaticlabs_go_ssz//:go_default_library", diff --git a/shared/depositutil/deposit.go b/shared/depositutil/deposit.go index 0873ef979408..8db254d320dc 100644 --- a/shared/depositutil/deposit.go +++ b/shared/depositutil/deposit.go @@ -11,8 +11,9 @@ import ( "github.com/prysmaticlabs/go-ssz" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" contract "github.com/prysmaticlabs/prysm/contracts/deposit-contract" - pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" + p2ppb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bls" + "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/sirupsen/logrus" @@ -56,7 +57,7 @@ func DepositInput( if err != nil { return nil, [32]byte{}, err } - root, err := (&pb.SigningData{ObjectRoot: sr[:], Domain: domain}).HashTreeRoot() + root, err := (&p2ppb.SigningData{ObjectRoot: sr[:], Domain: domain}).HashTreeRoot() if err != nil { return nil, [32]byte{}, err } @@ -82,6 +83,39 @@ func WithdrawalCredentialsHash(withdrawalKey bls.SecretKey) []byte { return append([]byte{params.BeaconConfig().BLSWithdrawalPrefixByte}, h[1:]...)[:32] } +// VerifyDepositSignature verifies the correctness of Eth1 deposit BLS signature +func VerifyDepositSignature(dd *ethpb.Deposit_Data, domain []byte) error { + if featureconfig.Get().SkipBLSVerify { + return nil + } + ddCopy := *dd + publicKey, err := bls.PublicKeyFromBytes(dd.PublicKey) + if err != nil { + return errors.Wrap(err, "could not convert bytes to public key") + } + sig, err := bls.SignatureFromBytes(dd.Signature) + if err != nil { + return errors.Wrap(err, "could not convert bytes to signature") + } + ddCopy.Signature = nil + root, err := ssz.SigningRoot(ddCopy) + if err != nil { + return errors.Wrap(err, "could not get signing root") + } + signingData := &p2ppb.SigningData{ + ObjectRoot: root[:], + Domain: domain, + } + ctrRoot, err := signingData.HashTreeRoot() + if err != nil { + return errors.Wrap(err, "could not get container root") + } + if !sig.Verify(publicKey, ctrRoot[:]) { + return helpers.ErrSigFailedToVerify + } + return nil +} + // GenerateDepositTransaction uses the provided validating key and withdrawal key to // create a transaction object for the deposit contract. func GenerateDepositTransaction( diff --git a/shared/depositutil/deposit_test.go b/shared/depositutil/deposit_test.go index c0305742e5d1..320acf2c9e1b 100644 --- a/shared/depositutil/deposit_test.go +++ b/shared/depositutil/deposit_test.go @@ -9,6 +9,7 @@ import ( "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/depositutil" "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/testutil" "github.com/prysmaticlabs/prysm/shared/testutil/assert" "github.com/prysmaticlabs/prysm/shared/testutil/require" ) @@ -37,3 +38,44 @@ func TestDepositInput_GeneratesPb(t *testing.T) { require.NoError(t, err) assert.Equal(t, true, sig.Verify(k1.PublicKey(), root[:])) } + +func TestVerifyDepositSignature_ValidSig(t *testing.T) { + deposits, _, err := testutil.DeterministicDepositsAndKeys(1) + if err != nil { + t.Fatalf("Error Generating Deposits and Keys - %v", err) + } + deposit := deposits[0] + domain, err := helpers.ComputeDomain( + params.BeaconConfig().DomainDeposit, + params.BeaconConfig().GenesisForkVersion, + params.BeaconConfig().ZeroHash[:], + ) + if err != nil { + t.Fatalf("Error Computing Domain - %v", err) + } + err = depositutil.VerifyDepositSignature(deposit.Data, domain) + if err != nil { + t.Fatal("Deposit Verification fails with a valid signature") + } +} + +func TestVerifyDepositSignature_InvalidSig(t *testing.T) { + deposits, _, err := testutil.DeterministicDepositsAndKeys(1) + if err != nil { + t.Fatalf("Error Generating Deposits and Keys - %v", err) + } + deposit := deposits[0] + domain, err := helpers.ComputeDomain( + params.BeaconConfig().DomainDeposit, + params.BeaconConfig().GenesisForkVersion, + params.BeaconConfig().ZeroHash[:], + ) + if err != nil { + t.Fatalf("Error Computing Domain - %v", err) + } + deposit.Data.Signature = deposit.Data.Signature[1:] + err = depositutil.VerifyDepositSignature(deposit.Data, domain) + if err == nil { + t.Fatal("Deposit Verification succeeds with a invalid signature") + } +} diff --git a/validator/client/validator.go b/validator/client/validator.go index ef768fd20045..f25022457471 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -291,6 +291,8 @@ func (v *validator) checkAndLogValidatorStatus(validatorStatuses []*ethpb.Valida validatorActivated = true case ethpb.ValidatorStatus_EXITED: log.Info("Validator exited") + case ethpb.ValidatorStatus_INVALID: + log.Warn("Invalid Eth1 deposit") default: log.WithFields(logrus.Fields{ "activationEpoch": status.Status.ActivationEpoch,