Skip to content

Commit 529a6f6

Browse files
committed
Add unit tests & blocks data
1 parent 0274f4f commit 529a6f6

31 files changed

+543553
-152
lines changed

.solcover.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
module.exports = {
2-
skipFiles: [],
2+
skipFiles: ["mock/"],
33
configureYulOptimizer: true,
44
};

contracts/SPVContract.sol

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import {BlocksStorage} from "./libs/BlocksStorage.sol";
88
import {TargetsStorage} from "./libs/targets/TargetsStorage.sol";
99
import {TargetsHelper} from "./libs/targets/TargetsHelper.sol";
1010

11-
// import "hardhat/console.sol";
12-
1311
contract SPVContract is Initializable {
1412
using BlockHeader for bytes;
1513
using BlocksStorage for BlocksStorage.BlocksData;
@@ -59,8 +57,6 @@ contract SPVContract is Initializable {
5957
(BlockHeaderData memory blockHeader_, bytes32 blockHash_) = blockHeaderRaw_
6058
.parseBlockHeaderData();
6159

62-
// console.logBytes32(blockHash_);
63-
6460
require(!$.blocksData.blockExists(blockHash_), BlockAlreadyExists(blockHash_));
6561
require(
6662
$.blocksData.blockExists(blockHeader_.prevBlockHash),
@@ -152,12 +148,4 @@ contract SPVContract is Initializable {
152148

153149
require(blockHeader_.time > medianTime_, InvalidBlockTime(blockHeader_.time, medianTime_));
154150
}
155-
156-
// function test(bytes calldata blockHeaderRaw_) external {
157-
// BlockHeader.BlockHeaderData memory initBlockHeaderData_ = BlockHeader.parseBlockHeaderData(
158-
// blockHeaderRaw_
159-
// );
160-
161-
// console.logBytes32(initBlockHeaderData_.blockHash);
162-
// }
163151
}

contracts/libs/BlockHeader.sol

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ library BlockHeader {
2222
function getBlockHeaderHash(bytes calldata blockHeaderRaw_) internal pure returns (bytes32) {
2323
bytes32 rawBlockHash_ = sha256(abi.encode(sha256(blockHeaderRaw_)));
2424

25-
return reverseBlockHash(rawBlockHash_);
25+
return reverseHash(rawBlockHash_);
2626
}
2727

2828
function parseBlockHeaderData(
@@ -34,33 +34,41 @@ library BlockHeader {
3434
);
3535

3636
headerData_ = BlockHeaderData({
37-
version: uint32(_convertToBigEndian(blockHeaderRaw_[0:4])),
38-
prevBlockHash: reverseBlockHash(bytes32(blockHeaderRaw_[4:36])),
39-
merkleRoot: bytes32(blockHeaderRaw_[36:68]),
40-
time: uint32(_convertToBigEndian(blockHeaderRaw_[68:72])),
41-
bits: bytes4(uint32(_convertToBigEndian(blockHeaderRaw_[72:76]))),
42-
nonce: uint32(_convertToBigEndian(blockHeaderRaw_[76:80]))
37+
version: uint32(_reverseBytes(blockHeaderRaw_[0:4])),
38+
prevBlockHash: reverseHash(bytes32(blockHeaderRaw_[4:36])),
39+
merkleRoot: reverseHash(bytes32(blockHeaderRaw_[36:68])),
40+
time: uint32(_reverseBytes(blockHeaderRaw_[68:72])),
41+
bits: bytes4(uint32(_reverseBytes(blockHeaderRaw_[72:76]))),
42+
nonce: uint32(_reverseBytes(blockHeaderRaw_[76:80]))
4343
});
4444
blockHash_ = getBlockHeaderHash(blockHeaderRaw_);
4545
}
4646

4747
function toRawBytes(BlockHeaderData memory headerData_) internal pure returns (bytes memory) {
4848
return
49-
abi.encode(
50-
headerData_.version,
51-
reverseBlockHash(headerData_.prevBlockHash),
52-
headerData_.merkleRoot,
53-
headerData_.time,
54-
headerData_.bits,
55-
headerData_.nonce
49+
abi.encodePacked(
50+
_reverseUint32(headerData_.version),
51+
reverseHash(headerData_.prevBlockHash),
52+
reverseHash(headerData_.merkleRoot),
53+
_reverseUint32(headerData_.time),
54+
_reverseUint32(uint32(headerData_.bits)),
55+
_reverseUint32(headerData_.nonce)
5656
);
5757
}
5858

59-
function reverseBlockHash(bytes32 blockHash_) internal pure returns (bytes32) {
59+
function reverseHash(bytes32 blockHash_) internal pure returns (bytes32) {
6060
return bytes32(uint256(blockHash_).reverseBytes());
6161
}
6262

63-
function _convertToBigEndian(bytes calldata bytesToConvert_) private pure returns (uint256) {
63+
function _reverseBytes(bytes calldata bytesToConvert_) private pure returns (uint256) {
6464
return uint256(bytes32(bytesToConvert_)).reverseBytes();
6565
}
66+
67+
function _reverseUint32(uint32 input_) private pure returns (uint32) {
68+
return
69+
((input_ & 0x000000FF) << 24) |
70+
((input_ & 0x0000FF00) << 8) |
71+
((input_ & 0x00FF0000) >> 8) |
72+
((input_ & 0xFF000000) >> 24);
73+
}
6674
}

contracts/libs/BlocksStorage.sol

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -84,21 +84,6 @@ library BlocksStorage {
8484
);
8585
}
8686

87-
function _initialize(
88-
BlocksData storage self,
89-
BlockHeaderData memory startBlockHeader_,
90-
bytes32 startBlockHash_,
91-
uint256 startBlockHeight_,
92-
uint256 startBlockWork_,
93-
uint256 pendingBlockCount_
94-
) private {
95-
require(!_isInitialized(self), BlocksStorageAlreadyInitialized());
96-
97-
_addBlock(self, startBlockHeader_, startBlockHash_, startBlockHeight_, startBlockWork_);
98-
99-
self.pendingBlockCount = pendingBlockCount_;
100-
}
101-
10287
function addBlock(
10388
BlocksData storage self,
10489
BlockHeaderData memory blockHeader_,
@@ -109,6 +94,10 @@ library BlocksStorage {
10994
_addBlock(self, blockHeader_, blockHash_, blockHeight_, blockWork_);
11095
}
11196

97+
function getPendingBlockCount(BlocksData storage self) internal view returns (uint256) {
98+
return self.pendingBlockCount;
99+
}
100+
112101
function getBlockHashByBlockHeight(
113102
BlocksData storage self,
114103
uint256 blockHeight_
@@ -159,6 +148,33 @@ library BlocksStorage {
159148
return getBlockStatus(self, blockHash_) == status_;
160149
}
161150

151+
function getMainchainHeight(BlocksData storage self) internal view returns (uint256) {
152+
return self.currentBlockHeight;
153+
}
154+
155+
function getBlockData(
156+
BlocksData storage self,
157+
bytes32 blockHash_
158+
) internal view returns (BlockData memory) {
159+
return self.blocksData[blockHash_];
160+
}
161+
162+
function getNextMainchainBlock(
163+
BlocksData storage self,
164+
bytes32 blockHash_
165+
) internal view returns (bytes32) {
166+
return self.mainchain[blockHash_];
167+
}
168+
169+
function isInMainchain(
170+
BlocksData storage self,
171+
bytes32 blockHash_
172+
) internal view returns (bool) {
173+
BlockData storage blockData = self.blocksData[blockHash_];
174+
175+
return self.mainchain[blockData.header.prevBlockHash] == blockHash_;
176+
}
177+
162178
function getBlockStatus(
163179
BlocksData storage self,
164180
bytes32 blockHash_
@@ -192,18 +208,33 @@ library BlocksStorage {
192208
return 0;
193209
}
194210

195-
uint256[] memory blocksTime = new uint256[](MEDIAN_PAST_BLOCKS);
196-
uint256 blocksTimeIndex = MEDIAN_PAST_BLOCKS - 1;
211+
uint256[] memory blocksTime_ = new uint256[](MEDIAN_PAST_BLOCKS);
212+
uint256 blocksTimeIndex_ = MEDIAN_PAST_BLOCKS;
197213

198214
for (uint256 i = blockHeight_ - MEDIAN_PAST_BLOCKS; i < blockHeight_; ++i) {
199-
blocksTime[blocksTimeIndex--] = self.blocksData[toBlockHash_].header.time;
215+
blocksTime_[--blocksTimeIndex_] = self.blocksData[toBlockHash_].header.time;
200216

201217
toBlockHash_ = self.blocksData[toBlockHash_].header.prevBlockHash;
202218
}
203219

204-
LibSort.insertionSort(blocksTime);
220+
LibSort.insertionSort(blocksTime_);
205221

206-
return uint32(blocksTime[MEDIAN_PAST_BLOCKS / 2]);
222+
return uint32(blocksTime_[MEDIAN_PAST_BLOCKS / 2]);
223+
}
224+
225+
function _initialize(
226+
BlocksData storage self,
227+
BlockHeaderData memory startBlockHeader_,
228+
bytes32 startBlockHash_,
229+
uint256 startBlockHeight_,
230+
uint256 startBlockWork_,
231+
uint256 pendingBlockCount_
232+
) private {
233+
require(!_isInitialized(self), BlocksStorageAlreadyInitialized());
234+
235+
_addBlock(self, startBlockHeader_, startBlockHash_, startBlockHeight_, startBlockWork_);
236+
237+
self.pendingBlockCount = pendingBlockCount_;
207238
}
208239

209240
function _addBlock(
@@ -225,7 +256,9 @@ library BlocksStorage {
225256

226257
bytes32 mainchainHead_ = self.heightToBlockHash[self.currentBlockHeight];
227258

228-
if (self.blocksData[mainchainHead_].cumulativeWork < newBlockCumulativeWork_) {
259+
if (self.blocksData[mainchainHead_].cumulativeWork <= newBlockCumulativeWork_) {
260+
self.currentBlockHeight = blockHeight_;
261+
229262
_updateMainchain(self, blockHash_);
230263
}
231264
}
@@ -241,7 +274,7 @@ library BlocksStorage {
241274
blockHash_ = prevBlockHash_;
242275
blockData = self.blocksData[blockHash_];
243276
prevBlockHash_ = blockData.header.prevBlockHash;
244-
} while (self.mainchain[prevBlockHash_] != blockHash_);
277+
} while (self.mainchain[prevBlockHash_] != blockHash_ && prevBlockHash_ != 0);
245278
}
246279

247280
function _onlyInitialized(BlocksData storage self) private view {

contracts/libs/targets/TargetsHelper.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ library TargetsHelper {
4040
Math.mulDiv(uint256(currentTarget_), currentRatio, TARGET_FIXED_POINT_FACTOR)
4141
);
4242

43-
return target_ > INITIAL_TARGET ? target_ : INITIAL_TARGET;
43+
return target_ > INITIAL_TARGET ? INITIAL_TARGET : target_;
4444
}
4545

4646
function countBlockWork(bytes32 target_) internal pure returns (uint256 blockWork_) {

contracts/libs/targets/TargetsStorage.sol

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,13 @@ library TargetsStorage {
128128
/**
129129
* @notice Function to count network target epoch number
130130
*
131-
* From 0 to 2016 -> 1st epoch
132-
* From 2017 to 4032 -> 2st epoch
131+
* From 0 to 2015 -> 1st epoch
132+
* From 2016 to 4031 -> 2st epoch
133133
*
134134
* @param blockHeight_ The network block height value
135135
*/
136136
function countTargetEpoch(uint256 blockHeight_) internal pure returns (uint256 targetEpoch_) {
137137
targetEpoch_ = blockHeight_ / DIFFICULTY_ADJSTMENT_INTERVAL + 1;
138-
139-
if (isTargetAdjustmentBlock(blockHeight_)) {
140-
targetEpoch_--;
141-
}
142138
}
143139

144140
function isTargetAdjustmentBlock(uint256 blockHeight_) internal pure returns (bool) {
@@ -174,7 +170,7 @@ library TargetsStorage {
174170
InvalidEpochTimeParameters(epochStartTime_, epochEndTime_)
175171
);
176172

177-
uint256 nextTargetEpoch_ = countTargetEpoch(blockHeight_) + 1;
173+
uint256 nextTargetEpoch_ = countTargetEpoch(blockHeight_);
178174
bytes32 newTargetValue_ = getTarget(self, nextTargetEpoch_ - 1).countNewRoundedTarget(
179175
epochEndTime_ - epochStartTime_
180176
);

contracts/mock/SPVContractMock.sol

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.28;
3+
4+
import {SPVContract} from "../SPVContract.sol";
5+
6+
import {BlocksStorage} from "../libs/BlocksStorage.sol";
7+
import {TargetsStorage} from "../libs/targets/TargetsStorage.sol";
8+
9+
contract SPVContractMock is SPVContract {
10+
using BlocksStorage for BlocksStorage.BlocksData;
11+
using TargetsStorage for TargetsStorage.TargetsData;
12+
13+
function hasPendingTarget() external view returns (bool) {
14+
return _getSPVContractStorage().targets.hasPendingTarget();
15+
}
16+
17+
function getLastEpoch() external view returns (uint256) {
18+
return _getSPVContractStorage().targets.getLastEpoch();
19+
}
20+
21+
function getPendingEpoch() external view returns (uint256) {
22+
return _getSPVContractStorage().targets.getPendingEpoch();
23+
}
24+
25+
function getLastTarget() external view returns (bytes32) {
26+
return _getSPVContractStorage().targets.getLastTarget();
27+
}
28+
29+
function getPendingTarget(bytes32 blockHash_) external view returns (bytes32) {
30+
return _getSPVContractStorage().targets.getPendingTarget(blockHash_);
31+
}
32+
33+
function getTarget(uint256 targetEpoch_) external view returns (bytes32) {
34+
return _getSPVContractStorage().targets.getTarget(targetEpoch_);
35+
}
36+
37+
function getNextMainchainBlock(bytes32 blockHash_) external view returns (bytes32) {
38+
return _getSPVContractStorage().blocksData.getNextMainchainBlock(blockHash_);
39+
}
40+
41+
function isInMainchain(bytes32 blockHash_) external view returns (bool) {
42+
return _getSPVContractStorage().blocksData.isInMainchain(blockHash_);
43+
}
44+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.28;
3+
4+
import {BlockHeader, BlockHeaderData} from "../../libs/BlockHeader.sol";
5+
6+
contract BlockHeaderMock {
7+
using BlockHeader for bytes;
8+
9+
function getBlockHeaderHash(bytes calldata blockHeaderRaw_) external pure returns (bytes32) {
10+
return blockHeaderRaw_.getBlockHeaderHash();
11+
}
12+
13+
function parseBlockHeaderData(
14+
bytes calldata blockHeaderRaw_
15+
) external pure returns (BlockHeaderData memory, bytes32) {
16+
return blockHeaderRaw_.parseBlockHeaderData();
17+
}
18+
19+
function toRawBytes(BlockHeaderData memory headerData_) external pure returns (bytes memory) {
20+
return BlockHeader.toRawBytes(headerData_);
21+
}
22+
}

0 commit comments

Comments
 (0)