Skip to content

Commit 7590d39

Browse files
committed
Add signer to pyth header
1 parent 6f34310 commit 7590d39

File tree

13 files changed

+163
-119
lines changed

13 files changed

+163
-119
lines changed

target_chains/ethereum/contracts/contracts/pyth/Pyth.sol

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,9 @@ abstract contract Pyth is
104104
) {
105105
(
106106
uint offset,
107-
UpdateType updateType
108-
) = extractUpdateTypeFromAccumulatorHeader(updateData[i]);
107+
UpdateType updateType,
108+
109+
) = extractAccumulatorHeaderDetails(updateData[i]);
109110
if (updateType != UpdateType.WormholeMerkle) {
110111
revert PythErrors.InvalidUpdateData();
111112
}
@@ -135,8 +136,9 @@ abstract contract Pyth is
135136
) {
136137
(
137138
uint offset,
138-
UpdateType updateType
139-
) = extractUpdateTypeFromAccumulatorHeader(updateData[0]);
139+
UpdateType updateType,
140+
141+
) = extractAccumulatorHeaderDetails(updateData[0]);
140142
if (updateType != UpdateType.WormholeMerkle) {
141143
revert PythErrors.InvalidUpdateData();
142144
}
@@ -273,9 +275,10 @@ abstract contract Pyth is
273275
}
274276

275277
uint offset;
278+
Signer signer;
276279
{
277280
UpdateType updateType;
278-
(offset, updateType) = extractUpdateTypeFromAccumulatorHeader(
281+
(offset, updateType, signer) = extractAccumulatorHeaderDetails(
279282
singleUpdateData
280283
);
281284

@@ -293,10 +296,7 @@ abstract contract Pyth is
293296
merkleData.numUpdates,
294297
encoded,
295298
merkleData.slot
296-
) = extractWormholeMerkleHeaderDigestAndNumUpdatesAndEncodedAndSlotFromAccumulatorUpdate(
297-
singleUpdateData,
298-
offset
299-
);
299+
) = extractWormholeMerkleHeader(singleUpdateData, signer, offset);
300300

301301
// Process each update within the Merkle proof
302302
for (uint j = 0; j < merkleData.numUpdates; j++) {
@@ -435,12 +435,13 @@ abstract contract Pyth is
435435
)
436436
{
437437
UpdateType updateType;
438+
Signer signer;
438439
uint offset;
439440
bytes20 digest;
440441
uint8 numUpdates;
441442
bytes calldata encoded;
442443
// Extract and validate the header for start data
443-
(offset, updateType) = extractUpdateTypeFromAccumulatorHeader(
444+
(offset, updateType, signer) = extractAccumulatorHeaderDetails(
444445
updateData
445446
);
446447

@@ -455,10 +456,7 @@ abstract contract Pyth is
455456
encoded,
456457
// slot ignored
457458

458-
) = extractWormholeMerkleHeaderDigestAndNumUpdatesAndEncodedAndSlotFromAccumulatorUpdate(
459-
updateData,
460-
offset
461-
);
459+
) = extractWormholeMerkleHeader(updateData, signer, offset);
462460

463461
// Add additional validation before extracting TWAP price info
464462
if (offset >= updateData.length) {

target_chains/ethereum/contracts/contracts/pyth/PythAccumulator.sol

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,44 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth {
2929
TwapPriceFeed
3030
}
3131

32+
enum Signer {
33+
Wormhole,
34+
Verifier
35+
}
36+
3237
// This method is also used by batch attestation but moved here
3338
// as the batch attestation will deprecate soon.
3439
function parseAndVerifyPythVM(
35-
bytes calldata encodedVm
40+
bytes calldata encodedVm,
41+
Signer signer
3642
) internal view returns (IWormhole.VM memory vm) {
37-
{
38-
bool valid;
43+
bool valid;
44+
if (signer == Signer.Wormhole) {
3945
(vm, valid, ) = wormhole().parseAndVerifyVM(encodedVm);
40-
if (!valid) {
41-
if (address(verifier()) == address(0)) {
42-
revert PythErrors.InvalidUpdateData();
43-
}
44-
(vm, valid, ) = verifier().parseAndVerifyVM(encodedVm);
45-
if (!valid) {
46-
revert PythErrors.InvalidUpdateData();
47-
}
48-
}
46+
} else if (signer == Signer.Verifier) {
47+
if (address(verifier()) == address(0))
48+
revert PythErrors.InvalidUpdateData();
49+
(vm, valid, ) = verifier().parseAndVerifyVM(encodedVm);
50+
} else {
51+
revert PythErrors.InvalidSigner();
4952
}
5053

51-
if (!isValidDataSource(vm.emitterChainId, vm.emitterAddress))
54+
if (!valid) {
55+
revert PythErrors.InvalidUpdateData();
56+
}
57+
58+
if (!isValidDataSource(vm.emitterChainId, vm.emitterAddress)) {
5259
revert PythErrors.InvalidUpdateDataSource();
60+
}
5361
}
5462

55-
function extractUpdateTypeFromAccumulatorHeader(
63+
function extractAccumulatorHeaderDetails(
5664
bytes calldata accumulatorUpdate
57-
) internal pure returns (uint offset, UpdateType updateType) {
65+
)
66+
internal
67+
pure
68+
returns (uint offset, UpdateType updateType, Signer signer)
69+
{
5870
unchecked {
5971
offset = 0;
6072

@@ -98,13 +110,14 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth {
98110
);
99111
offset += 1;
100112

101-
// We use another offset for the trailing header and in the end add the
102-
// offset by trailingHeaderSize to skip the future headers.
103-
//
104-
// An example would be like this:
105-
// uint trailingHeaderOffset = offset
106-
// uint x = UnsafeBytesLib.ToUint8(accumulatorUpdate, trailingHeaderOffset)
107-
// trailingHeaderOffset += 1
113+
uint trailingHeaderOffset = offset;
114+
signer = Signer(
115+
UnsafeCalldataBytesLib.toUint8(
116+
accumulatorUpdate,
117+
trailingHeaderOffset
118+
)
119+
);
120+
trailingHeaderOffset += 1;
108121

109122
offset += trailingHeaderSize;
110123
}
@@ -120,8 +133,9 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth {
120133
}
121134
}
122135

123-
function extractWormholeMerkleHeaderDigestAndNumUpdatesAndEncodedAndSlotFromAccumulatorUpdate(
136+
function extractWormholeMerkleHeader(
124137
bytes calldata accumulatorUpdate,
138+
Signer signer,
125139
uint encodedOffset
126140
)
127141
internal
@@ -156,7 +170,8 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth {
156170
encoded,
157171
offset,
158172
whProofSize
159-
)
173+
),
174+
signer
160175
);
161176
offset += whProofSize;
162177

@@ -179,7 +194,7 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth {
179194
UpdateType updateType = UpdateType(
180195
UnsafeBytesLib.toUint8(encodedPayload, payloadOffset)
181196
);
182-
++payloadOffset;
197+
payloadOffset += 1;
183198

184199
if (updateType != UpdateType.WormholeMerkle)
185200
revert PythErrors.InvalidUpdateData();
@@ -468,8 +483,9 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth {
468483
) internal returns (uint8 numUpdates) {
469484
(
470485
uint encodedOffset,
471-
UpdateType updateType
472-
) = extractUpdateTypeFromAccumulatorHeader(accumulatorUpdate);
486+
UpdateType updateType,
487+
Signer signer
488+
) = extractAccumulatorHeaderDetails(accumulatorUpdate);
473489

474490
if (updateType != UpdateType.WormholeMerkle) {
475491
revert PythErrors.InvalidUpdateData();
@@ -485,8 +501,9 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth {
485501
numUpdates,
486502
encoded,
487503
slot
488-
) = extractWormholeMerkleHeaderDigestAndNumUpdatesAndEncodedAndSlotFromAccumulatorUpdate(
504+
) = extractWormholeMerkleHeader(
489505
accumulatorUpdate,
506+
signer,
490507
encodedOffset
491508
);
492509

target_chains/ethereum/contracts/forge-test/Executor.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import "../contracts/executor/ExecutorUpgradable.sol";
88
import "./utils/WormholeTestUtils.t.sol";
99
import "./utils/InvalidMagic.t.sol";
1010

11-
contract ExecutorTest is Test, WormholeTestUtils {
11+
contract ExecutorTest is Test, AbstractWormholeTestUtils {
1212
Wormhole public wormhole;
1313
ExecutorUpgradable public executor;
1414
ExecutorUpgradable public executor2;

target_chains/ethereum/contracts/forge-test/GasBenchmark.t.sol

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
1111
import "./utils/WormholeTestUtils.t.sol";
1212
import "./utils/PythTestUtils.t.sol";
1313

14-
contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
14+
contract GasBenchmark is Test, PythTestUtils {
1515
// 19, current mainnet number of guardians, is used to have gas estimates
1616
// close to our mainnet transactions.
1717
uint8 constant NUM_GUARDIANS = 19;
@@ -53,9 +53,11 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
5353
uint randomSeed;
5454

5555
function setUp() public {
56-
address wormholeAddr = setUpWormholeReceiver(NUM_GUARDIANS);
57-
wormhole = IWormhole(wormholeAddr);
58-
pyth = IPyth(setUpPyth(wormholeAddr));
56+
WormholeTestUtils wormholeTestUtils = new WormholeTestUtils(
57+
NUM_GUARDIANS
58+
);
59+
wormhole = IWormhole(wormholeTestUtils.getWormholeReceiverAddr());
60+
pyth = IPyth(setUpPyth(wormholeTestUtils));
5961

6062
priceIds = new bytes32[](NUM_PRICES);
6163
priceIds[0] = bytes32(

target_chains/ethereum/contracts/forge-test/Pyth.Aave.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ contract PythAaveTest is PythWormholeMerkleAccumulatorTest {
2424
uint constant VALID_TIME_PERIOD_SECS = 60;
2525

2626
function setUp() public override {
27-
pyth = IPyth(setUpPyth(setUpWormholeReceiver(1)));
27+
pyth = IPyth(setUpPyth(new WormholeTestUtils(1)));
2828
assets = new address[](NUM_PRICE_FEEDS);
2929
PriceFeedMessage[]
3030
memory priceFeedMessages = generateRandomBoundedPriceFeedMessage(

target_chains/ethereum/contracts/forge-test/Pyth.WormholeMerkleAccumulator.t.sol

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,20 @@ import "forge-std/Test.sol";
88
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
99
import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol";
1010
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
11-
import "./utils/WormholeTestUtils.t.sol";
1211
import "./utils/PythTestUtils.t.sol";
1312
import "./utils/RandTestUtils.t.sol";
1413

1514
import "../contracts/libraries/MerkleTree.sol";
15+
import "./utils/WormholeTestUtils.t.sol";
1616

17-
contract PythWormholeMerkleAccumulatorTest is
18-
Test,
19-
WormholeTestUtils,
20-
PythTestUtils
21-
{
17+
contract PythWormholeMerkleAccumulatorTest is Test, PythTestUtils {
2218
IPyth public pyth;
2319

2420
// -1 is equal to 0xffffff which is the biggest uint if converted back
2521
uint64 constant MAX_UINT64 = uint64(int64(-1));
2622

2723
function setUp() public virtual {
28-
pyth = IPyth(setUpPyth(setUpWormholeReceiver(1)));
24+
pyth = IPyth(setUpPyth(new WormholeTestUtils(1)));
2925
}
3026

3127
function assertPriceFeedMessageStored(
@@ -530,42 +526,45 @@ contract PythWormholeMerkleAccumulatorTest is
530526
bytes memory wormholePayload;
531527
unchecked {
532528
wormholePayload = abi.encodePacked(
533-
isNotMatch(forgeItem, "whMagic")
529+
_wormholeTestUtils.isNotMatch(forgeItem, "whMagic")
534530
? uint32(0x41555756)
535531
: uint32(0x41555750),
536-
isNotMatch(forgeItem, "whUpdateType")
532+
_wormholeTestUtils.isNotMatch(forgeItem, "whUpdateType")
537533
? uint8(PythAccumulator.UpdateType.WormholeMerkle)
538534
: uint8(PythAccumulator.UpdateType.WormholeMerkle) + 1,
539535
uint64(0), // Slot, not used in target networks
540536
uint32(0), // Storage index, not used in target networks
541-
isNotMatch(forgeItem, "rootDigest")
537+
_wormholeTestUtils.isNotMatch(forgeItem, "rootDigest")
542538
? rootDigest
543539
: bytes20(uint160(rootDigest) + 1)
544540
);
545541
}
546542

547543
bytes memory wormholeMerkleVaa = generateVaa(
548544
0,
549-
isNotMatch(forgeItem, "whSourceChain")
545+
_wormholeTestUtils.isNotMatch(forgeItem, "whSourceChain")
550546
? SOURCE_EMITTER_CHAIN_ID
551547
: SOURCE_EMITTER_CHAIN_ID + 1,
552-
isNotMatch(forgeItem, "whSourceAddress")
548+
_wormholeTestUtils.isNotMatch(forgeItem, "whSourceAddress")
553549
? SOURCE_EMITTER_ADDRESS
554550
: bytes32(
555551
0x71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa00
556552
),
557553
0,
558554
wormholePayload,
559-
1 // num signers
555+
1, // num signers
556+
false
560557
);
561558

562559
updateData = new bytes[](1);
563560

564561
updateData[0] = abi.encodePacked(
565-
isNotMatch(forgeItem, "headerMagic")
562+
_wormholeTestUtils.isNotMatch(forgeItem, "headerMagic")
566563
? uint32(0x504e4155)
567564
: uint32(0x504e4150), // PythAccumulator.ACCUMULATOR_MAGIC
568-
isNotMatch(forgeItem, "headerMajorVersion") ? uint8(1) : uint8(2), // major version
565+
_wormholeTestUtils.isNotMatch(forgeItem, "headerMajorVersion")
566+
? uint8(1)
567+
: uint8(2), // major version
569568
uint8(0), // minor version
570569
uint8(0), // trailing header size
571570
uint8(PythAccumulator.UpdateType.WormholeMerkle),
@@ -575,11 +574,15 @@ contract PythWormholeMerkleAccumulatorTest is
575574
);
576575

577576
for (uint i = 0; i < priceFeedMessages.length; i++) {
577+
bytes memory proof = proofs[0];
578+
if (_wormholeTestUtils.isNotMatch(forgeItem, "proofItem")) {
579+
proof = proofs[i];
580+
}
578581
updateData[0] = abi.encodePacked(
579582
updateData[0],
580583
uint16(encodedPriceFeedMessages[i].length),
581584
encodedPriceFeedMessages[i],
582-
isNotMatch(forgeItem, "proofItem") ? proofs[i] : proofs[0]
585+
proof
583586
);
584587
}
585588

target_chains/ethereum/contracts/forge-test/Pyth.t.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import "./utils/PythTestUtils.t.sol";
1313
import "./utils/RandTestUtils.t.sol";
1414
import "forge-std/console.sol";
1515

16-
contract PythTest is Test, WormholeTestUtils, PythTestUtils {
16+
contract PythTest is Test, PythTestUtils {
1717
IPyth public pyth;
1818

1919
// -1 is equal to 0xffffff which is the biggest uint if converted back
@@ -32,7 +32,7 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils {
3232
bytes32[2] basePriceIds;
3333

3434
function setUp() public {
35-
pyth = IPyth(setUpPyth(setUpWormholeReceiver(NUM_GUARDIAN_SIGNERS)));
35+
pyth = IPyth(setUpPyth(new WormholeTestUtils(NUM_GUARDIAN_SIGNERS)));
3636

3737
// Initialize base TWAP messages for two price feeds
3838
basePriceIds[0] = bytes32(uint256(1));

0 commit comments

Comments
 (0)