Skip to content

Commit d79009e

Browse files
committed
Add storage optimizations
1 parent b354b53 commit d79009e

File tree

7 files changed

+136
-120
lines changed

7 files changed

+136
-120
lines changed

contracts/SPVContract.sol

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ contract SPVContract is ISPVContract, Initializable {
2323

2424
struct SPVContractStorage {
2525
mapping(bytes32 => BlockData) blocksData;
26-
mapping(uint256 => bytes32) blocksHeightToBlockHash;
26+
mapping(uint64 => bytes32) blocksHeightToBlockHash;
2727
bytes32 mainchainHead;
2828
uint256 lastEpochCumulativeWork;
2929
}
@@ -56,7 +56,7 @@ contract SPVContract is ISPVContract, Initializable {
5656

5757
function __SPVContract_init(
5858
bytes calldata blockHeaderRaw_,
59-
uint256 blockHeight_,
59+
uint64 blockHeight_,
6060
uint256 cumulativeWork_
6161
) external initializer {
6262
(BlockHeaderData memory blockHeader_, bytes32 blockHash_) = _parseBlockHeaderRaw(
@@ -91,11 +91,11 @@ contract SPVContract is ISPVContract, Initializable {
9191
bytes32[] memory blockHashes_
9292
) = _parseBlockHeadersRaw(blockHeaderRawArray_);
9393

94-
uint256 firstBlockHeight_ = getBlockHeight(blockHeaders_[0].prevBlockHash) + 1;
94+
uint64 firstBlockHeight_ = getBlockHeight(blockHeaders_[0].prevBlockHash) + 1;
9595
bytes32 currentTarget_ = getBlockTarget(blockHeaders_[0].prevBlockHash);
9696

97-
for (uint256 i = 0; i < blockHeaderRawArray_.length; ++i) {
98-
uint256 currentBlockHeight_ = firstBlockHeight_ + i;
97+
for (uint64 i = 0; i < blockHeaderRawArray_.length; ++i) {
98+
uint64 currentBlockHeight_ = firstBlockHeight_ + i;
9999

100100
currentTarget_ = _updateLastEpochCumulativeWork(currentTarget_, currentBlockHeight_);
101101

@@ -126,7 +126,7 @@ contract SPVContract is ISPVContract, Initializable {
126126
PrevBlockDoesNotExist(blockHeader_.prevBlockHash)
127127
);
128128

129-
uint256 blockHeight_ = getBlockHeight(blockHeader_.prevBlockHash) + 1;
129+
uint64 blockHeight_ = getBlockHeight(blockHeader_.prevBlockHash) + 1;
130130
bytes32 currentTarget_ = getBlockTarget(blockHeader_.prevBlockHash);
131131

132132
currentTarget_ = _updateLastEpochCumulativeWork(currentTarget_, blockHeight_);
@@ -142,16 +142,7 @@ contract SPVContract is ISPVContract, Initializable {
142142
}
143143

144144
/// @inheritdoc ISPVContract
145-
function validateBlockHash(bytes32 blockHash_) external view returns (bool, uint256) {
146-
if (!isInMainchain(blockHash_)) {
147-
return (false, 0);
148-
}
149-
150-
return (true, getMainchainBlockHeight() - getBlockHeight(blockHash_));
151-
}
152-
153-
/// @inheritdoc ISPVContract
154-
function verifyTx(
145+
function checkTxInclusion(
155146
bytes32 blockHash_,
156147
bytes32 txid_,
157148
bytes32[] calldata merkleProof_,
@@ -164,6 +155,15 @@ contract SPVContract is ISPVContract, Initializable {
164155
return TxMerkleProof.verify(merkleProof_, directions_, reversedRoot_, txid_);
165156
}
166157

158+
/// @inheritdoc ISPVContract
159+
function getBlockStatus(bytes32 blockHash_) external view returns (bool, uint256) {
160+
if (!isInMainchain(blockHash_)) {
161+
return (false, 0);
162+
}
163+
164+
return (true, getMainchainHeight() - getBlockHeight(blockHash_));
165+
}
166+
167167
/// @inheritdoc ISPVContract
168168
function getBlockInfo(bytes32 blockHash_) external view returns (BlockInfo memory blockInfo_) {
169169
if (!blockExists(blockHash_)) {
@@ -186,7 +186,7 @@ contract SPVContract is ISPVContract, Initializable {
186186

187187
/// @inheritdoc ISPVContract
188188
function getBlockMerkleRoot(bytes32 blockHash_) public view returns (bytes32) {
189-
return _getBlockHeader(blockHash_).merkleRoot;
189+
return _getSPVContractStorage().blocksData[blockHash_].merkleRoot;
190190
}
191191

192192
/// @inheritdoc ISPVContract
@@ -200,27 +200,27 @@ contract SPVContract is ISPVContract, Initializable {
200200
}
201201

202202
/// @inheritdoc ISPVContract
203-
function getBlockHeight(bytes32 blockHash_) public view returns (uint256) {
203+
function getBlockHeight(bytes32 blockHash_) public view returns (uint64) {
204204
return _getSPVContractStorage().blocksData[blockHash_].blockHeight;
205205
}
206206

207207
/// @inheritdoc ISPVContract
208-
function getBlockHash(uint256 blockHeight_) public view returns (bytes32) {
208+
function getBlockHash(uint64 blockHeight_) public view returns (bytes32) {
209209
return _getSPVContractStorage().blocksHeightToBlockHash[blockHeight_];
210210
}
211211

212212
/// @inheritdoc ISPVContract
213213
function getBlockTarget(bytes32 blockHash_) public view returns (bytes32) {
214-
return TargetsHelper.bitsToTarget(_getBlockHeader(blockHash_).bits);
214+
return TargetsHelper.bitsToTarget(_getSPVContractStorage().blocksData[blockHash_].bits);
215215
}
216216

217217
/// @inheritdoc ISPVContract
218218
function blockExists(bytes32 blockHash_) public view returns (bool) {
219-
return _getBlockHeader(blockHash_).time > 0;
219+
return _getBlockHeaderTime(blockHash_) > 0;
220220
}
221221

222222
/// @inheritdoc ISPVContract
223-
function getMainchainBlockHeight() public view returns (uint256) {
223+
function getMainchainHeight() public view returns (uint256) {
224224
return getBlockHeight(_getSPVContractStorage().mainchainHead);
225225
}
226226

@@ -232,11 +232,19 @@ contract SPVContract is ISPVContract, Initializable {
232232
function _addBlock(
233233
BlockHeaderData memory blockHeader_,
234234
bytes32 blockHash_,
235-
uint256 blockHeight_
235+
uint64 blockHeight_
236236
) internal {
237237
SPVContractStorage storage $ = _getSPVContractStorage();
238238

239-
$.blocksData[blockHash_] = BlockData({header: blockHeader_, blockHeight: blockHeight_});
239+
$.blocksData[blockHash_] = BlockData({
240+
prevBlockHash: blockHeader_.prevBlockHash,
241+
merkleRoot: blockHeader_.merkleRoot,
242+
version: blockHeader_.version,
243+
time: blockHeader_.time,
244+
nonce: blockHeader_.nonce,
245+
bits: blockHeader_.bits,
246+
blockHeight: blockHeight_
247+
});
240248

241249
_updateMainchainHead(blockHeader_, blockHash_, blockHeight_);
242250

@@ -246,7 +254,7 @@ contract SPVContract is ISPVContract, Initializable {
246254
function _updateMainchainHead(
247255
BlockHeaderData memory blockHeader_,
248256
bytes32 blockHash_,
249-
uint256 blockHeight_
257+
uint64 blockHeight_
250258
) internal {
251259
SPVContractStorage storage $ = _getSPVContractStorage();
252260

@@ -270,31 +278,34 @@ contract SPVContract is ISPVContract, Initializable {
270278
$.blocksHeightToBlockHash[blockHeight_] = blockHash_;
271279

272280
bytes32 prevBlockHash_ = blockHeader_.prevBlockHash;
273-
uint256 prevBlockHeight_ = blockHeight_ - 1;
281+
uint64 prevBlockHeight_ = blockHeight_ - 1;
274282

275283
do {
276284
$.blocksHeightToBlockHash[prevBlockHeight_] = prevBlockHash_;
277285

278-
prevBlockHash_ = _getBlockHeader(prevBlockHash_).prevBlockHash;
279-
--prevBlockHeight_;
286+
prevBlockHash_ = _getSPVContractStorage().blocksData[prevBlockHash_].prevBlockHash;
287+
288+
unchecked {
289+
--prevBlockHeight_;
290+
}
280291
} while (getBlockHash(prevBlockHeight_) != prevBlockHash_ && prevBlockHash_ != 0);
281292
}
282293
}
283294

284295
function _updateLastEpochCumulativeWork(
285296
bytes32 currentTarget_,
286-
uint256 blockHeight_
297+
uint64 blockHeight_
287298
) internal returns (bytes32) {
288299
SPVContractStorage storage $ = _getSPVContractStorage();
289300

290301
if (TargetsHelper.isTargetAdjustmentBlock(blockHeight_)) {
291302
$.lastEpochCumulativeWork += TargetsHelper.countEpochCumulativeWork(currentTarget_);
292303

293-
uint256 epochStartTime_ = _getBlockHeader(
304+
uint32 epochStartTime_ = _getBlockHeaderTime(
294305
getBlockHash(blockHeight_ - TargetsHelper.DIFFICULTY_ADJUSTMENT_INTERVAL)
295-
).time;
296-
uint256 epochEndTime_ = _getBlockHeader(getBlockHash(blockHeight_ - 1)).time;
297-
uint256 passedTime_ = epochEndTime_ - epochStartTime_;
306+
);
307+
uint32 epochEndTime_ = _getBlockHeaderTime(getBlockHash(blockHeight_ - 1));
308+
uint32 passedTime_ = epochEndTime_ - epochStartTime_;
298309

299310
currentTarget_ = TargetsHelper.countNewRoundedTarget(currentTarget_, passedTime_);
300311
}
@@ -350,17 +361,17 @@ contract SPVContract is ISPVContract, Initializable {
350361
bytes32 toBlockHash_ = blockHeader_.prevBlockHash;
351362

352363
if (blockHeight_ - 1 < MEDIAN_PAST_BLOCKS) {
353-
return _getBlockHeader(toBlockHash_).time;
364+
return _getBlockHeaderTime(toBlockHash_);
354365
}
355366

356367
uint256[] memory blocksTime_ = new uint256[](MEDIAN_PAST_BLOCKS);
357368
bool needsSort_;
358369

359370
for (uint256 i = MEDIAN_PAST_BLOCKS; i > 0; --i) {
360-
uint32 currentTime_ = _getBlockHeader(toBlockHash_).time;
371+
uint32 currentTime_ = _getBlockHeaderTime(toBlockHash_);
361372

362373
blocksTime_[i - 1] = currentTime_;
363-
toBlockHash_ = _getBlockHeader(toBlockHash_).prevBlockHash;
374+
toBlockHash_ = _getSPVContractStorage().blocksData[toBlockHash_].prevBlockHash;
364375

365376
if (i < MEDIAN_PAST_BLOCKS && currentTime_ > blocksTime_[i]) {
366377
needsSort_ = true;
@@ -395,7 +406,7 @@ contract SPVContract is ISPVContract, Initializable {
395406
}
396407

397408
function _getBlockCumulativeWork(
398-
uint256 blockHeight_,
409+
uint64 blockHeight_,
399410
bytes32 blockHash_
400411
) internal view returns (uint256) {
401412
uint256 currentEpochCumulativeWork_ = getBlockTarget(blockHash_).countCumulativeWork(
@@ -405,8 +416,8 @@ contract SPVContract is ISPVContract, Initializable {
405416
return _getSPVContractStorage().lastEpochCumulativeWork + currentEpochCumulativeWork_;
406417
}
407418

408-
function _getBlockHeader(bytes32 blockHash_) internal view returns (BlockHeaderData storage) {
409-
return _getSPVContractStorage().blocksData[blockHash_].header;
419+
function _getBlockHeaderTime(bytes32 blockHash_) internal view returns (uint32) {
420+
return _getSPVContractStorage().blocksData[blockHash_].time;
410421
}
411422

412423
function _onlyNonExistingBlock(bytes32 blockHash_) internal view {

contracts/interfaces/ISPVContract.sol

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.28;
33

4-
import {BlockHeaderData} from "../libs/BlockHeader.sol";
54
import {TxMerkleProof} from "../libs/TxMerkleProof.sol";
65

76
/**
@@ -79,12 +78,22 @@ interface ISPVContract {
7978

8079
/**
8180
* @notice Represents the data of a block
82-
* @param header The parsed block header data
81+
* @param prevBlockHash The hash of the previous block
82+
* @param merkleRoot The Merkle root of the transactions in the block
83+
* @param version The block version number
84+
* @param time The block's timestamp
85+
* @param nonce The nonce used for mining
86+
* @param bits The encoded difficulty target for the block
8387
* @param blockHeight The block height
8488
*/
8589
struct BlockData {
86-
BlockHeaderData header;
87-
uint256 blockHeight;
90+
bytes32 prevBlockHash;
91+
bytes32 merkleRoot;
92+
uint32 version;
93+
uint32 time;
94+
uint32 nonce;
95+
bytes4 bits;
96+
uint64 blockHeight;
8897
}
8998

9099
/**
@@ -114,27 +123,28 @@ interface ISPVContract {
114123
function addBlockHeader(bytes calldata blockHeaderRaw_) external;
115124

116125
/**
117-
* @notice Validates a given block hash and returns its mainchain status and confirmation count
118-
* @param blockHash_ The hash of the block to validate
119-
* @return isInMainchain True if the block is in the mainchain, false otherwise
120-
* @return confirmationsCount The number of blocks that have been mined on top of the validated block
121-
*/
122-
function validateBlockHash(bytes32 blockHash_) external view returns (bool, uint256);
123-
124-
/**
125-
* @notice Verifies that given txid is included in the specified block
126+
* @notice Checks that given txId is included in the specified block
126127
* @param blockHash_ The hash of the block in which to verify the transaction
127-
* @param txid_ The transaction id to verify
128+
* @param txId_ The transaction id to verify
128129
* @param merkleProof_ The array of hashes used to build the Merkle root
129130
* @param directions_ The array indicating the hashing directions for the Merkle proof
131+
* @return True if the txId is present in the block's Merkle tree, false otherwise
130132
*/
131-
function verifyTx(
133+
function checkTxInclusion(
132134
bytes32 blockHash_,
133-
bytes32 txid_,
135+
bytes32 txId_,
134136
bytes32[] memory merkleProof_,
135137
TxMerkleProof.HashDirection[] calldata directions_
136138
) external view returns (bool);
137139

140+
/**
141+
* @notice Returns the current status of a given block
142+
* @param blockHash_ The hash of the block to check
143+
* @return isInMainchain True if the block is in the mainchain, false otherwise
144+
* @return confirmationsCount The number of blocks that have been mined on top of the given block
145+
*/
146+
function getBlockStatus(bytes32 blockHash_) external view returns (bool, uint256);
147+
138148
/**
139149
* @notice Returns the cumulative work of the last epoch.
140150
* This represents the total difficulty accumulated up to the last epoch boundary
@@ -178,23 +188,23 @@ interface ISPVContract {
178188
* This represents the highest block number on the most accumulated work chain
179189
* @return The height of the mainchain head
180190
*/
181-
function getMainchainBlockHeight() external view returns (uint256);
191+
function getMainchainHeight() external view returns (uint256);
182192

183193
/**
184194
* @notice Returns the block height for a given block hash
185195
* This function retrieves the height at which the block exists in the chain
186196
* @param blockHash_ The hash of the block
187197
* @return The height of the block
188198
*/
189-
function getBlockHeight(bytes32 blockHash_) external view returns (uint256);
199+
function getBlockHeight(bytes32 blockHash_) external view returns (uint64);
190200

191201
/**
192202
* @notice Returns the block hash for a given block height.
193203
* This function retrieves the hash of the block from the mainchain at the specified height
194204
* @param blockHeight_ The height of the block
195205
* @return The hash of the block
196206
*/
197-
function getBlockHash(uint256 blockHeight_) external view returns (bytes32);
207+
function getBlockHash(uint64 blockHeight_) external view returns (bytes32);
198208

199209
/**
200210
* @notice Returns the target of a given block hash.

contracts/libs/TargetsHelper.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ library TargetsHelper {
1717
* @notice The number of blocks after which the difficulty target is adjusted.
1818
* This is a fundamental constant in Bitcoin's difficulty adjustment algorithm
1919
*/
20-
uint256 public constant DIFFICULTY_ADJUSTMENT_INTERVAL = 2016;
20+
uint64 public constant DIFFICULTY_ADJUSTMENT_INTERVAL = 2016;
2121

2222
/**
2323
* @notice The initial difficulty target for the Bitcoin blockchain.
@@ -54,7 +54,7 @@ library TargetsHelper {
5454
* @param blockHeight_ The height of the block to check
5555
* @return True if it's an adjustment block, false otherwise
5656
*/
57-
function isTargetAdjustmentBlock(uint256 blockHeight_) internal pure returns (bool) {
57+
function isTargetAdjustmentBlock(uint64 blockHeight_) internal pure returns (bool) {
5858
return getEpochBlockNumber(blockHeight_) == 0 && blockHeight_ > 0;
5959
}
6060

@@ -64,7 +64,7 @@ library TargetsHelper {
6464
* @param blockHeight_ The height of the block
6565
* @return The block number within its current epoch
6666
*/
67-
function getEpochBlockNumber(uint256 blockHeight_) internal pure returns (uint256) {
67+
function getEpochBlockNumber(uint64 blockHeight_) internal pure returns (uint64) {
6868
return blockHeight_ % DIFFICULTY_ADJUSTMENT_INTERVAL;
6969
}
7070

@@ -77,7 +77,7 @@ library TargetsHelper {
7777
*/
7878
function countNewRoundedTarget(
7979
bytes32 currentTarget_,
80-
uint256 actualPassedTime_
80+
uint32 actualPassedTime_
8181
) internal pure returns (bytes32) {
8282
return roundTarget(countNewTarget(currentTarget_, actualPassedTime_));
8383
}
@@ -91,7 +91,7 @@ library TargetsHelper {
9191
*/
9292
function countNewTarget(
9393
bytes32 currentTarget_,
94-
uint256 actualPassedTime_
94+
uint32 actualPassedTime_
9595
) internal pure returns (bytes32) {
9696
uint256 currentRatio = (actualPassedTime_ * TARGET_FIXED_POINT_FACTOR) /
9797
EXPECTED_TARGET_BLOCKS_TIME;
@@ -124,7 +124,7 @@ library TargetsHelper {
124124
*/
125125
function countCumulativeWork(
126126
bytes32 epochTarget_,
127-
uint256 blocksCount_
127+
uint64 blocksCount_
128128
) internal pure returns (uint256) {
129129
return countBlockWork(epochTarget_) * blocksCount_;
130130
}

0 commit comments

Comments
 (0)