[eth] Add benchmark tests (#368)
* Add remappings This helps vs code solidity LSP work * Remove unused wormhole contract * Format foundry config file * Fix install foundry script * Add benchmark tests and its utils
This commit is contained in:
parent
a19cd93cd3
commit
0df243ba9e
|
@ -34,3 +34,42 @@ npm run install-forge-deps
|
|||
|
||||
After installing the dependencies. Run `forge build` to build the contracts and `forge test` to
|
||||
test the contracts using tests in `forge-test` directory.
|
||||
|
||||
### Gas Benchmark
|
||||
|
||||
You can use foundry to run benchmark tests written in [`forge-test/GasBenchmark.t.sol`](./forge-test/GasBenchmark.t.sol). To run the tests with gas report
|
||||
you can run `forge test --gas-report --match-contract GasBenchmark`. However, as there are multiple benchmarks, this might not be useful. You can run a
|
||||
specific benchmark test by passing the test name using `--match-test`. A full command to run `testBenchmarkUpdatePriceFeedsFresh` benchmark test is like this:
|
||||
|
||||
```
|
||||
forge test --gas-report --match-contract GasBenchmark --match-test testBenchmarkUpdatePriceFeedsFresh
|
||||
```
|
||||
|
||||
A gas report should have a couple of tables like this:
|
||||
|
||||
```
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────┬─────────────────┬────────┬────────┬─────────┬─────────╮
|
||||
│ node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol:ERC1967Proxy contract ┆ ┆ ┆ ┆ ┆ │
|
||||
╞═══════════════════════════════════════════════════════════════════════════════════════════╪═════════════════╪════════╪════════╪═════════╪═════════╡
|
||||
│ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ 164236 ┆ 2050 ┆ ┆ ┆ ┆ │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ ............. ┆ ..... ┆ ..... ┆ ..... ┆ ..... ┆ .. │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ parseAndVerifyVM ┆ 90292 ┆ 91262 ┆ 90292 ┆ 138792 ┆ 50 │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ updatePriceFeeds ┆ 187385 ┆ 206005 ┆ 187385 ┆ 1118385 ┆ 50 │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ ............. ┆ ..... ┆ ..... ┆ ..... ┆ ..... ┆ ... │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────┴─────────────────┴────────┴────────┴─────────┴─────────╯
|
||||
```
|
||||
|
||||
For most of the methods, the median gas usage is an indication of our desired gas usage. Because the calls that store something in the storage
|
||||
for the first time use significantly more gas.
|
||||
|
||||
If you like to optimize the contract and measure the gas optimization you can get gas snapshots using `forge snapshot` and evaluate your
|
||||
optimization with it. For more information, please refer to [Gas Snapshots documentation](https://book.getfoundry.sh/forge/gas-snapshots).
|
||||
Once you optimized the code, please share the snapshot difference (generated using `forge snapshot --diff <old-snapshot>`) in the PR too.
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
// contracts/Implementation.sol
|
||||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../Implementation.sol";
|
||||
|
||||
contract MockImplementation is Implementation {
|
||||
function initialize() initializer public {
|
||||
// this function needs to be exposed for an upgrade to pass
|
||||
}
|
||||
|
||||
function testNewImplementationActive() external pure returns (bool) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
|
||||
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
|
||||
import "./utils/WormholeTestUtils.t.sol";
|
||||
import "./utils/PythTestUtils.t.sol";
|
||||
|
||||
contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
|
||||
// 19, current mainnet number of guardians, is used to have gas estimates
|
||||
// close to our mainnet transactions.
|
||||
uint8 constant NUM_GUARDIANS = 19;
|
||||
// 2/3 of the guardians should sign a message for a VAA which is 13 out of 19 guardians.
|
||||
// It is possible to have more signers but the median seems to be 13.
|
||||
uint8 constant NUM_GUARDIAN_SIGNERS = 13;
|
||||
|
||||
// We use 5 prices to form a batch of 5 prices, close to our mainnet transactions.
|
||||
uint8 constant NUM_PRICES = 5;
|
||||
|
||||
uint constant BENCHMARK_ITERATIONS = 1000;
|
||||
|
||||
IPyth public pyth;
|
||||
|
||||
bytes32[] priceIds;
|
||||
PythStructs.Price[] prices;
|
||||
uint64 sequence;
|
||||
uint randSeed;
|
||||
|
||||
function setUp() public {
|
||||
pyth = IPyth(setUpPyth(setUpWormhole(NUM_GUARDIANS)));
|
||||
|
||||
priceIds = new bytes32[](NUM_PRICES);
|
||||
priceIds[0] = bytes32(0x1000000000000000000000000000000000000000000000000000000000000f00);
|
||||
for (uint i = 1; i < NUM_PRICES; ++i) {
|
||||
priceIds[i] = bytes32(uint256(priceIds[i-1])+1);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < NUM_PRICES; ++i) {
|
||||
prices.push(PythStructs.Price(
|
||||
int64(uint64(getRand() % 1000)), // Price
|
||||
uint64(getRand() % 100), // Confidence
|
||||
-5, // Expo
|
||||
getRand() % 10 // publishTime
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function getRand() internal returns (uint val) {
|
||||
++randSeed;
|
||||
val = uint(keccak256(abi.encode(randSeed)));
|
||||
}
|
||||
|
||||
function advancePrices() internal {
|
||||
for (uint i = 0; i < NUM_PRICES; ++i) {
|
||||
prices[i].price = int64(uint64(getRand() % 1000));
|
||||
prices[i].conf = uint64(getRand() % 100);
|
||||
prices[i].publishTime += getRand() % 10;
|
||||
}
|
||||
}
|
||||
|
||||
function generateUpdateDataAndFee() internal returns (bytes[] memory updateData, uint updateFee) {
|
||||
bytes memory vaa = generatePriceFeedUpdateVAA(
|
||||
priceIds,
|
||||
prices,
|
||||
sequence,
|
||||
NUM_GUARDIAN_SIGNERS
|
||||
);
|
||||
|
||||
++sequence;
|
||||
|
||||
updateData = new bytes[](1);
|
||||
updateData[0] = vaa;
|
||||
|
||||
updateFee = pyth.getUpdateFee(updateData);
|
||||
}
|
||||
|
||||
function testBenchmarkUpdatePriceFeedsFresh() public {
|
||||
for (uint i = 0; i < BENCHMARK_ITERATIONS; ++i) {
|
||||
advancePrices();
|
||||
|
||||
(bytes[] memory updateData, uint updateFee) = generateUpdateDataAndFee();
|
||||
pyth.updatePriceFeeds{value: updateFee}(updateData);
|
||||
}
|
||||
}
|
||||
|
||||
function testBenchmarkUpdatePriceFeedsNotFresh() public {
|
||||
for (uint i = 0; i < BENCHMARK_ITERATIONS; ++i) {
|
||||
(bytes[] memory updateData, uint updateFee) = generateUpdateDataAndFee();
|
||||
pyth.updatePriceFeeds{value: updateFee}(updateData);
|
||||
}
|
||||
}
|
||||
|
||||
function testBenchmarkUpdatePriceFeedsIfNecessaryFresh() public {
|
||||
for (uint i = 0; i < BENCHMARK_ITERATIONS; ++i) {
|
||||
advancePrices();
|
||||
|
||||
uint64[] memory publishTimes = new uint64[](NUM_PRICES);
|
||||
|
||||
for (uint j = 0; j < NUM_PRICES; ++j) {
|
||||
publishTimes[j] = uint64(prices[j].publishTime);
|
||||
}
|
||||
|
||||
(bytes[] memory updateData, uint updateFee) = generateUpdateDataAndFee();
|
||||
|
||||
// Since the prices have advanced, the publishTimes are newer than one in
|
||||
// the contract and hence, the call should succeed.
|
||||
pyth.updatePriceFeedsIfNecessary{value: updateFee}(updateData, priceIds, publishTimes);
|
||||
}
|
||||
}
|
||||
|
||||
function testBenchmarkUpdatePriceFeedsIfNecessaryNotFresh() public {
|
||||
for (uint i = 0; i < BENCHMARK_ITERATIONS; ++i) {
|
||||
uint64[] memory publishTimes = new uint64[](NUM_PRICES);
|
||||
|
||||
for (uint j = 0; j < NUM_PRICES; ++j) {
|
||||
publishTimes[j] = uint64(prices[j].publishTime);
|
||||
}
|
||||
|
||||
(bytes[] memory updateData, uint updateFee) = generateUpdateDataAndFee();
|
||||
|
||||
// Since the price is not advanced, the publishTimes are the same as the
|
||||
// ones in the contract except the first update.
|
||||
if (i > 0) {
|
||||
vm.expectRevert(bytes("no prices in the submitted batch have fresh prices, so this update will have no effect"));
|
||||
}
|
||||
|
||||
pyth.updatePriceFeedsIfNecessary{value: updateFee}(updateData, priceIds, publishTimes);
|
||||
}
|
||||
}
|
||||
|
||||
function testBenchmarkGetPrice() public {
|
||||
(bytes[] memory updateData, uint updateFee) = generateUpdateDataAndFee();
|
||||
pyth.updatePriceFeeds{value: updateFee}(updateData);
|
||||
|
||||
// Set the block timestamp to the publish time, so getPrice work as expected.
|
||||
vm.warp(prices[0].publishTime);
|
||||
|
||||
for (uint i = 0; i < BENCHMARK_ITERATIONS; ++i) {
|
||||
pyth.getPrice(priceIds[getRand() % NUM_PRICES]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../contracts/pyth/PythUpgradable.sol";
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
contract TestPythUpgradable is Test {
|
||||
PythUpgradable public pyth;
|
||||
|
||||
function setUp() public {
|
||||
pyth = new PythUpgradable();
|
||||
// The values below are just dummy values and this test does nothing.
|
||||
pyth.initialize(
|
||||
address(0x0000000000000000000000000000000000000000000000000000000000000000),
|
||||
0,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../../contracts/pyth/PythUpgradable.sol";
|
||||
import "../../contracts/pyth/PythInternalStructs.sol";
|
||||
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
|
||||
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
|
||||
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
|
||||
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
import "./WormholeTestUtils.t.sol";
|
||||
|
||||
abstract contract PythTestUtils is Test, WormholeTestUtils {
|
||||
uint16 constant SOURCE_EMITTER_CHAIN_ID = 0x1;
|
||||
bytes32 constant SOURCE_EMITTER_ADDRESS = 0x71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b;
|
||||
|
||||
uint16 constant GOVERNANCE_EMITTER_CHAIN_ID = 0x1;
|
||||
bytes32 constant GOVERNANCE_EMITTER_ADDRESS = 0x0000000000000000000000000000000000000000000000000000000000000011;
|
||||
|
||||
function setUpPyth(address wormhole) public returns (address) {
|
||||
PythUpgradable implementation = new PythUpgradable();
|
||||
ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), new bytes(0));
|
||||
PythUpgradable pyth = PythUpgradable(address(proxy));
|
||||
pyth.initialize(
|
||||
wormhole,
|
||||
SOURCE_EMITTER_CHAIN_ID,
|
||||
SOURCE_EMITTER_ADDRESS
|
||||
);
|
||||
|
||||
// TODO: All the logic below should be moved to the initializer
|
||||
pyth.addDataSource(
|
||||
SOURCE_EMITTER_CHAIN_ID,
|
||||
SOURCE_EMITTER_ADDRESS
|
||||
);
|
||||
|
||||
pyth.updateSingleUpdateFeeInWei(
|
||||
1
|
||||
);
|
||||
|
||||
pyth.updateValidTimePeriodSeconds(
|
||||
60
|
||||
);
|
||||
|
||||
pyth.updateGovernanceDataSource(
|
||||
GOVERNANCE_EMITTER_CHAIN_ID,
|
||||
GOVERNANCE_EMITTER_ADDRESS,
|
||||
0
|
||||
);
|
||||
|
||||
return address(pyth);
|
||||
}
|
||||
|
||||
// Generates byte-encoded payload for the given prices. It sets the emaPrice the same
|
||||
// as the given price. You can use this to mock wormhole call using `vm.mockCall` and
|
||||
// return a VM struct with this payload.
|
||||
// You can use generatePriceFeedUpdateVAA to generate a VAA for a price update.
|
||||
function generatePriceFeedUpdatePayload(
|
||||
bytes32[] memory priceIds,
|
||||
PythStructs.Price[] memory prices
|
||||
) public returns (bytes memory payload) {
|
||||
assertEq(priceIds.length, prices.length);
|
||||
|
||||
bytes memory attestations = new bytes(0);
|
||||
|
||||
for (uint i = 0; i < prices.length; ++i) {
|
||||
// encodePacked uses padding for arrays and we don't want it, so we manually concat them.
|
||||
attestations = abi.encodePacked(
|
||||
attestations,
|
||||
priceIds[i], // Product ID, we use the same price Id. This field is not used.
|
||||
priceIds[i], // Price ID,
|
||||
prices[i].price, // Price
|
||||
prices[i].conf, // Confidence
|
||||
prices[i].expo, // Exponent
|
||||
prices[i].price, // EMA price
|
||||
prices[i].conf // EMA confidence
|
||||
);
|
||||
|
||||
// Breaking this in two encodePackes because of the limited EVM stack.
|
||||
attestations = abi.encodePacked(
|
||||
attestations,
|
||||
uint8(PythInternalStructs.PriceAttestationStatus.TRADING),
|
||||
uint32(5), // Number of publishers. This field is not used.
|
||||
uint32(10), // Maximum number of publishers. This field is not used.
|
||||
uint64(prices[i].publishTime), // Attestation time. This field is not used.
|
||||
uint64(prices[i].publishTime), // Publish time.
|
||||
// Previous values are unused as status is trading. We use the same value
|
||||
// to make sure the test is irrelevant of the logic of which price is chosen.
|
||||
uint64(prices[i].publishTime), // Previous publish time.
|
||||
prices[i].price, // Previous price
|
||||
prices[i].conf // Previous confidence
|
||||
);
|
||||
}
|
||||
|
||||
payload = abi.encodePacked(
|
||||
uint32(0x50325748), // Magic
|
||||
uint16(3), // Major version
|
||||
uint16(0), // Minor version
|
||||
uint16(1), // Header size of 1 byte as it only contains payloadId
|
||||
uint8(2), // Payload ID 2 means it's a batch price attestation
|
||||
uint16(prices.length), // Number of attestations
|
||||
uint16(attestations.length / prices.length), // Size of a single price attestation.
|
||||
attestations
|
||||
);
|
||||
}
|
||||
|
||||
// Generates a VAA for the given prices.
|
||||
// This method calls generatePriceFeedUpdatePayload and then creates a VAA with it.
|
||||
// The VAAs generated from this method use block timestamp as their timestamp.
|
||||
function generatePriceFeedUpdateVAA(
|
||||
bytes32[] memory priceIds,
|
||||
PythStructs.Price[] memory prices,
|
||||
uint64 sequence,
|
||||
uint8 numSigners
|
||||
) public returns (bytes memory vaa) {
|
||||
bytes memory payload = generatePriceFeedUpdatePayload(
|
||||
priceIds,
|
||||
prices
|
||||
);
|
||||
|
||||
vaa = generateVaa(
|
||||
uint32(block.timestamp),
|
||||
SOURCE_EMITTER_CHAIN_ID,
|
||||
SOURCE_EMITTER_ADDRESS,
|
||||
sequence,
|
||||
payload,
|
||||
numSigners
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
contract PythTestUtilsTest is Test, WormholeTestUtils, PythTestUtils {
|
||||
// TODO: It is better to have a PythEvents contract that be extendable.
|
||||
event PriceFeedUpdate(bytes32 indexed id, bool indexed fresh, uint16 chainId, uint64 sequenceNumber, uint lastPublishTime, uint publishTime, int64 price, uint64 conf);
|
||||
|
||||
function testGeneratePriceFeedUpdateVAAWorks() public {
|
||||
IPyth pyth = IPyth(setUpPyth(setUpWormhole(
|
||||
1 // Number of guardians
|
||||
)));
|
||||
|
||||
bytes32[] memory priceIds = new bytes32[](1);
|
||||
priceIds[0] = 0x0000000000000000000000000000000000000000000000000000000000000222;
|
||||
|
||||
PythStructs.Price[] memory prices = new PythStructs.Price[](1);
|
||||
prices[0] = PythStructs.Price(
|
||||
100, // Price
|
||||
10, // Confidence
|
||||
-5, // Exponent
|
||||
1 // Publish time
|
||||
);
|
||||
|
||||
bytes memory vaa = generatePriceFeedUpdateVAA(
|
||||
priceIds,
|
||||
prices,
|
||||
1, // Sequence
|
||||
1 // No. Signers
|
||||
);
|
||||
|
||||
bytes[] memory updateData = new bytes[](1);
|
||||
updateData[0] = vaa;
|
||||
|
||||
uint updateFee = pyth.getUpdateFee(updateData);
|
||||
|
||||
vm.expectEmit(true, true, false, true);
|
||||
emit PriceFeedUpdate(priceIds[0], true, SOURCE_EMITTER_CHAIN_ID, 1, 0, 1, 100, 10);
|
||||
|
||||
pyth.updatePriceFeeds{value: updateFee}(updateData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../../contracts/wormhole/Implementation.sol";
|
||||
import "../../contracts/wormhole/Setup.sol";
|
||||
import "../../contracts/wormhole/Wormhole.sol";
|
||||
import "../../contracts/wormhole/interfaces/IWormhole.sol";
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
abstract contract WormholeTestUtils is Test {
|
||||
function setUpWormhole(uint8 numGuardians) public returns (address) {
|
||||
Implementation wormholeImpl = new Implementation();
|
||||
Setup wormholeSetup = new Setup();
|
||||
|
||||
Wormhole wormhole = new Wormhole(address(wormholeSetup), new bytes(0));
|
||||
|
||||
address[] memory initSigners = new address[](numGuardians);
|
||||
|
||||
for (uint256 i = 0; i < numGuardians; ++i) {
|
||||
initSigners[i] = vm.addr(i + 1); // i+1 is the private key for the i-th signer.
|
||||
}
|
||||
|
||||
// These values are the default values used in our tilt test environment
|
||||
// and are not important.
|
||||
Setup(address(wormhole)).setup(
|
||||
address(wormholeImpl),
|
||||
initSigners,
|
||||
2, // Ethereum chain ID
|
||||
1, // Governance source chain ID (1 = solana)
|
||||
0x0000000000000000000000000000000000000000000000000000000000000004 // Governance source address
|
||||
);
|
||||
|
||||
return address(wormhole);
|
||||
}
|
||||
|
||||
function generateVaa(
|
||||
uint32 timestamp,
|
||||
uint16 emitterChainId,
|
||||
bytes32 emitterAddress,
|
||||
uint64 sequence,
|
||||
bytes memory payload,
|
||||
uint8 numSigners
|
||||
) public returns (bytes memory vaa) {
|
||||
bytes memory body = abi.encodePacked(
|
||||
timestamp,
|
||||
uint32(0), // Nonce. It is zero for single VAAs.
|
||||
emitterChainId,
|
||||
emitterAddress,
|
||||
sequence,
|
||||
uint8(0), // Consistency level (sometimes no. confirmation block). Not important here.
|
||||
payload
|
||||
);
|
||||
|
||||
bytes32 hash = keccak256(abi.encodePacked(keccak256(body)));
|
||||
|
||||
bytes memory signatures = new bytes(0);
|
||||
|
||||
for (uint256 i = 0; i < numSigners; ++i) {
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(i + 1, hash);
|
||||
// encodePacked uses padding for arrays and we don't want it, so we manually concat them.
|
||||
signatures = abi.encodePacked(
|
||||
signatures,
|
||||
uint8(i), // Guardian index of the signature
|
||||
r,
|
||||
s,
|
||||
v - 27 // v is either 27 or 28. 27 is added to v in Eth (following BTC) but Wormhole doesn't use it.
|
||||
);
|
||||
}
|
||||
|
||||
vaa = abi.encodePacked(
|
||||
uint8(1), // Version
|
||||
uint32(0), // Guardian set index. it is initialized by 0
|
||||
numSigners,
|
||||
signatures,
|
||||
body
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
contract WormholeTestUtilsTest is Test, WormholeTestUtils {
|
||||
function testGenerateVaaWorks() public {
|
||||
IWormhole wormhole = IWormhole(setUpWormhole(5));
|
||||
|
||||
bytes memory vaa = generateVaa(
|
||||
112,
|
||||
7,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000bad,
|
||||
10,
|
||||
hex"deadbeaf",
|
||||
4
|
||||
);
|
||||
|
||||
(Structs.VM memory vm, bool valid, ) = wormhole.parseAndVerifyVM(vaa);
|
||||
assertTrue(valid);
|
||||
|
||||
assertEq(vm.timestamp, 112);
|
||||
assertEq(vm.emitterChainId, 7);
|
||||
assertEq(vm.emitterAddress, 0x0000000000000000000000000000000000000000000000000000000000000bad);
|
||||
assertEq(vm.payload, hex"deadbeaf");
|
||||
assertEq(vm.signatures.length, 4);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
[profile.default]
|
||||
solc_version = "0.8.4"
|
||||
solc_version = '0.8.4'
|
||||
optimizer = true
|
||||
optimizer_runs = 200
|
||||
src="contracts"
|
||||
src = 'contracts'
|
||||
# We put the tests into the forge-test directory (instead of test) so that
|
||||
# truffle doesn't try to build them
|
||||
test="forge-test"
|
||||
test = 'forge-test'
|
||||
|
||||
libs = [
|
||||
'lib',
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
@ensdomains/=node_modules/@ensdomains/
|
||||
@openzeppelin/=node_modules/@openzeppelin/
|
||||
@pythnetwork/=node_modules/@pythnetwork/
|
||||
ds-test/=lib/forge-std/lib/ds-test/src/
|
||||
forge-std/=lib/forge-std/src/
|
||||
truffle/=node_modules/truffle/
|
|
@ -13,7 +13,7 @@ if [ ! -f foundry.toml ]; then
|
|||
fi
|
||||
|
||||
# Read compiler version from foundry.toml
|
||||
SOLC_VERSION=$(grep solc_version foundry.toml | cut -d'=' -f2 | tr -d '" ') || true
|
||||
SOLC_VERSION=$(grep solc_version foundry.toml | cut -d'=' -f2 | tr -d "' ") || true
|
||||
|
||||
if [ -z "$SOLC_VERSION" ]; then
|
||||
echo "solc_version not found in foundry.toml." >& 2
|
||||
|
|
Loading…
Reference in New Issue