Coverage Summary for Class: BlockTimeStampValidationRule (co.rsk.validators)
Class |
Class, %
|
Method, %
|
Line, %
|
BlockTimeStampValidationRule |
0%
(0/1)
|
0%
(0/10)
|
0%
(0/47)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package co.rsk.validators;
20
21 import co.rsk.bitcoinj.core.BtcBlock;
22 import co.rsk.bitcoinj.core.NetworkParameters;
23 import co.rsk.bitcoinj.params.RegTestParams;
24 import co.rsk.util.TimeProvider;
25 import org.ethereum.config.Constants;
26 import org.ethereum.config.blockchain.upgrades.ActivationConfig;
27 import org.ethereum.config.blockchain.upgrades.ConsensusRule;
28 import org.ethereum.core.Block;
29 import org.ethereum.core.BlockHeader;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 import javax.annotation.Nonnull;
34 import javax.annotation.Nullable;
35 import java.util.Objects;
36
37 /**
38 * Created by mario on 23/01/17.
39 */
40 public class BlockTimeStampValidationRule implements BlockParentDependantValidationRule, BlockHeaderParentDependantValidationRule, BlockValidationRule, BlockHeaderValidationRule {
41
42 private static final Logger logger = LoggerFactory.getLogger("blockvalidator");
43
44 private static final long MAX_TIMESTAMPS_DIFF_IN_SECS = Constants.getMaxTimestampsDiffInSecs();
45
46 private final int validPeriodLength;
47 private final ActivationConfig activationConfig;
48 private final TimeProvider timeProvider;
49 private final NetworkParameters bitcoinNetworkParameters;
50
51 public BlockTimeStampValidationRule(int validPeriodLength, ActivationConfig activationConfig,
52 TimeProvider timeProvider, NetworkParameters bitcoinNetworkParameters) {
53 this.validPeriodLength = validPeriodLength;
54 this.activationConfig = Objects.requireNonNull(activationConfig);
55 this.timeProvider = Objects.requireNonNull(timeProvider);
56 this.bitcoinNetworkParameters = Objects.requireNonNull(bitcoinNetworkParameters);
57 }
58
59 public BlockTimeStampValidationRule(int validPeriodLength, ActivationConfig activationConfig, TimeProvider timeProvider) {
60 this(validPeriodLength, activationConfig, timeProvider, RegTestParams.get());
61 }
62
63 public BlockTimeStampValidationRule(int validPeriodLength, ActivationConfig activationConfig) {
64 this(validPeriodLength, activationConfig, System::currentTimeMillis, RegTestParams.get());
65 }
66
67 @Override
68 public boolean isValid(Block block) {
69 return isValid(block.getHeader());
70 }
71
72 @Override
73 public boolean isValid(BlockHeader header) {
74 if (this.validPeriodLength == 0) {
75 return true;
76 }
77
78 final long currentTime = timeProvider.currentTimeMillis() / 1000L;
79 final long blockTime = header.getTimestamp();
80
81 boolean result = blockTime - currentTime <= this.validPeriodLength;
82
83 if (!result) {
84 logger.warn("Error validating block. Invalid timestamp {}.", blockTime);
85 }
86
87 return result && isBitcoinTimestampValid(header);
88 }
89
90 @Override
91 public boolean isValid(BlockHeader header, Block parent) {
92 if (this.validPeriodLength == 0) {
93 return true;
94 }
95
96 boolean result = this.isValid(header);
97
98 final long blockTime = header.getTimestamp();
99 final long parentTime = parent.getTimestamp();
100 result = result && (blockTime > parentTime);
101
102 if (!result) {
103 logger.warn("Error validating block. Invalid timestamp {} for parent timestamp {}", blockTime, parentTime);
104 }
105
106 return result;
107 }
108
109 @Override
110 public boolean isValid(Block block, Block parent) {
111 return isValid(block.getHeader(), parent);
112 }
113
114 private boolean isBitcoinTimestampValid(BlockHeader header) {
115 if (!activationConfig.isActive(ConsensusRule.RSKIP179, header.getNumber())) {
116 return true;
117 }
118
119 byte[] bitcoinMergedMiningHeader = header.getBitcoinMergedMiningHeader();
120 if (bitcoinMergedMiningHeader == null) {
121 return false;
122 }
123
124 BtcBlock btcBlock = makeBlock(bitcoinMergedMiningHeader);
125 if (btcBlock == null) {
126 return false;
127 }
128
129 long bitcoinTimestampInSecs = btcBlock.getTimeSeconds();
130
131 long rskTimestampInSecs = header.getTimestamp();
132
133 boolean valid = Math.abs(bitcoinTimestampInSecs - rskTimestampInSecs) < MAX_TIMESTAMPS_DIFF_IN_SECS;
134
135 if (!valid) {
136 logger.warn("Error validating block. RSK block timestamp {} and BTC block timestamp {} differ by more than {} secs.",
137 rskTimestampInSecs, bitcoinTimestampInSecs, MAX_TIMESTAMPS_DIFF_IN_SECS);
138 }
139
140 return valid;
141 }
142
143 @Nullable
144 private BtcBlock makeBlock(@Nonnull byte[] bitcoinMergedMiningHeader) {
145 try {
146 return bitcoinNetworkParameters.getDefaultSerializer().makeBlock(bitcoinMergedMiningHeader);
147 } catch (RuntimeException e) {
148 logger.error("Cannot make a BTC block from `{}`", bitcoinMergedMiningHeader, e);
149 return null;
150 }
151 }
152 }