Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test for ui #19

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,36 @@ anvil -f https://mainnet.infura.io/v3/<API_KEY> --fork-block-number <BLOCK_NUM>
source .env
forge script script/Test.s.sol:End2End --slow --sig "<FUNCTION_NAME>" --broadcast --rpc-url ${RPC_URL_ANVIL}
```

## Test for UI
1. Start Anvil forking mainnet, use anvil dev account private keys for PRIVATE_KEY_0 and PRIVATE_KEY_1 in .env
2. run deployment script to deploy bridge and eBtc<br>
*Try removing cache file if deployment fails*
```bash
anvil --chain-id 831337 --state <CACHE_FILE_PATH> -f https://mainnet.infura.io/v3/<API_KEY>

source .env
forge script script/e2e/0.Deployment.s.sol:End2End --broadcast --rpc-url ${RPC_URL_ANVIL}
```
3. Use bridge and ebtc addresses from Deployments.json in UI
4. Add anvil as custom network in wallet
5. *[In BitVM]* Run e2e test step 0 to create a completed peg in<br>
*Follow instructions in BitVM to run regtest environment*
```bash
cargo test -- --nocapture --ignored test_e2e_0
```
5. Test on UI to initiate a peg out<br>
*May need 'Clear activity tab data' in settings/advaced to reset nonce*
6. *[In BitVM]* Run e2e test step 1 to broadcast peg out tx
```bash
cargo test -- --nocapture --ignored test_e2e_1
```
7. Fetch data from local regtest and reset storage
```bash
node script/fetchTestDataPegOut.mjs 3 <pegOutTxId>
forge script script/e2e/1.BurnEBTC.s.sol:End2End --broadcast --rpc-url ${RPC_URL_ANVIL}
```
8. *[In BitVM]* Run e2e test step 2 to verify burn events
```bash
cargo test -- --nocapture --ignored test_e2e_2
```
5 changes: 3 additions & 2 deletions data/fetchTestDataPegOut.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getProvider } from './lib/provider.mjs'
import { reverseBytesNArray, BLOCK_HEADER_BYTES, EPOCH_BLOCK_COUNT } from './lib/coder.mjs'
import { getBlockInfoByHeight, getTransactionInfo } from './lib/api.mjs'

// usage: `node script/fetchTestDataPegOut.mjs <provider> <txId>`
// usage: `node script/fetchTestDataPegOut.mjs <provider> <pegOutTxId>`
; (async () => {
const providerId = parseInt(process.argv[2])
const provider = getProvider(providerId)
Expand Down Expand Up @@ -57,7 +57,8 @@ import { getBlockInfoByHeight, getTransactionInfo } from './lib/api.mjs'

testData.pegOut.withdrawer = SHARED_DATA.withdrawerEvmAddress
testData.pegOut.pegOutTimestamp = SHARED_DATA.pegOutTimestamp
testData.pegOut.amount = SHARED_DATA.pegOutValue
// testData.pegOut.amount = SHARED_DATA.pegOutValue
testData.pegOut.amount = proofResult.proofInfo.vin[0].prevout.value
testData.pegOut.withdrawerPubKey = SHARED_DATA.withdrawerPubKey
testData.pegOut.operatorPubKey = SHARED_DATA.operatorPubKey
testData.pegOut.nOfNPubKey = SHARED_DATA.nOfNPubKey
Expand Down
8 changes: 5 additions & 3 deletions data/lib/helper.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ const TEST_DATA_SAMPLE_FILE = path.join(__dirname, '../../test/fixture/test-data
export const TEST_DATA_FILE = path.join(__dirname, '../../test/fixture/test-data.json')

export const SHARED_DATA = {
depositorEvmAddress: '0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd',
depositorEvmAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', //anvil 1
pegInTimelock: 1,
pegInValue: 131072,
depositorPubKey: '0xedf074e2780407ed6ff9e291b8617ee4b4b8d7623e85b58318666f33a422301b',
withdrawerEvmAddress: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
pegOutValue: 131072,
// withdrawerEvmAddress: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
withdrawerEvmAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', //anvil 1
// withdrawerEvmAddress: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC', //anvil 2
pegOutValue: 131072, //TODO: remove this, should get it in prevout
pegOutTimestamp: 1722328130,
withdrawerPubKey: '0x02f80c9d1ef9ff640df2058c431c282299f48424480d34f1bade2274746fb4df8b',
operatorPubKey: '0x03484db4a2950d63da8455a1b705b39715e4075dd33511d0c7e3ce308c93449deb',
Expand Down
6 changes: 6 additions & 0 deletions data/lib/provider.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ const BLOCKSTREAM_BLOCK_CHUNK_SIZE = 10
const MUTINYNET_API_URL = 'https://www.mutinynet.com/api'
const MUTINYNET_BLOCK_CHUNK_SIZE = 10

// local regtest network
const LOCAL_REGTEST_API_URL = 'http://127.0.0.1:8094/regtest/api'
const LOCAL_REGTEST_BLOCK_CHUNK_SIZE = 10

const getAPIs = (baseUrl, blockChunkSize) => {
return {
blockChunkSize,
Expand All @@ -30,6 +34,8 @@ export const getProvider = (providerId) => {
return getAPIs(BLOCKSTREAM_API_URL, BLOCKSTREAM_BLOCK_CHUNK_SIZE)
case 2:
return getAPIs(MUTINYNET_API_URL, MUTINYNET_BLOCK_CHUNK_SIZE)
case 3:
return getAPIs(LOCAL_REGTEST_API_URL, LOCAL_REGTEST_BLOCK_CHUNK_SIZE)
default:
throw new Error(`unknown provider id ${providerId}`)
}
Expand Down
8 changes: 6 additions & 2 deletions script/DeploymentsFile.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@ contract DeploymentsFile is FileBase, StdChains {
string public constant DEPLOYMENTS_PATH = "script/Deployments.json";
string private constant ROOT_KEY = "deployments";

constructor() {
constructor(Chain[] memory customChains) {
reload(DEPLOYMENTS_PATH);
if (!valid) {
vm.writeJson("{}", path);
reload(DEPLOYMENTS_PATH);
}

for (uint256 i = 0; i < customChains.length; i++) {
setChain(customChains[i].chainAlias, customChains[i]);
}
}

function writeDeployment(address _storageAddress, address _bridgeAddress, address ebtcAddress) public {
string memory chain = getChain(block.chainid).name;
string memory timestamp = vm.toString(block.timestamp);
string memory timestamp = vm.toString(vm.unixTime());
string memory oldContent = content;
loadOldContent(oldContent);

Expand Down
2 changes: 1 addition & 1 deletion script/Test.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ contract End2End is Script {
if (!data.valid()) {
revert("Invalid Data file");
}
deployments = new DeploymentsFile();
deployments = new DeploymentsFile(new Chain[](0));
}

function run() public {
Expand Down
79 changes: 79 additions & 0 deletions script/e2e/0.Deployment.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import "forge-std/Script.sol";
import "../../test/mockup/EBTCTest.sol";
import "../../test/mockup/StorageTestnet.sol";
import "../../test/mockup/BridgeTestnet.sol";
import {Util} from "../../test/utils/Util.sol";
import {TestData} from "../../test/fixture/TestData.sol";
import {DeploymentsFile} from "../DeploymentsFile.sol";
import {StorageFixture, StorageSetupInfo} from "../../test/fixture/StorageFixture.sol";
import {Outpoint, ProofInfo} from "../../src/interfaces/IBridge.sol";
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

contract End2End is Script {
TestData data;
DeploymentsFile deployments;
Chain[] customChains;

function setUp() public {
customChains.push(
Chain({name: "Anvil-For-UI", chainId: 831337, chainAlias: "anvil4ui", rpcUrl: "http://localhost:8545"})
);
for (uint256 i = 0; i < customChains.length; i++) {
setChain(customChains[i].chainAlias, customChains[i]);
}

data = new TestData();
if (!data.valid()) {
revert("Invalid Data file");
}

deployments = new DeploymentsFile(customChains);
}

function run() public {
uint256 ownerPrivateKey = vm.envUint("PRIVATE_KEY_0");
address withdrawer = vm.addr(vm.envUint("PRIVATE_KEY_1"));
StorageSetupInfo memory params = data._storage(data.pegOutStorageKey());

vm.startBroadcast(ownerPrivateKey);
IStorage _storage = _deployStorage(params);
EBTCTest ebtcTest = _deployEbtc();
(IBridge bridge, EBTC ebtc) = _deployBridge(_storage, ebtcTest);

_mintForTesting(withdrawer, ebtcTest);
vm.stopBroadcast();

deployments.writeDeployment(address(_storage), address(bridge), address(ebtc));
}

function _deployStorage(StorageSetupInfo memory params) public returns (IStorage _storage) {
_storage = new StorageTestnet(
params.step,
params.height,
IStorage.KeyBlock(params.blockHash, 0, params.timestamp),
IStorage.Epoch(bytes4(Endian.reverse32(params.bits)), params.epochTimestamp)
);
_submit(_storage, params);
}

function _deployBridge(IStorage _storage, EBTCTest ebtcTest) public returns (IBridge bridge, EBTC ebtc) {
ebtc = EBTC(ebtcTest);
bridge = new BridgeTestnet(ebtc, _storage, data.nOfNPubKey(), data.pegInTimelock());
ebtc.setBridge(address(bridge));
}

function _deployEbtc() public returns (EBTCTest ebtcTest) {
ebtcTest = new EBTCTest(address(0));
}

function _mintForTesting(address receipient, EBTCTest ebtcTest) public {
ebtcTest.mintForTest(receipient, 100 ** ebtcTest.decimals());
}

function _submit(IStorage _storage, StorageSetupInfo memory params) public {
_storage.submit(params.headers, params.startHeight);
}
}
67 changes: 67 additions & 0 deletions script/e2e/1.BurnEBTC.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import "forge-std/Script.sol";
import "../../test/mockup/EBTCTest.sol";
import "../../test/mockup/StorageTestnet.sol";
import "../../test/mockup/BridgeTestnet.sol";
import {Util} from "../../test/utils/Util.sol";
import {TestData} from "../../test/fixture/TestData.sol";
import {DeploymentsFile} from "../DeploymentsFile.sol";
import {StorageFixture, StorageSetupInfo} from "../../test/fixture/StorageFixture.sol";
import {Outpoint, ProofInfo} from "../../src/interfaces/IBridge.sol";
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

contract End2End is Script {
TestData data;
DeploymentsFile deployments;
Chain[] customChains;

function setUp() public {
customChains.push(
Chain({name: "Anvil-For-UI", chainId: 831337, chainAlias: "anvil4ui", rpcUrl: "http://localhost:8545"})
);
for (uint256 i = 0; i < customChains.length; i++) {
setChain(customChains[i].chainAlias, customChains[i]);
}

data = new TestData();
if (!data.valid()) {
revert("Invalid Data file");
}

deployments = new DeploymentsFile(customChains);
}

function run() public {
uint256 ownerPrivateKey = vm.envUint("PRIVATE_KEY_0");
address withdrawer = vm.addr(vm.envUint("PRIVATE_KEY_1"));
StorageSetupInfo memory params = data._storage(data.pegOutStorageKey());
(, address bridgeAddress, address ebtc) = deployments.getLastRunDeployment();
ProofInfo memory proof = Util.paramToProof(data.proof(data.pegOutProofKey()), false);

vm.startBroadcast(ownerPrivateKey);
// deploy new and reset storage for testing purpose
IStorage _storage = _deployStorage(params);
BridgeTestnet(bridgeAddress).setBlockStorage(_storage);

IBridge(bridgeAddress).burnEBTC(withdrawer, proof);
vm.stopBroadcast();

deployments.writeDeployment(address(_storage), address(bridgeAddress), address(ebtc));
}

function _deployStorage(StorageSetupInfo memory params) public returns (IStorage _storage) {
_storage = new StorageTestnet(
params.step,
params.height,
IStorage.KeyBlock(params.blockHash, 0, params.timestamp),
IStorage.Epoch(bytes4(Endian.reverse32(params.bits)), params.epochTimestamp)
);
_submit(_storage, params);
}

function _submit(IStorage _storage, StorageSetupInfo memory params) public {
_storage.submit(params.headers, params.startHeight);
}
}
3 changes: 2 additions & 1 deletion src/Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ contract Bridge is IBridge {
bytes32 nOfNPubKey;
bytes4 private version = 0x02000000;
bytes4 private locktime = 0x00000000;
uint256 private constant DUST_AMOUNT = 10000;

constructor(EBTC _ebtc, IStorage _blockStorage, bytes32 _nOfNPubKey) {
ebtc = _ebtc;
Expand Down Expand Up @@ -141,7 +142,7 @@ contract Bridge is IBridge {
if (!txOut.scriptPubkeyWithoutLength().equals(inscriptionScript.generateP2WSHScriptPubKey())) {
revert InvalidPegOutProofScriptPubKey();
}
if (txOut.value() != info.amount) {
if (txOut.value() != info.amount - DUST_AMOUNT) {
revert InvalidPegOutProofAmount();
}
bytes32 txId = ViewSPV.calculateTxId(
Expand Down
8 changes: 7 additions & 1 deletion src/libraries/Coder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ library Coder {
uint256 public constant BLOCK_HEADER_LENGTH = 80;
uint256 public constant MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000;
uint256 public constant DIFFICULTY_PRECISION = 10 ** 6;
uint256 public constant DIFFICULTY_PRECISION_TESTNET = 10 ** 18;
uint32 public constant EPOCH_BLOCK_COUNT = 2016;
uint32 public constant EPOCH_TARGET_TIMESPAN = 10 * 60 * EPOCH_BLOCK_COUNT;

Expand Down Expand Up @@ -54,7 +55,12 @@ library Coder {
}

function toDifficulty(uint256 target) internal pure returns (uint256) {
return MAX_TARGET * DIFFICULTY_PRECISION / target;
if (MAX_TARGET >= target) {
return MAX_TARGET * DIFFICULTY_PRECISION / target;
} else {
// for testnet to prevent overflow
return DIFFICULTY_PRECISION_TESTNET / (target / MAX_TARGET);
}
}

function bitToDifficulty(bytes32 bits) internal pure returns (uint256) {
Expand Down
2 changes: 1 addition & 1 deletion test/fixture/TestData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ contract TestData is FileBase {
}

function proof(string memory keyPrefix) public view validated returns (ProofParam memory) {
bytes memory merkleProof = abi.decode(node(string.concat(keyPrefix, ".merkleProof")), (bytes));
bytes memory merkleProof = node(string.concat(keyPrefix, ".merkleProof"));
bytes memory parents = abi.decode(node(string.concat(keyPrefix, ".parents")), (bytes));
bytes memory children = abi.decode(node(string.concat(keyPrefix, ".children")), (bytes));
bytes memory rawTx = abi.decode(node(string.concat(keyPrefix, ".rawTx")), (bytes));
Expand Down