mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 13:58:09 -05:00
Compare commits
4 Commits
v1.0.0.rc.
...
v1.0.0.rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2cb814648a | ||
|
|
dc897a2007 | ||
|
|
a051e684ae | ||
|
|
64be627a6d |
@@ -265,7 +265,7 @@ func (bs *Server) StreamIndexedAttestations(
|
||||
}
|
||||
if data.Attestation == nil || data.Attestation.Aggregate == nil {
|
||||
// One nil attestation shouldn't stop the stream.
|
||||
log.Info("Indexed attestations stream got nil attestation or nil attestation aggregate")
|
||||
log.Debug("Indexed attestations stream got nil attestation or nil attestation aggregate")
|
||||
continue
|
||||
}
|
||||
bs.ReceivedAttestationsBuffer <- data.Attestation.Aggregate
|
||||
@@ -340,7 +340,7 @@ func (bs *Server) collectReceivedAttestations(ctx context.Context) {
|
||||
// We aggregate the received attestations, we know they all have the same data root.
|
||||
aggAtts, err := attaggregation.Aggregate(atts)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not aggregate collected attestations")
|
||||
log.WithError(err).Error("Could not aggregate attestations")
|
||||
continue
|
||||
}
|
||||
if len(aggAtts) == 0 {
|
||||
@@ -356,7 +356,7 @@ func (bs *Server) collectReceivedAttestations(ctx context.Context) {
|
||||
case att := <-bs.ReceivedAttestationsBuffer:
|
||||
attDataRoot, err := att.Data.HashTreeRoot()
|
||||
if err != nil {
|
||||
log.Errorf("Could not hash tree root data: %v", err)
|
||||
log.Errorf("Could not hash tree root attestation data: %v", err)
|
||||
continue
|
||||
}
|
||||
attsByRoot[attDataRoot] = append(attsByRoot[attDataRoot], att)
|
||||
|
||||
@@ -38,7 +38,7 @@ func (bs *Server) ListBlocks(
|
||||
case *ethpb.ListBlocksRequest_Epoch:
|
||||
blks, _, err := bs.BeaconDB.Blocks(ctx, filters.NewFilter().SetStartEpoch(q.Epoch).SetEndEpoch(q.Epoch))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Failed to get blocks: %v", err)
|
||||
return nil, status.Errorf(codes.Internal, "Could not get blocks: %v", err)
|
||||
}
|
||||
|
||||
numBlks := len(blks)
|
||||
@@ -194,12 +194,12 @@ func (bs *Server) StreamBlocks(_ *ptypes.Empty, stream ethpb.BeaconChain_StreamB
|
||||
}
|
||||
headState, err := bs.HeadFetcher.HeadState(bs.Ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("blockSlot", data.SignedBlock.Block.Slot).Warn("Could not get head state to verify block signature")
|
||||
log.WithError(err).WithField("blockSlot", data.SignedBlock.Block.Slot).Error("Could not get head state")
|
||||
continue
|
||||
}
|
||||
|
||||
if err := blocks.VerifyBlockSignature(headState, data.SignedBlock); err != nil {
|
||||
log.WithError(err).WithField("blockSlot", data.SignedBlock.Block.Slot).Warn("Could not verify block signature")
|
||||
log.WithError(err).WithField("blockSlot", data.SignedBlock.Block.Slot).Error("Could not verify block signature")
|
||||
continue
|
||||
}
|
||||
if err := stream.Send(data.SignedBlock); err != nil {
|
||||
|
||||
@@ -358,7 +358,7 @@ func (is *infostream) generatePendingValidatorInfo(info *ethpb.ValidatorInfo) (*
|
||||
if deposit.block != nil {
|
||||
info.Status = ethpb.ValidatorStatus_DEPOSITED
|
||||
if queueTimestamp, err := is.depositQueueTimestamp(deposit.block); err != nil {
|
||||
log.WithError(err).Error("Failed to obtain queue activation timestamp")
|
||||
log.WithError(err).Error("Could not obtain queue activation timestamp")
|
||||
} else {
|
||||
info.TransitionTimestamp = queueTimestamp
|
||||
}
|
||||
@@ -415,7 +415,7 @@ func (is *infostream) calculateActivationTimeForPendingValidators(res []*ethpb.V
|
||||
for curEpoch := epoch + 1; len(sortedIndices) > 0 && len(pendingValidators) > 0; curEpoch++ {
|
||||
toProcess, err := helpers.ValidatorChurnLimit(numAttestingValidators)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to determine validator churn limit")
|
||||
log.WithError(err).Error("Could not determine validator churn limit")
|
||||
}
|
||||
if toProcess > uint64(len(sortedIndices)) {
|
||||
toProcess = uint64(len(sortedIndices))
|
||||
@@ -456,7 +456,7 @@ func (is *infostream) handleBlockProcessed() {
|
||||
is.currentEpoch = blockEpoch
|
||||
if err := is.sendValidatorsInfo(is.pubKeys); err != nil {
|
||||
// Client probably disconnected.
|
||||
log.WithError(err).Debug("Failed to send infostream response")
|
||||
log.WithError(err).Debug("Could not send infostream response")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ func (vs *Server) GetAttestationData(ctx context.Context, req *ethpb.Attestation
|
||||
}
|
||||
defer func() {
|
||||
if err := vs.AttestationCache.MarkNotInProgress(req); err != nil {
|
||||
log.WithError(err).Error("Failed to mark cache not in progress")
|
||||
log.WithError(err).Error("Could not mark cache not in progress")
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -89,7 +89,7 @@ func (vs *Server) GetAttestationData(ctx context.Context, req *ethpb.Attestation
|
||||
}
|
||||
}
|
||||
if headState == nil {
|
||||
return nil, status.Error(codes.Internal, "Failed to lookup parent state from head.")
|
||||
return nil, status.Error(codes.Internal, "Could not lookup parent state from head.")
|
||||
}
|
||||
|
||||
if helpers.CurrentEpoch(headState) < helpers.SlotToEpoch(req.Slot) {
|
||||
|
||||
@@ -192,12 +192,12 @@ func (vs *Server) eth1Data(ctx context.Context, slot uint64) (*ethpb.Eth1Data, e
|
||||
// Look up most recent block up to timestamp
|
||||
blockNumber, err := vs.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, eth1VotingPeriodStartTime)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get block number from timestamp")
|
||||
log.WithError(err).Error("Could not get block number from timestamp")
|
||||
return vs.randomETH1DataVote(ctx)
|
||||
}
|
||||
eth1Data, err := vs.defaultEth1DataResponse(ctx, blockNumber)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get eth1 data from block number")
|
||||
log.WithError(err).Error("Could not get eth1 data from block number")
|
||||
return vs.randomETH1DataVote(ctx)
|
||||
}
|
||||
|
||||
@@ -237,12 +237,12 @@ func (vs *Server) eth1DataMajorityVote(ctx context.Context, beaconState *stateTr
|
||||
|
||||
lastBlockByEarliestValidTime, err := vs.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, earliestValidTime)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get last block by earliest valid time")
|
||||
log.WithError(err).Error("Could not get last block by earliest valid time")
|
||||
return vs.randomETH1DataVote(ctx)
|
||||
}
|
||||
timeOfLastBlockByEarliestValidTime, err := vs.Eth1BlockFetcher.BlockTimeByHeight(ctx, lastBlockByEarliestValidTime)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get time of last block by earliest valid time")
|
||||
log.WithError(err).Error("Could not get time of last block by earliest valid time")
|
||||
return vs.randomETH1DataVote(ctx)
|
||||
}
|
||||
// Increment the earliest block if the original block's time is before valid time.
|
||||
@@ -253,12 +253,12 @@ func (vs *Server) eth1DataMajorityVote(ctx context.Context, beaconState *stateTr
|
||||
|
||||
lastBlockByLatestValidTime, err := vs.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, latestValidTime)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get last block by latest valid time")
|
||||
log.WithError(err).Error("Could not get last block by latest valid time")
|
||||
return vs.randomETH1DataVote(ctx)
|
||||
}
|
||||
timeOfLastBlockByLatestValidTime, err := vs.Eth1BlockFetcher.BlockTimeByHeight(ctx, lastBlockByLatestValidTime)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get time of last block by latest valid time")
|
||||
log.WithError(err).Error("Could not get time of last block by latest valid time")
|
||||
return vs.randomETH1DataVote(ctx)
|
||||
}
|
||||
if timeOfLastBlockByLatestValidTime < earliestValidTime {
|
||||
@@ -278,7 +278,7 @@ func (vs *Server) eth1DataMajorityVote(ctx context.Context, beaconState *stateTr
|
||||
if lastBlockDepositCount >= vs.HeadFetcher.HeadETH1Data().DepositCount {
|
||||
hash, err := vs.Eth1BlockFetcher.BlockHashByHeight(ctx, lastBlockByLatestValidTime)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get hash of last block by latest valid time")
|
||||
log.WithError(err).Error("Could not get hash of last block by latest valid time")
|
||||
return vs.randomETH1DataVote(ctx)
|
||||
}
|
||||
return ðpb.Eth1Data{
|
||||
@@ -507,7 +507,7 @@ func (vs *Server) canonicalEth1Data(
|
||||
|
||||
// Add in current vote, to get accurate vote tally
|
||||
if err := beaconState.AppendEth1DataVotes(currentVote); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to append eth1 data votes to state")
|
||||
return nil, nil, errors.Wrap(err, "could not append eth1 data votes to state")
|
||||
}
|
||||
hasSupport, err := blocks.Eth1DataHasEnoughSupport(beaconState, currentVote)
|
||||
if err != nil {
|
||||
|
||||
@@ -36,18 +36,19 @@ type Flags struct {
|
||||
PyrmontTestnet bool // PyrmontTestnet defines the flag through which we can enable the node to run on the Pyrmont testnet.
|
||||
|
||||
// Feature related flags.
|
||||
WriteSSZStateTransitions bool // WriteSSZStateTransitions to tmp directory.
|
||||
SkipBLSVerify bool // Skips BLS verification across the runtime.
|
||||
EnableBlst bool // Enables new BLS library from supranational.
|
||||
PruneEpochBoundaryStates bool // PruneEpochBoundaryStates prunes the epoch boundary state before last finalized check point.
|
||||
EnableSnappyDBCompression bool // EnableSnappyDBCompression in the database.
|
||||
SlasherProtection bool // SlasherProtection protects validator fron sending over a slashable offense over the network using external slasher.
|
||||
EnableNoise bool // EnableNoise enables the beacon node to use NOISE instead of SECIO when performing a handshake with another peer.
|
||||
EnableEth1DataMajorityVote bool // EnableEth1DataMajorityVote uses the Voting With The Majority algorithm to vote for eth1data.
|
||||
EnablePeerScorer bool // EnablePeerScorer enables experimental peer scoring in p2p.
|
||||
EnablePruningDepositProofs bool // EnablePruningDepositProofs enables pruning deposit proofs which significantly reduces the size of a deposit
|
||||
EnableSyncBacktracking bool // EnableSyncBacktracking enables backtracking algorithm when searching for alternative forks during initial sync.
|
||||
EnableLargerGossipHistory bool // EnableLargerGossipHistory increases the gossip history we store in our caches.
|
||||
WriteSSZStateTransitions bool // WriteSSZStateTransitions to tmp directory.
|
||||
SkipBLSVerify bool // Skips BLS verification across the runtime.
|
||||
EnableBlst bool // Enables new BLS library from supranational.
|
||||
PruneEpochBoundaryStates bool // PruneEpochBoundaryStates prunes the epoch boundary state before last finalized check point.
|
||||
EnableSnappyDBCompression bool // EnableSnappyDBCompression in the database.
|
||||
SlasherProtection bool // SlasherProtection protects validator fron sending over a slashable offense over the network using external slasher.
|
||||
EnableNoise bool // EnableNoise enables the beacon node to use NOISE instead of SECIO when performing a handshake with another peer.
|
||||
EnableEth1DataMajorityVote bool // EnableEth1DataMajorityVote uses the Voting With The Majority algorithm to vote for eth1data.
|
||||
EnablePeerScorer bool // EnablePeerScorer enables experimental peer scoring in p2p.
|
||||
EnablePruningDepositProofs bool // EnablePruningDepositProofs enables pruning deposit proofs which significantly reduces the size of a deposit
|
||||
EnableSyncBacktracking bool // EnableSyncBacktracking enables backtracking algorithm when searching for alternative forks during initial sync.
|
||||
EnableLargerGossipHistory bool // EnableLargerGossipHistory increases the gossip history we store in our caches.
|
||||
WriteWalletPasswordOnWebOnboarding bool // WriteWalletPasswordOnWebOnboarding writes the password to disk after Prysm web signup.
|
||||
|
||||
// Logging related toggles.
|
||||
DisableGRPCConnectionLogs bool // Disables logging when a new grpc client has connected.
|
||||
@@ -206,6 +207,11 @@ func ConfigureValidator(ctx *cli.Context) {
|
||||
log.Warn("Enabled validator attestation and block slashing protection using an external slasher.")
|
||||
cfg.SlasherProtection = true
|
||||
}
|
||||
if ctx.Bool(writeWalletPasswordOnWebOnboarding.Name) {
|
||||
log.Warn("Enabled full web mode, wallet password will be written to disk at the wallet directory " +
|
||||
"upon completing web onboarding.")
|
||||
cfg.WriteWalletPasswordOnWebOnboarding = true
|
||||
}
|
||||
Init(cfg)
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,11 @@ var (
|
||||
Name: "enable-larger-gossip-history",
|
||||
Usage: "Enables the node to store a larger amount of gossip messages in its cache.",
|
||||
}
|
||||
writeWalletPasswordOnWebOnboarding = &cli.BoolFlag{
|
||||
Name: "write-wallet-password-on-web-onboarding",
|
||||
Usage: "(Danger): Writes the wallet password to the wallet directory on completing Prysm web onboarding. " +
|
||||
"We recommend against this flag unless you are an advanced user.",
|
||||
}
|
||||
)
|
||||
|
||||
// devModeFlags holds list of flags that are set when development mode is on.
|
||||
@@ -94,6 +99,7 @@ var devModeFlags = []cli.Flag{
|
||||
|
||||
// ValidatorFlags contains a list of all the feature flags that apply to the validator client.
|
||||
var ValidatorFlags = append(deprecatedFlags, []cli.Flag{
|
||||
writeWalletPasswordOnWebOnboarding,
|
||||
enableExternalSlasherProtectionFlag,
|
||||
ToledoTestnet,
|
||||
PyrmontTestnet,
|
||||
|
||||
@@ -173,7 +173,6 @@ func prepareClients(cliCtx *cli.Context) (*ethpb.BeaconNodeValidatorClient, *eth
|
||||
dialOpts := client.ConstructDialOptions(
|
||||
cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name),
|
||||
cliCtx.String(flags.CertFlag.Name),
|
||||
strings.Split(cliCtx.String(flags.GrpcHeadersFlag.Name), ","),
|
||||
cliCtx.Uint(flags.GrpcRetriesFlag.Name),
|
||||
cliCtx.Duration(flags.GrpcRetryDelayFlag.Name),
|
||||
)
|
||||
|
||||
@@ -33,7 +33,9 @@ const (
|
||||
WalletPasswordPromptText = "Wallet password"
|
||||
// ConfirmPasswordPromptText for confirming a wallet password.
|
||||
ConfirmPasswordPromptText = "Confirm password"
|
||||
hashCost = 8
|
||||
// DefaultWalletPasswordFile used to store a wallet password with appropriate permissions
|
||||
// if a user signs up via the Prysm web UI via RPC.
|
||||
DefaultWalletPasswordFile = "walletpassword.txt"
|
||||
// CheckExistsErrMsg for when there is an error while checking for a wallet
|
||||
CheckExistsErrMsg = "could not check if wallet exists"
|
||||
// CheckValidityErrMsg for when there is an error while checking wallet validity
|
||||
|
||||
@@ -64,10 +64,10 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot uint64, pubKey [
|
||||
Data: data,
|
||||
}
|
||||
if err := v.preAttSignValidations(ctx, indexedAtt, pubKey); err != nil {
|
||||
log.WithFields(logrus.Fields{
|
||||
"sourceEpoch": indexedAtt.Data.Source.Epoch,
|
||||
"targetEpoch": indexedAtt.Data.Target.Epoch,
|
||||
}).WithError(err).Error("Failed attestation safety check")
|
||||
log.WithError(err).Error("Failed attestation slashing protection check")
|
||||
log.WithFields(
|
||||
attestationLogFields(pubKey, indexedAtt),
|
||||
).Debug("Attempted slashable attestation details")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -107,10 +107,10 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot uint64, pubKey [
|
||||
|
||||
indexedAtt.Signature = sig
|
||||
if err := v.postAttSignUpdate(ctx, indexedAtt, pubKey, signingRoot); err != nil {
|
||||
log.WithFields(logrus.Fields{
|
||||
"sourceEpoch": indexedAtt.Data.Source.Epoch,
|
||||
"targetEpoch": indexedAtt.Data.Target.Epoch,
|
||||
}).WithError(err).Error("Failed post attestation signing updates")
|
||||
log.WithError(err).Error("Failed attestation slashing protection check")
|
||||
log.WithFields(
|
||||
attestationLogFields(pubKey, indexedAtt),
|
||||
).Debug("Attempted slashable attestation details")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -227,3 +227,17 @@ func (v *validator) waitToSlotOneThird(ctx context.Context, slot uint64) {
|
||||
finalTime := startTime.Add(delay)
|
||||
time.Sleep(timeutils.Until(finalTime))
|
||||
}
|
||||
|
||||
func attestationLogFields(pubKey [48]byte, indexedAtt *ethpb.IndexedAttestation) logrus.Fields {
|
||||
return logrus.Fields{
|
||||
"attesterPublicKey": fmt.Sprintf("%#x", pubKey),
|
||||
"attestationSlot": indexedAtt.Data.Slot,
|
||||
"committeeIndex": indexedAtt.Data.CommitteeIndex,
|
||||
"beaconBlockRoot": fmt.Sprintf("%#x", indexedAtt.Data.BeaconBlockRoot),
|
||||
"sourceEpoch": indexedAtt.Data.Source.Epoch,
|
||||
"sourceRoot": fmt.Sprintf("%#x", indexedAtt.Data.Source.Root),
|
||||
"targetEpoch": indexedAtt.Data.Target.Epoch,
|
||||
"targetRoot": fmt.Sprintf("%#x", indexedAtt.Data.Target.Root),
|
||||
"signature": fmt.Sprintf("%#x", indexedAtt.Signature),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/validator/db/kv"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var failedAttLocalProtectionErr = "attempted to make slashable attestation, rejected by local slashing protection"
|
||||
@@ -27,12 +28,24 @@ func (v *validator) preAttSignValidations(ctx context.Context, indexedAtt *ethpb
|
||||
log.WithError(err).Error("Could not get domain and signing root from attestation")
|
||||
return err
|
||||
}
|
||||
if ok && isNewAttSlashable(ctx, attesterHistory, indexedAtt.Data.Source.Epoch, indexedAtt.Data.Target.Epoch, sr) {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorAttestFailVec.WithLabelValues(fmtKey).Inc()
|
||||
if ok {
|
||||
slashable, err := isNewAttSlashable(
|
||||
ctx,
|
||||
attesterHistory,
|
||||
indexedAtt.Data.Source.Epoch,
|
||||
indexedAtt.Data.Target.Epoch,
|
||||
sr,
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not check if attestation is slashable")
|
||||
}
|
||||
return errors.New(failedAttLocalProtectionErr)
|
||||
} else if !ok {
|
||||
if slashable {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorAttestFailVec.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
return errors.New(failedAttLocalProtectionErr)
|
||||
}
|
||||
} else {
|
||||
log.WithField("publicKey", fmtKey).Debug("Could not get local slashing protection data for validator in pre validation")
|
||||
}
|
||||
|
||||
@@ -53,14 +66,35 @@ func (v *validator) postAttSignUpdate(ctx context.Context, indexedAtt *ethpb.Ind
|
||||
defer v.attesterHistoryByPubKeyLock.Unlock()
|
||||
attesterHistory, ok := v.attesterHistoryByPubKey[pubKey]
|
||||
if ok {
|
||||
if isNewAttSlashable(ctx, attesterHistory, indexedAtt.Data.Source.Epoch, indexedAtt.Data.Target.Epoch, signingRoot) {
|
||||
slashable, err := isNewAttSlashable(
|
||||
ctx,
|
||||
attesterHistory,
|
||||
indexedAtt.Data.Source.Epoch,
|
||||
indexedAtt.Data.Target.Epoch,
|
||||
signingRoot,
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not check if attestation is slashable")
|
||||
}
|
||||
if slashable {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorAttestFailVec.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
return errors.New(failedAttLocalProtectionErr)
|
||||
}
|
||||
attesterHistory = markAttestationForTargetEpoch(ctx, attesterHistory, indexedAtt.Data.Source.Epoch, indexedAtt.Data.Target.Epoch, signingRoot)
|
||||
v.attesterHistoryByPubKey[pubKey] = attesterHistory
|
||||
newHistory, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(
|
||||
ctx,
|
||||
attesterHistory,
|
||||
indexedAtt.Data.Target.Epoch,
|
||||
&kv.HistoryData{
|
||||
Source: indexedAtt.Data.Source.Epoch,
|
||||
SigningRoot: signingRoot[:],
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not mark epoch %d as attested", indexedAtt.Data.Target.Epoch)
|
||||
}
|
||||
v.attesterHistoryByPubKey[pubKey] = newHistory
|
||||
} else {
|
||||
log.WithField("publicKey", fmtKey).Debug("Could not get local slashing protection data for validator in post validation")
|
||||
}
|
||||
@@ -78,114 +112,139 @@ func (v *validator) postAttSignUpdate(ctx context.Context, indexedAtt *ethpb.Ind
|
||||
|
||||
// isNewAttSlashable uses the attestation history to determine if an attestation of sourceEpoch
|
||||
// and targetEpoch would be slashable. It can detect double, surrounding, and surrounded votes.
|
||||
func isNewAttSlashable(ctx context.Context, history kv.EncHistoryData, sourceEpoch, targetEpoch uint64, signingRoot [32]byte) bool {
|
||||
func isNewAttSlashable(
|
||||
ctx context.Context,
|
||||
history kv.EncHistoryData,
|
||||
sourceEpoch,
|
||||
targetEpoch uint64,
|
||||
signingRoot [32]byte,
|
||||
) (bool, error) {
|
||||
if history == nil {
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod
|
||||
// Previously pruned, we should return false.
|
||||
latestEpochWritten, err := history.GetLatestEpochWritten(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get latest epoch written from encapsulated data")
|
||||
return false
|
||||
return false, err
|
||||
}
|
||||
|
||||
if latestEpochWritten >= wsPeriod && targetEpoch <= latestEpochWritten-wsPeriod { //Underflow protected older then weak subjectivity check.
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check if there has already been a vote for this target epoch.
|
||||
hd, err := history.GetTargetData(ctx, targetEpoch)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("Could not get target data for target epoch: %d", targetEpoch)
|
||||
return false
|
||||
return false, errors.Wrapf(err, "could not get target data for epoch: %d", targetEpoch)
|
||||
}
|
||||
if !hd.IsEmpty() && !bytes.Equal(signingRoot[:], hd.SigningRoot) {
|
||||
return true
|
||||
log.WithFields(logrus.Fields{
|
||||
"signingRoot": fmt.Sprintf("%#x", signingRoot),
|
||||
"targetEpoch": targetEpoch,
|
||||
"previouslyAttestedSigningRoot": fmt.Sprintf("%#x", hd.SigningRoot),
|
||||
}).Warn("Attempted to submit a double vote, but blocked by slashing protection")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Check if the new attestation would be surrounding another attestation.
|
||||
isSurround, err := isSurroundVote(ctx, history, latestEpochWritten, sourceEpoch, targetEpoch)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "could not check if attestation is surround vote")
|
||||
}
|
||||
return isSurround, nil
|
||||
}
|
||||
|
||||
func isSurroundVote(
|
||||
ctx context.Context,
|
||||
history kv.EncHistoryData,
|
||||
latestEpochWritten,
|
||||
sourceEpoch,
|
||||
targetEpoch uint64,
|
||||
) (bool, error) {
|
||||
for i := sourceEpoch; i <= targetEpoch; i++ {
|
||||
// Unattested for epochs are marked as (*kv.HistoryData)(nil).
|
||||
historyBoundary := safeTargetToSource(ctx, history, i)
|
||||
if historyBoundary.IsEmpty() {
|
||||
historicalAtt, err := checkHistoryAtTargetEpoch(ctx, history, latestEpochWritten, i)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "could not check historical attestation at target epoch: %d", i)
|
||||
}
|
||||
if historicalAtt.IsEmpty() {
|
||||
continue
|
||||
}
|
||||
if historyBoundary.Source > sourceEpoch {
|
||||
return true
|
||||
prevTarget := i
|
||||
prevSource := historicalAtt.Source
|
||||
if surroundingPrevAttestation(prevSource, prevTarget, sourceEpoch, targetEpoch) {
|
||||
// Surrounding attestation caught.
|
||||
log.WithFields(logrus.Fields{
|
||||
"targetEpoch": targetEpoch,
|
||||
"sourceEpoch": sourceEpoch,
|
||||
"previouslyAttestedTargetEpoch": prevTarget,
|
||||
"previouslyAttestedSourceEpoch": prevSource,
|
||||
}).Warn("Attempted to submit a surrounding attestation, but blocked by slashing protection")
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the new attestation is being surrounded.
|
||||
for i := targetEpoch; i <= latestEpochWritten; i++ {
|
||||
h := safeTargetToSource(ctx, history, i)
|
||||
if h.IsEmpty() {
|
||||
historicalAtt, err := checkHistoryAtTargetEpoch(ctx, history, latestEpochWritten, i)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "could not check historical attestation at target epoch: %d", i)
|
||||
}
|
||||
if historicalAtt.IsEmpty() {
|
||||
continue
|
||||
}
|
||||
if h.Source < sourceEpoch {
|
||||
return true
|
||||
prevTarget := i
|
||||
prevSource := historicalAtt.Source
|
||||
if surroundedByPrevAttestation(prevSource, prevTarget, sourceEpoch, targetEpoch) {
|
||||
// Surrounded attestation caught.
|
||||
log.WithFields(logrus.Fields{
|
||||
"targetEpoch": targetEpoch,
|
||||
"sourceEpoch": sourceEpoch,
|
||||
"previouslyAttestedTargetEpoch": prevTarget,
|
||||
"previouslyAttestedSourceEpoch": prevSource,
|
||||
}).Warn("Attempted to submit a surrounded attestation, but blocked by slashing protection")
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// markAttestationForTargetEpoch returns the modified attestation history with the passed-in epochs marked
|
||||
// as attested for. This is done to prevent the validator client from signing any slashable attestations.
|
||||
func markAttestationForTargetEpoch(ctx context.Context, history kv.EncHistoryData, sourceEpoch, targetEpoch uint64, signingRoot [32]byte) kv.EncHistoryData {
|
||||
if history == nil {
|
||||
return nil
|
||||
}
|
||||
func surroundedByPrevAttestation(prevSource, prevTarget, newSource, newTarget uint64) bool {
|
||||
return prevSource < newSource && newTarget < prevTarget
|
||||
}
|
||||
|
||||
func surroundingPrevAttestation(prevSource, prevTarget, newSource, newTarget uint64) bool {
|
||||
return newSource < prevSource && prevTarget < newTarget
|
||||
}
|
||||
|
||||
// Checks that the difference between the latest epoch written and
|
||||
// target epoch is greater than or equal to the weak subjectivity period.
|
||||
func differenceOutsideWeakSubjectivityBounds(latestEpochWritten, targetEpoch uint64) bool {
|
||||
wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod
|
||||
latestEpochWritten, err := history.GetLatestEpochWritten(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get latest epoch written from encapsulated data")
|
||||
return nil
|
||||
}
|
||||
if targetEpoch > latestEpochWritten {
|
||||
// If the target epoch to mark is ahead of latest written epoch, override the old targets and mark the requested epoch.
|
||||
// Limit the overwriting to one weak subjectivity period as further is not needed.
|
||||
maxToWrite := latestEpochWritten + wsPeriod
|
||||
for i := latestEpochWritten + 1; i < targetEpoch && i <= maxToWrite; i++ {
|
||||
history, err = history.SetTargetData(ctx, i%wsPeriod, &kv.HistoryData{Source: params.BeaconConfig().FarFutureEpoch})
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not set target to the encapsulated data")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
history, err = history.SetLatestEpochWritten(ctx, targetEpoch)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not set latest epoch written to the encapsulated data")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
history, err = history.SetTargetData(ctx, targetEpoch%wsPeriod, &kv.HistoryData{Source: sourceEpoch, SigningRoot: signingRoot[:]})
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not set target to the encapsulated data")
|
||||
return nil
|
||||
}
|
||||
return history
|
||||
return latestEpochWritten >= wsPeriod && targetEpoch <= latestEpochWritten-wsPeriod
|
||||
}
|
||||
|
||||
// safeTargetToSource makes sure the epoch accessed is within bounds, and if it's not it at
|
||||
// returns the "default" nil value.
|
||||
func safeTargetToSource(ctx context.Context, history kv.EncHistoryData, targetEpoch uint64) *kv.HistoryData {
|
||||
// Returns the actual attesting history at a specified target epoch.
|
||||
// The response is nil if there was no attesting history at that epoch.
|
||||
func checkHistoryAtTargetEpoch(
|
||||
ctx context.Context,
|
||||
history kv.EncHistoryData,
|
||||
latestEpochWritten,
|
||||
targetEpoch uint64,
|
||||
) (*kv.HistoryData, error) {
|
||||
wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod
|
||||
latestEpochWritten, err := history.GetLatestEpochWritten(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get latest epoch written from encapsulated data")
|
||||
return nil
|
||||
if differenceOutsideWeakSubjectivityBounds(latestEpochWritten, targetEpoch) {
|
||||
return nil, nil
|
||||
}
|
||||
// Ignore target epoch is > latest written.
|
||||
if targetEpoch > latestEpochWritten {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
if latestEpochWritten >= wsPeriod && targetEpoch < latestEpochWritten-wsPeriod { //Underflow protected older then weak subjectivity check.
|
||||
return nil
|
||||
}
|
||||
hd, err := history.GetTargetData(ctx, targetEpoch%wsPeriod)
|
||||
historicalAtt, err := history.GetTargetData(ctx, targetEpoch%wsPeriod)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("Could not get target data for target epoch: %d", targetEpoch)
|
||||
return nil
|
||||
return nil, errors.Wrapf(err, "could not get target data for target epoch: %d", targetEpoch)
|
||||
}
|
||||
return hd
|
||||
return historicalAtt, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -168,7 +169,12 @@ func TestAttestationHistory_BlocksDoubleAttestation(t *testing.T) {
|
||||
newAttSource := uint64(0)
|
||||
newAttTarget := uint64(3)
|
||||
sr1 := [32]byte{1}
|
||||
history = markAttestationForTargetEpoch(ctx, history, newAttSource, newAttTarget, sr1)
|
||||
newHist, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, newAttTarget, &kv.HistoryData{
|
||||
Source: newAttSource,
|
||||
SigningRoot: sr1[:],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history = newHist
|
||||
lew, err := history.GetLatestEpochWritten(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newAttTarget, lew, "Unexpected latest epoch written")
|
||||
@@ -177,7 +183,9 @@ func TestAttestationHistory_BlocksDoubleAttestation(t *testing.T) {
|
||||
sr2 := [32]byte{2}
|
||||
newAttSource = uint64(1)
|
||||
newAttTarget = uint64(3)
|
||||
if !isNewAttSlashable(ctx, history, newAttSource, newAttTarget, sr2) {
|
||||
slashable, err := isNewAttSlashable(ctx, history, newAttSource, newAttTarget, sr2)
|
||||
require.NoError(t, err)
|
||||
if !slashable {
|
||||
t.Fatalf("Expected attestation of source %d and target %d to be considered slashable", newAttSource, newAttTarget)
|
||||
}
|
||||
}
|
||||
@@ -294,15 +302,27 @@ func TestAttestationHistory_Prunes(t *testing.T) {
|
||||
history := kv.NewAttestationHistoryArray(0)
|
||||
|
||||
// Try an attestation on totally unmarked history, should not be slashable.
|
||||
require.Equal(t, false, isNewAttSlashable(ctx, history, 0, wsPeriod+5, signingRoot), "Should not be slashable")
|
||||
slashable, err := isNewAttSlashable(ctx, history, 0, wsPeriod+5, signingRoot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, slashable, "Should not be slashable")
|
||||
|
||||
// Mark attestations spanning epochs 0 to 3 and 6 to 9.
|
||||
prunedNewAttSource := uint64(0)
|
||||
prunedNewAttTarget := uint64(3)
|
||||
history = markAttestationForTargetEpoch(ctx, history, prunedNewAttSource, prunedNewAttTarget, signingRoot)
|
||||
newHist, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, prunedNewAttTarget, &kv.HistoryData{
|
||||
Source: prunedNewAttSource,
|
||||
SigningRoot: signingRoot[:],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history = newHist
|
||||
newAttSource := prunedNewAttSource + 6
|
||||
newAttTarget := prunedNewAttTarget + 6
|
||||
history = markAttestationForTargetEpoch(ctx, history, newAttSource, newAttTarget, signingRoot2)
|
||||
newHist, err = kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, newAttTarget, &kv.HistoryData{
|
||||
Source: newAttSource,
|
||||
SigningRoot: signingRoot2[:],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history = newHist
|
||||
lte, err := history.GetLatestEpochWritten(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newAttTarget, lte, "Unexpected latest epoch")
|
||||
@@ -310,24 +330,39 @@ func TestAttestationHistory_Prunes(t *testing.T) {
|
||||
// Mark an attestation spanning epochs 54000 to 54003.
|
||||
farNewAttSource := newAttSource + wsPeriod
|
||||
farNewAttTarget := newAttTarget + wsPeriod
|
||||
history = markAttestationForTargetEpoch(ctx, history, farNewAttSource, farNewAttTarget, signingRoot3)
|
||||
newHist, err = kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, farNewAttTarget, &kv.HistoryData{
|
||||
Source: farNewAttSource,
|
||||
SigningRoot: signingRoot3[:],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history = newHist
|
||||
lte, err = history.GetLatestEpochWritten(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, farNewAttTarget, lte, "Unexpected latest epoch")
|
||||
|
||||
require.Equal(t, (*kv.HistoryData)(nil), safeTargetToSource(ctx, history, prunedNewAttTarget), "Unexpectedly marked attestation")
|
||||
require.Equal(t, farNewAttSource, safeTargetToSource(ctx, history, farNewAttTarget).Source, "Unexpectedly marked attestation")
|
||||
histAtt, err := checkHistoryAtTargetEpoch(ctx, history, lte, prunedNewAttTarget)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, (*kv.HistoryData)(nil), histAtt, "Unexpectedly marked attestation")
|
||||
histAtt, err = checkHistoryAtTargetEpoch(ctx, history, lte, farNewAttTarget)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, farNewAttSource, histAtt.Source, "Unexpectedly marked attestation")
|
||||
|
||||
// Try an attestation from existing source to outside prune, should slash.
|
||||
if !isNewAttSlashable(ctx, history, newAttSource, farNewAttTarget, signingRoot4) {
|
||||
slashable, err = isNewAttSlashable(ctx, history, newAttSource, farNewAttTarget, signingRoot4)
|
||||
require.NoError(t, err)
|
||||
if !slashable {
|
||||
t.Fatalf("Expected attestation of source %d, target %d to be considered slashable", newAttSource, farNewAttTarget)
|
||||
}
|
||||
// Try an attestation from before existing target to outside prune, should slash.
|
||||
if !isNewAttSlashable(ctx, history, newAttTarget-1, farNewAttTarget, signingRoot4) {
|
||||
slashable, err = isNewAttSlashable(ctx, history, newAttTarget-1, farNewAttTarget, signingRoot4)
|
||||
require.NoError(t, err)
|
||||
if !slashable {
|
||||
t.Fatalf("Expected attestation of source %d, target %d to be considered slashable", newAttTarget-1, farNewAttTarget)
|
||||
}
|
||||
// Try an attestation larger than pruning amount, should slash.
|
||||
if !isNewAttSlashable(ctx, history, 0, farNewAttTarget+5, signingRoot4) {
|
||||
slashable, err = isNewAttSlashable(ctx, history, 0, farNewAttTarget+5, signingRoot4)
|
||||
require.NoError(t, err)
|
||||
if !slashable {
|
||||
t.Fatalf("Expected attestation of source 0, target %d to be considered slashable", farNewAttTarget+5)
|
||||
}
|
||||
}
|
||||
@@ -340,7 +375,12 @@ func TestAttestationHistory_BlocksSurroundedAttestation(t *testing.T) {
|
||||
signingRoot := [32]byte{1}
|
||||
newAttSource := uint64(0)
|
||||
newAttTarget := uint64(3)
|
||||
history = markAttestationForTargetEpoch(ctx, history, newAttSource, newAttTarget, signingRoot)
|
||||
newHist, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, newAttTarget, &kv.HistoryData{
|
||||
Source: newAttSource,
|
||||
SigningRoot: signingRoot[:],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history = newHist
|
||||
lte, err := history.GetLatestEpochWritten(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newAttTarget, lte)
|
||||
@@ -348,7 +388,9 @@ func TestAttestationHistory_BlocksSurroundedAttestation(t *testing.T) {
|
||||
// Try an attestation that should be slashable (being surrounded) spanning epochs 1 to 2.
|
||||
newAttSource = uint64(1)
|
||||
newAttTarget = uint64(2)
|
||||
require.Equal(t, true, isNewAttSlashable(ctx, history, newAttSource, newAttTarget, signingRoot), "Expected slashable attestation")
|
||||
slashable, err := isNewAttSlashable(ctx, history, newAttSource, newAttTarget, signingRoot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, slashable, "Expected slashable attestation")
|
||||
}
|
||||
|
||||
func TestAttestationHistory_BlocksSurroundingAttestation(t *testing.T) {
|
||||
@@ -359,7 +401,12 @@ func TestAttestationHistory_BlocksSurroundingAttestation(t *testing.T) {
|
||||
// Mark an attestation spanning epochs 1 to 2.
|
||||
newAttSource := uint64(1)
|
||||
newAttTarget := uint64(2)
|
||||
history = markAttestationForTargetEpoch(ctx, history, newAttSource, newAttTarget, signingRoot)
|
||||
newHist, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, newAttTarget, &kv.HistoryData{
|
||||
Source: newAttSource,
|
||||
SigningRoot: signingRoot[:],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history = newHist
|
||||
lte, err := history.GetLatestEpochWritten(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newAttTarget, lte)
|
||||
@@ -370,5 +417,346 @@ func TestAttestationHistory_BlocksSurroundingAttestation(t *testing.T) {
|
||||
// Try an attestation that should be slashable (surrounding) spanning epochs 0 to 3.
|
||||
newAttSource = uint64(0)
|
||||
newAttTarget = uint64(3)
|
||||
require.Equal(t, true, isNewAttSlashable(ctx, history, newAttSource, newAttTarget, signingRoot))
|
||||
slashable, err := isNewAttSlashable(ctx, history, newAttSource, newAttTarget, signingRoot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, slashable)
|
||||
}
|
||||
|
||||
func Test_isSurroundVote(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
source := uint64(1)
|
||||
target := uint64(4)
|
||||
history := kv.NewAttestationHistoryArray(0)
|
||||
signingRoot1 := bytesutil.PadTo([]byte{1}, 32)
|
||||
hist, err := history.SetTargetData(ctx, target, &kv.HistoryData{
|
||||
Source: source,
|
||||
SigningRoot: signingRoot1,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history = hist
|
||||
tests := []struct {
|
||||
name string
|
||||
history kv.EncHistoryData
|
||||
latestEpochWritten uint64
|
||||
sourceEpoch uint64
|
||||
targetEpoch uint64
|
||||
want bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "ignores attestations outside of weak subjectivity bounds",
|
||||
history: kv.NewAttestationHistoryArray(0),
|
||||
latestEpochWritten: 2 * params.BeaconConfig().WeakSubjectivityPeriod,
|
||||
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
|
||||
sourceEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "detects surrounding attestations",
|
||||
history: history,
|
||||
latestEpochWritten: target,
|
||||
targetEpoch: target + 1,
|
||||
sourceEpoch: source - 1,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "detects surrounded attestations",
|
||||
history: history,
|
||||
latestEpochWritten: target,
|
||||
targetEpoch: target - 1,
|
||||
sourceEpoch: source + 1,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "new attestation source == old source, but new target < old target",
|
||||
history: history,
|
||||
latestEpochWritten: target,
|
||||
targetEpoch: target - 1,
|
||||
sourceEpoch: source,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "new attestation source > old source, but new target == old target",
|
||||
history: history,
|
||||
latestEpochWritten: target,
|
||||
targetEpoch: target,
|
||||
sourceEpoch: source + 1,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "new attestation source and targets equal to old one",
|
||||
history: history,
|
||||
latestEpochWritten: target,
|
||||
targetEpoch: target,
|
||||
sourceEpoch: source,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "new attestation source == old source, but new target > old target",
|
||||
history: history,
|
||||
latestEpochWritten: target,
|
||||
targetEpoch: target + 1,
|
||||
sourceEpoch: source,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "new attestation source < old source, but new target == old target",
|
||||
history: history,
|
||||
latestEpochWritten: target,
|
||||
targetEpoch: target,
|
||||
sourceEpoch: source - 1,
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := isSurroundVote(ctx, tt.history, tt.latestEpochWritten, tt.sourceEpoch, tt.targetEpoch)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("isSurroundVote() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("isSurroundVote() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_surroundedByPrevAttestation(t *testing.T) {
|
||||
type args struct {
|
||||
oldSource uint64
|
||||
oldTarget uint64
|
||||
newSource uint64
|
||||
newTarget uint64
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "0 values returns false",
|
||||
args: args{
|
||||
oldSource: 0,
|
||||
oldTarget: 0,
|
||||
newSource: 0,
|
||||
newTarget: 0,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "new attestation is surrounded by an old one",
|
||||
args: args{
|
||||
oldSource: 2,
|
||||
oldTarget: 6,
|
||||
newSource: 3,
|
||||
newTarget: 5,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "new attestation source and targets equal to old one",
|
||||
args: args{
|
||||
oldSource: 3,
|
||||
oldTarget: 5,
|
||||
newSource: 3,
|
||||
newTarget: 5,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "new attestation source == old source, but new target < old target",
|
||||
args: args{
|
||||
oldSource: 3,
|
||||
oldTarget: 5,
|
||||
newSource: 3,
|
||||
newTarget: 4,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "new attestation source > old source, but new target == old target",
|
||||
args: args{
|
||||
oldSource: 3,
|
||||
oldTarget: 5,
|
||||
newSource: 4,
|
||||
newTarget: 5,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := surroundedByPrevAttestation(tt.args.oldSource, tt.args.oldTarget, tt.args.newSource, tt.args.newTarget); got != tt.want {
|
||||
t.Errorf("surroundedByPrevAttestation() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_surroundingPrevAttestation(t *testing.T) {
|
||||
type args struct {
|
||||
oldSource uint64
|
||||
oldTarget uint64
|
||||
newSource uint64
|
||||
newTarget uint64
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "0 values returns false",
|
||||
args: args{
|
||||
oldSource: 0,
|
||||
oldTarget: 0,
|
||||
newSource: 0,
|
||||
newTarget: 0,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "new attestation is surrounding an old one",
|
||||
args: args{
|
||||
oldSource: 3,
|
||||
oldTarget: 5,
|
||||
newSource: 2,
|
||||
newTarget: 6,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "new attestation source and targets equal to old one",
|
||||
args: args{
|
||||
oldSource: 3,
|
||||
oldTarget: 5,
|
||||
newSource: 3,
|
||||
newTarget: 5,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "new attestation source == old source, but new target > old target",
|
||||
args: args{
|
||||
oldSource: 3,
|
||||
oldTarget: 5,
|
||||
newSource: 3,
|
||||
newTarget: 6,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "new attestation source < old source, but new target == old target",
|
||||
args: args{
|
||||
oldSource: 3,
|
||||
oldTarget: 5,
|
||||
newSource: 2,
|
||||
newTarget: 5,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := surroundingPrevAttestation(tt.args.oldSource, tt.args.oldTarget, tt.args.newSource, tt.args.newTarget); got != tt.want {
|
||||
t.Errorf("surroundingPrevAttestation() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_checkHistoryAtTargetEpoch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
history := kv.NewAttestationHistoryArray(0)
|
||||
signingRoot1 := bytesutil.PadTo([]byte{1}, 32)
|
||||
hist, err := history.SetTargetData(ctx, 1, &kv.HistoryData{
|
||||
Source: 0,
|
||||
SigningRoot: signingRoot1,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history = hist
|
||||
tests := []struct {
|
||||
name string
|
||||
history kv.EncHistoryData
|
||||
latestEpochWritten uint64
|
||||
targetEpoch uint64
|
||||
want *kv.HistoryData
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "ignores difference in epochs outside of weak subjectivity bounds",
|
||||
history: kv.NewAttestationHistoryArray(0),
|
||||
latestEpochWritten: 2 * params.BeaconConfig().WeakSubjectivityPeriod,
|
||||
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
|
||||
want: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ignores target epoch > latest written epoch",
|
||||
history: kv.NewAttestationHistoryArray(0),
|
||||
latestEpochWritten: params.BeaconConfig().WeakSubjectivityPeriod,
|
||||
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod + 1,
|
||||
want: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "target epoch == latest written epoch should return correct results",
|
||||
history: history,
|
||||
latestEpochWritten: 1,
|
||||
targetEpoch: 1,
|
||||
want: &kv.HistoryData{
|
||||
Source: 0,
|
||||
SigningRoot: signingRoot1,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := checkHistoryAtTargetEpoch(ctx, tt.history, tt.latestEpochWritten, tt.targetEpoch)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("checkHistoryAtTargetEpoch() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("checkHistoryAtTargetEpoch() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_differenceOutsideWeakSubjectivityBounds(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want bool
|
||||
latestEpochWritten uint64
|
||||
targetEpoch uint64
|
||||
}{
|
||||
{
|
||||
name: "difference of weak subjectivity period - 1 returns false",
|
||||
latestEpochWritten: (2 * params.BeaconConfig().WeakSubjectivityPeriod) - 1,
|
||||
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "difference of weak subjectivity period returns true",
|
||||
latestEpochWritten: 2 * params.BeaconConfig().WeakSubjectivityPeriod,
|
||||
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "difference > weak subjectivity period returns true",
|
||||
latestEpochWritten: (2 * params.BeaconConfig().WeakSubjectivityPeriod) + 1,
|
||||
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := differenceOutsideWeakSubjectivityBounds(tt.latestEpochWritten, tt.targetEpoch); got != tt.want {
|
||||
t.Errorf("differenceOutsideWeakSubjectivityBounds() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,9 @@ func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]by
|
||||
}
|
||||
|
||||
if err := v.preBlockSignValidations(ctx, pubKey, b); err != nil {
|
||||
log.WithField("slot", b.Slot).WithError(err).Error("Failed block safety check")
|
||||
log.WithFields(
|
||||
blockLogFields(pubKey, b, nil),
|
||||
).WithError(err).Error("Failed block slashing protection check")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -86,7 +88,9 @@ func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]by
|
||||
}
|
||||
|
||||
if err := v.postBlockSignUpdate(ctx, pubKey, blk, domain); err != nil {
|
||||
log.WithField("slot", blk.Block.Slot).WithError(err).Error("Failed post block signing validations")
|
||||
log.WithFields(
|
||||
blockLogFields(pubKey, b, sig),
|
||||
).WithError(err).Error("Failed block slashing protection check")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/blockutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var failedPreBlockSignLocalErr = "attempted to sign a double proposal, block rejected by local protection"
|
||||
@@ -83,3 +84,15 @@ func (v *validator) postBlockSignUpdate(ctx context.Context, pubKey [48]byte, bl
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func blockLogFields(pubKey [48]byte, blk *ethpb.BeaconBlock, sig []byte) logrus.Fields {
|
||||
fields := logrus.Fields{
|
||||
"proposerPublicKey": fmt.Sprintf("%#x", pubKey),
|
||||
"proposerIndex": blk.ProposerIndex,
|
||||
"blockSlot": blk.Slot,
|
||||
}
|
||||
if sig != nil {
|
||||
fields["signature"] = fmt.Sprintf("%#x", sig)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
@@ -126,7 +126,6 @@ func (v *ValidatorService) Start() {
|
||||
dialOpts := ConstructDialOptions(
|
||||
v.maxCallRecvMsgSize,
|
||||
v.withCert,
|
||||
v.grpcHeaders,
|
||||
v.grpcRetries,
|
||||
v.grpcRetryDelay,
|
||||
streamInterceptor,
|
||||
@@ -134,6 +133,18 @@ func (v *ValidatorService) Start() {
|
||||
if dialOpts == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, hdr := range v.grpcHeaders {
|
||||
if hdr != "" {
|
||||
ss := strings.Split(hdr, "=")
|
||||
if len(ss) != 2 {
|
||||
log.Warnf("Incorrect gRPC header flag format. Skipping %v", hdr)
|
||||
continue
|
||||
}
|
||||
v.ctx = metadata.AppendToOutgoingContext(v.ctx, ss[0], ss[1])
|
||||
}
|
||||
}
|
||||
|
||||
conn, err := grpc.DialContext(v.ctx, v.endpoint, dialOpts...)
|
||||
if err != nil {
|
||||
log.Errorf("Could not dial endpoint: %s, %v", v.endpoint, err)
|
||||
@@ -236,7 +247,6 @@ func (v *ValidatorService) recheckKeys(ctx context.Context) {
|
||||
func ConstructDialOptions(
|
||||
maxCallRecvMsgSize int,
|
||||
withCert string,
|
||||
grpcHeaders []string,
|
||||
grpcRetries uint,
|
||||
grpcRetryDelay time.Duration,
|
||||
extraOpts ...grpc.DialOption,
|
||||
@@ -260,25 +270,12 @@ func ConstructDialOptions(
|
||||
maxCallRecvMsgSize = 10 * 5 << 20 // Default 50Mb
|
||||
}
|
||||
|
||||
md := make(metadata.MD)
|
||||
for _, hdr := range grpcHeaders {
|
||||
if hdr != "" {
|
||||
ss := strings.Split(hdr, "=")
|
||||
if len(ss) != 2 {
|
||||
log.Warnf("Incorrect gRPC header flag format. Skipping %v", hdr)
|
||||
continue
|
||||
}
|
||||
md.Set(ss[0], ss[1])
|
||||
}
|
||||
}
|
||||
|
||||
dialOpts := []grpc.DialOption{
|
||||
transportSecurity,
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(maxCallRecvMsgSize),
|
||||
grpc_retry.WithMax(grpcRetries),
|
||||
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(grpcRetryDelay)),
|
||||
grpc.Header(&md),
|
||||
),
|
||||
grpc.WithStatsHandler(&ocgrpc.ClientHandler{}),
|
||||
grpc.WithUnaryInterceptor(middleware.ChainUnaryClient(
|
||||
|
||||
@@ -801,10 +801,21 @@ func TestSaveProtections_OK(t *testing.T) {
|
||||
attesterHistoryByPubKey: cleanHistories,
|
||||
}
|
||||
|
||||
sr := [32]byte{1}
|
||||
history1 := cleanHistories[pubKey1]
|
||||
history1 = markAttestationForTargetEpoch(ctx, history1, 0, 1, [32]byte{1})
|
||||
newHist, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history1, 1, &kv.HistoryData{
|
||||
Source: 0,
|
||||
SigningRoot: sr[:],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history1 = newHist
|
||||
|
||||
history2 := markAttestationForTargetEpoch(ctx, history1, 2, 3, [32]byte{2})
|
||||
sr2 := [32]byte{2}
|
||||
history2, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history1, 3, &kv.HistoryData{
|
||||
Source: 2,
|
||||
SigningRoot: sr2[:],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
cleanHistories[pubKey1] = history1
|
||||
cleanHistories[pubKey2] = history2
|
||||
@@ -835,7 +846,13 @@ func TestSaveProtection_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
history1 := cleanHistories[pubKey1]
|
||||
history1 = markAttestationForTargetEpoch(ctx, history1, 0, 1, [32]byte{1})
|
||||
sr := [32]byte{1}
|
||||
newHist, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history1, 1, &kv.HistoryData{
|
||||
Source: 0,
|
||||
SigningRoot: sr[:],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history1 = newHist
|
||||
|
||||
cleanHistories[pubKey1] = history1
|
||||
|
||||
|
||||
@@ -127,6 +127,49 @@ func (hd EncHistoryData) SetTargetData(ctx context.Context, target uint64, histo
|
||||
return hd, nil
|
||||
}
|
||||
|
||||
// MarkAllAsAttestedSinceLatestWrittenEpoch returns an attesting history with specified target+epoch pairs
|
||||
// since the latest written epoch up to the incoming attestation's target epoch as attested for.
|
||||
func MarkAllAsAttestedSinceLatestWrittenEpoch(
|
||||
ctx context.Context,
|
||||
hist EncHistoryData,
|
||||
incomingTarget uint64,
|
||||
incomingAtt *HistoryData,
|
||||
) (EncHistoryData, error) {
|
||||
wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod
|
||||
latestEpochWritten, err := hist.GetLatestEpochWritten(ctx)
|
||||
if err != nil {
|
||||
return EncHistoryData{}, errors.Wrap(err, "could not get latest epoch written from history")
|
||||
}
|
||||
currentHD := hist
|
||||
if incomingTarget > latestEpochWritten {
|
||||
// If the target epoch to mark is ahead of latest written epoch, override the old targets and mark the requested epoch.
|
||||
// Limit the overwriting to one weak subjectivity period as further is not needed.
|
||||
maxToWrite := latestEpochWritten + wsPeriod
|
||||
for i := latestEpochWritten + 1; i < incomingTarget && i <= maxToWrite; i++ {
|
||||
newHD, err := hist.SetTargetData(ctx, i%wsPeriod, &HistoryData{
|
||||
Source: params.BeaconConfig().FarFutureEpoch,
|
||||
})
|
||||
if err != nil {
|
||||
return EncHistoryData{}, errors.Wrap(err, "could not set target data")
|
||||
}
|
||||
currentHD = newHD
|
||||
}
|
||||
newHD, err := currentHD.SetLatestEpochWritten(ctx, incomingTarget)
|
||||
if err != nil {
|
||||
return EncHistoryData{}, errors.Wrap(err, "could not set latest epoch written")
|
||||
}
|
||||
currentHD = newHD
|
||||
}
|
||||
newHD, err := currentHD.SetTargetData(ctx, incomingTarget%wsPeriod, &HistoryData{
|
||||
Source: incomingAtt.Source,
|
||||
SigningRoot: incomingAtt.SigningRoot,
|
||||
})
|
||||
if err != nil {
|
||||
return EncHistoryData{}, errors.Wrap(err, "could not set target data")
|
||||
}
|
||||
return newHD, nil
|
||||
}
|
||||
|
||||
// AttestationHistoryForPubKeysV2 accepts an array of validator public keys and returns a mapping of corresponding attestation history.
|
||||
func (store *Store) AttestationHistoryForPubKeysV2(ctx context.Context, publicKeys [][48]byte) (map[[48]byte]EncHistoryData, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "Validator.AttestationHistoryForPubKeysV2")
|
||||
|
||||
@@ -247,6 +247,13 @@ func (s *ValidatorClient) initializeFromCLI(cliCtx *cli.Context) error {
|
||||
func (s *ValidatorClient) initializeForWeb(cliCtx *cli.Context) error {
|
||||
var keyManager keymanager.IKeymanager
|
||||
var err error
|
||||
walletDir := cliCtx.String(flags.WalletDirFlag.Name)
|
||||
defaultWalletPasswordFilePath := filepath.Join(walletDir, wallet.DefaultWalletPasswordFile)
|
||||
if fileutil.FileExists(defaultWalletPasswordFilePath) {
|
||||
if err := cliCtx.Set(flags.WalletPasswordFileFlag.Name, defaultWalletPasswordFilePath); err != nil {
|
||||
return errors.Wrap(err, "could not set default wallet password file path")
|
||||
}
|
||||
}
|
||||
// Read the wallet from the specified path.
|
||||
w, err := wallet.OpenWalletOrElseCli(cliCtx, func(cliCtx *cli.Context) (*wallet.Wallet, error) {
|
||||
return nil, nil
|
||||
|
||||
@@ -17,6 +17,7 @@ go_library(
|
||||
"//proto/validator/accounts/v2:go_default_library",
|
||||
"//shared/cmd:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/fileutil:go_default_library",
|
||||
"//shared/pagination:go_default_library",
|
||||
"//shared/petnames:go_default_library",
|
||||
@@ -66,6 +67,7 @@ go_test(
|
||||
"//proto/validator/accounts/v2:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/fileutil:go_default_library",
|
||||
"//shared/testutil/assert:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
|
||||
@@ -4,10 +4,14 @@ import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
ptypes "github.com/gogo/protobuf/types"
|
||||
"github.com/pkg/errors"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/fileutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/rand"
|
||||
"github.com/prysmaticlabs/prysm/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
|
||||
@@ -73,6 +77,9 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := writeWalletPasswordToDisk(walletDir, req.WalletPassword); err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not write wallet password to disk")
|
||||
}
|
||||
return &pb.CreateWalletResponse{
|
||||
Wallet: &pb.WalletResponse{
|
||||
WalletPath: walletDir,
|
||||
@@ -101,7 +108,9 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := writeWalletPasswordToDisk(walletDir, req.WalletPassword); err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not write wallet password to disk")
|
||||
}
|
||||
return &pb.CreateWalletResponse{
|
||||
Wallet: &pb.WalletResponse{
|
||||
WalletPath: walletDir,
|
||||
@@ -272,3 +281,14 @@ func (s *Server) initializeWallet(ctx context.Context, cfg *wallet.Config) error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeWalletPasswordToDisk(walletDir string, password string) error {
|
||||
if !featureconfig.Get().WriteWalletPasswordOnWebOnboarding {
|
||||
return nil
|
||||
}
|
||||
passwordFilePath := filepath.Join(walletDir, wallet.DefaultWalletPasswordFile)
|
||||
if fileutil.FileExists(passwordFilePath) {
|
||||
return fmt.Errorf("cannot write wallet password file as it already exists %s", passwordFilePath)
|
||||
}
|
||||
return fileutil.WriteFile(passwordFilePath, []byte(password))
|
||||
}
|
||||
|
||||
@@ -5,20 +5,24 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
ptypes "github.com/gogo/protobuf/types"
|
||||
"github.com/google/uuid"
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
|
||||
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/fileutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
"github.com/prysmaticlabs/prysm/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/imported"
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
)
|
||||
|
||||
func TestServer_CreateWallet_Imported(t *testing.T) {
|
||||
@@ -277,3 +281,30 @@ func TestServer_ImportKeystores_OK(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 3, len(keys))
|
||||
}
|
||||
|
||||
func Test_writeWalletPasswordToDisk(t *testing.T) {
|
||||
walletDir := setupWalletDir(t)
|
||||
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{
|
||||
WriteWalletPasswordOnWebOnboarding: false,
|
||||
})
|
||||
defer resetCfg()
|
||||
err := writeWalletPasswordToDisk(walletDir, "somepassword")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Expected a silent failure if the feature flag is not enabled.
|
||||
passwordFilePath := filepath.Join(walletDir, wallet.DefaultWalletPasswordFile)
|
||||
assert.Equal(t, false, fileutil.FileExists(passwordFilePath))
|
||||
resetCfg = featureconfig.InitWithReset(&featureconfig.Flags{
|
||||
WriteWalletPasswordOnWebOnboarding: true,
|
||||
})
|
||||
defer resetCfg()
|
||||
err = writeWalletPasswordToDisk(walletDir, "somepassword")
|
||||
require.NoError(t, err)
|
||||
|
||||
// File should have been written.
|
||||
assert.Equal(t, true, fileutil.FileExists(passwordFilePath))
|
||||
|
||||
// Attempting to write again should trigger an error.
|
||||
err = writeWalletPasswordToDisk(walletDir, "somepassword")
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user