Skip to content

Commit 3116e09

Browse files
committed
Add reorg tests
1 parent 238c5ea commit 3116e09

File tree

14 files changed

+1032
-18
lines changed

14 files changed

+1032
-18
lines changed

contracts/mock/SPVContractMock.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ import {SPVContract} from "../SPVContract.sol";
88
contract SPVContractMock is SPVContract {
99
using BlockHeader for bytes;
1010

11+
function __SPVContractMock_init(bytes calldata blockHeaderRaw_) external initializer {
12+
(BlockHeaderData memory genesisBlockHeader_, bytes32 genesisBlockHash_) = blockHeaderRaw_
13+
.parseBlockHeaderData();
14+
15+
_addBlock(genesisBlockHeader_, genesisBlockHash_, 0);
16+
17+
emit MainchainHeadUpdated(0, genesisBlockHash_);
18+
}
19+
1120
function getStorageMedianTime(
1221
bytes calldata blockHeaderRaw_,
1322
uint256 blockHeight_

package-lock.json

Lines changed: 105 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,14 @@
5151
"@typechain/ethers-v6": "^0.5.1",
5252
"@typechain/hardhat": "^9.1.0",
5353
"@types/chai": "^4.3.20",
54+
"@types/fs-extra": "^11.0.4",
5455
"@types/mocha": "^10.0.10",
5556
"@types/node": "^22.13.2",
57+
"axios": "^1.9.0",
5658
"chai": "^4.5.0",
5759
"dotenv": "^16.4.7",
5860
"ethers": "^6.13.5",
61+
"fs-extra": "^11.3.0",
5962
"hardhat": "^2.22.18",
6063
"hardhat-contract-sizer": "^2.10.0",
6164
"hardhat-gas-reporter": "^2.2.2",

test/SPVContract.test.ts

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,32 @@ import {
88
getBlockHeaderData,
99
getBlockHeaderDataBatch,
1010
getBlocksDataFilePath,
11+
getReorgBlockHeaderData,
12+
getReorgBlockHeaderDataBatch,
1113
Reverter,
1214
} from "@test-helpers";
1315

14-
import { BlockHeaderMock, SPVContractMock } from "@ethers-v6";
16+
import { SPVContractMock } from "@ethers-v6";
1517

1618
describe("SPVContract", () => {
1719
const reverter = new Reverter();
1820

1921
let spvContract: SPVContractMock;
20-
let blockHeaderLib: BlockHeaderMock;
2122

2223
let genesisBlockDataFilePath: string;
24+
let regtestGenesisBlockDataFilePath: string;
2325
let firstBlocksDataFilePath: string;
2426
let newestBlocksDataFilePath: string;
27+
let reorgBlocksDataFilePath: string;
2528

2629
before(async () => {
2730
spvContract = await ethers.deployContract("SPVContractMock");
28-
blockHeaderLib = await ethers.deployContract("BlockHeaderMock");
2931

3032
genesisBlockDataFilePath = getBlocksDataFilePath("genesis_block.json");
33+
regtestGenesisBlockDataFilePath = getBlocksDataFilePath("regtest_genesis_block.json");
3134
firstBlocksDataFilePath = getBlocksDataFilePath("headers_1_10000.json");
3235
newestBlocksDataFilePath = getBlocksDataFilePath("headers_800352_815000.json");
36+
reorgBlocksDataFilePath = getBlocksDataFilePath("headers_16_18_reorg.json");
3337

3438
await reverter.snapshot();
3539
});
@@ -298,6 +302,61 @@ describe("SPVContract", () => {
298302
}
299303
});
300304

305+
it("should correctly update mainchain with the longest chain", async () => {
306+
const genesisData = getBlockHeaderData(regtestGenesisBlockDataFilePath, 0);
307+
308+
await spvContract.__SPVContractMock_init(genesisData.rawHeader);
309+
310+
const mainchainBlocks = getReorgBlockHeaderDataBatch(reorgBlocksDataFilePath, 1, 15);
311+
const rawHeaders = mainchainBlocks.map((blockData) => blockData.rawHeader);
312+
313+
await spvContract.addBlockHeaderBatch(rawHeaders);
314+
315+
const altBlock = getReorgBlockHeaderData(reorgBlocksDataFilePath, 16, false);
316+
317+
let tx = await spvContract.addBlockHeader(altBlock.rawHeader);
318+
319+
let expectedCurrentBlockHeight = 16;
320+
321+
await expect(tx)
322+
.to.emit(spvContract, "MainchainHeadUpdated")
323+
.withArgs(expectedCurrentBlockHeight, altBlock.blockHash);
324+
expect(await spvContract.getMainchainHead()).to.be.eq(altBlock.blockHash);
325+
326+
const newMainchainBlocks = getReorgBlockHeaderDataBatch(reorgBlocksDataFilePath, 16, 2);
327+
const newRawHeaders = newMainchainBlocks.map((blockData) => blockData.rawHeader);
328+
329+
tx = await spvContract.addBlockHeaderBatch(newRawHeaders);
330+
331+
expectedCurrentBlockHeight = 17;
332+
const expectedMainchainHead = newMainchainBlocks[1];
333+
334+
await expect(tx)
335+
.to.emit(spvContract, "MainchainHeadUpdated")
336+
.withArgs(expectedCurrentBlockHeight, expectedMainchainHead.blockHash);
337+
expect(await spvContract.getMainchainHead()).to.be.eq(expectedMainchainHead.blockHash);
338+
expect(await spvContract.isInMainchain(altBlock.blockHash)).to.be.false;
339+
});
340+
341+
it("should correctly add alternative block without mainchain update", async () => {
342+
const genesisData = getBlockHeaderData(regtestGenesisBlockDataFilePath, 0);
343+
344+
await spvContract.__SPVContractMock_init(genesisData.rawHeader);
345+
346+
const mainchainBlocks = getReorgBlockHeaderDataBatch(reorgBlocksDataFilePath, 1, 21);
347+
const rawHeaders = mainchainBlocks.map((blockData) => blockData.rawHeader);
348+
349+
await spvContract.addBlockHeaderBatch(rawHeaders);
350+
351+
const altBlock = getReorgBlockHeaderData(reorgBlocksDataFilePath, 16, false);
352+
353+
const tx = await spvContract.addBlockHeader(altBlock.rawHeader);
354+
355+
await expect(tx).to.not.emit(spvContract, "MainchainHeadUpdated");
356+
357+
expect(await spvContract.isInMainchain(altBlock.blockHash)).to.be.false;
358+
});
359+
301360
it("should get exception if pass block that already exists", async () => {
302361
await spvContract["__SPVContract_init()"]();
303362

test/btc-utils/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
volumes.dev

test/btc-utils/bitcoind.conf

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
regtest=1
2+
testnet=0
3+
dnsseed=0
4+
upnp=0
5+
6+
[regtest]
7+
port=18444
8+
rpcport=18443
9+
10+
server=1
11+
txindex=1
12+
rest=1
13+
14+
rpcuser=admin1
15+
rpcpassword=123
16+
rpcallowip=0.0.0.0/0
17+
rpcbind=0.0.0.0
18+
fallbackfee=0.00001
19+
20+
zmqpubrawblock=tcp://0.0.0.0:28332
21+
zmqpubrawtx=tcp://0.0.0.0:28333
22+
23+
blockfilterindex=1
24+
peerblockfilters=1

test/btc-utils/docker-compose.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
version: "3"
2+
services:
3+
bitcoind:
4+
image: lncm/bitcoind:v26.0
5+
container_name: bitcoind
6+
restart: on-failure
7+
stop_grace_period: 30s
8+
user: 0:0
9+
healthcheck:
10+
test: [ "CMD", "bitcoin-cli", "getnetworkinfo" ]
11+
interval: 2s
12+
volumes:
13+
- ./bitcoind.conf:/root/.bitcoin/bitcoin.conf
14+
- ./volumes.dev/bitcoind:/root/.bitcoin
15+
command: [ "-addnode=bitcoind2:18444" ]
16+
entrypoint:
17+
- "sh"
18+
- "-c"
19+
- "bitcoind"
20+
ports:
21+
- 18443:18443
22+
- 18444:18444
23+
24+
bitcoind2:
25+
image: lncm/bitcoind:v26.0
26+
container_name: bitcoind2
27+
restart: on-failure
28+
stop_grace_period: 30s
29+
user: 0:0
30+
healthcheck:
31+
test: [ "CMD", "bitcoin-cli", "getnetworkinfo" ]
32+
interval: 2s
33+
volumes:
34+
- ./bitcoind.conf:/root/.bitcoin/bitcoin.conf
35+
- ./volumes.dev/bitcoind2:/root/.bitcoin
36+
command: [ "-addnode=bitcoind:18444" ]
37+
ports:
38+
- 18445:18443
39+
- 18446:18444

0 commit comments

Comments
 (0)