Update ETH smart contract for new signature schema
This commit is contained in:
parent
2a096790d3
commit
6e5a3fa118
|
@ -4,10 +4,8 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/certusone/wormhole/bridge/third_party/chainlink/secp256k1"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"go.dedis.ch/kyber/v3"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
|
@ -79,7 +77,7 @@ type (
|
|||
|
||||
BodyGuardianSetUpdate struct {
|
||||
// Key is the new guardian set key
|
||||
Key kyber.Point
|
||||
Keys []common.Address
|
||||
// NewIndex is the index of the new guardian set
|
||||
NewIndex uint32
|
||||
}
|
||||
|
@ -328,16 +326,22 @@ func (v *BodyTransfer) serialize() ([]byte, error) {
|
|||
func parseBodyGuardianSetUpdate(r io.Reader) (*BodyGuardianSetUpdate, error) {
|
||||
b := &BodyGuardianSetUpdate{}
|
||||
|
||||
b.Key = secp256k1.NewPoint()
|
||||
_, err := b.Key.UnmarshalFrom(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal new key: %w", err)
|
||||
}
|
||||
|
||||
if err := binary.Read(r, binary.BigEndian, &b.NewIndex); err != nil {
|
||||
return nil, fmt.Errorf("failed to read new index: %w", err)
|
||||
}
|
||||
|
||||
keyLen := uint8(0)
|
||||
if err := binary.Read(r, binary.BigEndian, &keyLen); err != nil {
|
||||
return nil, fmt.Errorf("failed to read guardianset key len: %w", err)
|
||||
}
|
||||
for i := 0; i < int(keyLen); i++ {
|
||||
key := common.Address{}
|
||||
if n, err := r.Read(key[:]); err != nil || n != 20 {
|
||||
return nil, fmt.Errorf("failed to read guardianset key [%d]: %w", i, err)
|
||||
}
|
||||
b.Keys = append(b.Keys, key)
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
|
@ -348,15 +352,11 @@ func (v *BodyGuardianSetUpdate) getActionID() Action {
|
|||
func (v *BodyGuardianSetUpdate) serialize() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if v.Key == nil {
|
||||
return nil, fmt.Errorf("key is empty")
|
||||
}
|
||||
_, err := v.Key.MarshalTo(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal key: %w", err)
|
||||
}
|
||||
|
||||
MustWrite(buf, binary.BigEndian, v.NewIndex)
|
||||
MustWrite(buf, binary.BigEndian, uint8(len(v.Keys)))
|
||||
for _, key := range v.Keys {
|
||||
buf.Write(key.Bytes())
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"github.com/certusone/wormhole/bridge/third_party/chainlink/cryptotest"
|
||||
"github.com/certusone/wormhole/bridge/third_party/chainlink/secp256k1"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -60,7 +59,7 @@ func TestSerializeDeserialize(t *testing.T) {
|
|||
},
|
||||
Timestamp: time.Unix(2837, 0),
|
||||
Payload: &BodyGuardianSetUpdate{
|
||||
Key: secp256k1.Generate(randomStream).Public,
|
||||
Keys: []common.Address{{}, {}},
|
||||
NewIndex: 2,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Taken from https://github.com/smartcontractkit/chainlink
|
||||
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
library Schnorr {
|
||||
// See https://en.bitcoin.it/wiki/Secp256k1 for this constant.
|
||||
uint256 constant public Q = // Group order of secp256k1
|
||||
// solium-disable-next-line indentation
|
||||
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
||||
|
||||
/** **************************************************************************
|
||||
@notice verifySignature returns true iff passed a valid Schnorr signature.
|
||||
@dev See https://en.wikipedia.org/wiki/Schnorr_signature for reference.
|
||||
@dev In what follows, let d be your secret key, PK be your public key,
|
||||
PKx be the x ordinate of your public key, and PKyp be the parity bit for
|
||||
the y ordinate (i.e., 0 if PKy is even, 1 if odd.)
|
||||
**************************************************************************
|
||||
@dev TO CREATE A VALID SIGNATURE FOR THIS METHOD
|
||||
@dev 1. Hash the target message to a uint256, called msgHash here, using
|
||||
keccak256
|
||||
@dev 2. Pick k uniformly and cryptographically securely randomly from
|
||||
{0,...,Q-1}. It is critical that k remains confidential, as your
|
||||
private key can be reconstructed from k and the signature.
|
||||
@dev 3. Compute k*g in the secp256k1 group, where g is the group
|
||||
generator. (This is the same as computing the public key from the
|
||||
secret key k.)
|
||||
@dev 4. Compute the ethereum address for k*g. This is the lower 160 bits
|
||||
of the keccak hash of the concatenated affine coordinates of k*g,
|
||||
as 32-byte big-endians. (For instance, you could pass k to
|
||||
ethereumjs-utils's privateToAddress to compute this, though that
|
||||
should be strictly a development convenience, not for handling
|
||||
live secrets, unless you've locked your javascript environment
|
||||
down very carefully.) Call this address
|
||||
nonceTimesGeneratorAddress.
|
||||
@dev 5. Compute e=uint256(keccak256(PKx as a 32-byte big-endian
|
||||
‖ PKyp as a single byte
|
||||
‖ msgHash
|
||||
‖ nonceTimesGeneratorAddress))
|
||||
This value e is called "msgChallenge" in verifySignature's source
|
||||
code below. Here "‖" means concatenation of the listed byte
|
||||
arrays.
|
||||
@dev 6. Let x be your secret key. Compute s = (k - d * e) % Q. Add Q to
|
||||
it, if it's negative. This is your signature. (d is your secret
|
||||
key.)
|
||||
**************************************************************************
|
||||
@dev TO VERIFY A SIGNATURE
|
||||
@dev Given a signature (s, e) of msgHash, constructed as above, compute
|
||||
S=e*PK+s*generator in the secp256k1 group law, and then the ethereum
|
||||
address of S, as described in step 4. Call that
|
||||
nonceTimesGeneratorAddress. Then call the verifySignature method as:
|
||||
@dev verifySignature(PKx, PKyp, s, msgHash,
|
||||
nonceTimesGeneratorAddress)
|
||||
**************************************************************************
|
||||
@dev This signging scheme deviates slightly from the classical Schnorr
|
||||
signature, in that the address of k*g is used in place of k*g itself,
|
||||
both when calculating e and when verifying sum S as described in the
|
||||
verification paragraph above. This reduces the difficulty of
|
||||
brute-forcing a signature by trying random secp256k1 points in place of
|
||||
k*g in the signature verification process from 256 bits to 160 bits.
|
||||
However, the difficulty of cracking the public key using "baby-step,
|
||||
giant-step" is only 128 bits, so this weakening constitutes no compromise
|
||||
in the security of the signatures or the key.
|
||||
**************************************************************************
|
||||
@param signingPubKeyX is the x ordinate of the public key.
|
||||
@param pubKeyYParity is 0 if the y ordinate of the public key is even, 1
|
||||
if it's odd.
|
||||
@param signature is the actual signature, described as s in the above
|
||||
instructions.
|
||||
@param msgHash is a 256-bit hash of the message being signed.
|
||||
@param nonceTimesGeneratorAddress is the ethereum address of k*g in the
|
||||
above instructions
|
||||
**************************************************************************
|
||||
@return True if passed a valid signature, false otherwise. */
|
||||
function verifySignature(
|
||||
uint256 signingPubKeyX,
|
||||
uint8 pubKeyYParity,
|
||||
uint256 signature,
|
||||
uint256 msgHash,
|
||||
address nonceTimesGeneratorAddress) external pure returns (bool) {
|
||||
// Avoid signature malleability from multiple representations for ℤ/Qℤ elts
|
||||
require(signature < Q, "signature must be reduced modulo Q");
|
||||
|
||||
// Forbid trivial inputs, to avoid ecrecover edge cases. The main thing to
|
||||
// avoid is something which causes ecrecover to return 0x0: then trivial
|
||||
// signatures could be constructed with the nonceTimesGeneratorAddress input
|
||||
// set to 0x0.
|
||||
//
|
||||
// solium-disable-next-line indentation
|
||||
require(nonceTimesGeneratorAddress != address(0) && signingPubKeyX > 0 &&
|
||||
signature > 0 && msgHash > 0, "no zero inputs allowed");
|
||||
|
||||
// solium-disable-next-line indentation
|
||||
uint256 msgChallenge = // "e"
|
||||
// solium-disable-next-line indentation
|
||||
uint256(keccak256(abi.encodePacked(signingPubKeyX, pubKeyYParity,
|
||||
msgHash, nonceTimesGeneratorAddress))
|
||||
);
|
||||
|
||||
// Verify msgChallenge * signingPubKey + signature * generator ==
|
||||
// nonce * generator
|
||||
//
|
||||
// https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9
|
||||
// The point corresponding to the address returned by
|
||||
// ecrecover(-s*r,v,r,e*r) is (r⁻¹ mod Q)*(e*r*R-(-s)*r*g)=e*R+s*g, where R
|
||||
// is the (v,r) point. See https://crypto.stackexchange.com/a/18106
|
||||
//
|
||||
// solium-disable-next-line indentation
|
||||
address recoveredAddress = ecrecover(
|
||||
// solium-disable-next-line zeppelin/no-arithmetic-operations
|
||||
bytes32(Q - mulmod(signingPubKeyX, signature, Q)),
|
||||
// https://ethereum.github.io/yellowpaper/paper.pdf p. 24, "The
|
||||
// value 27 represents an even y value and 28 represents an odd
|
||||
// y value."
|
||||
(pubKeyYParity == 0) ? 27 : 28,
|
||||
bytes32(signingPubKeyX),
|
||||
bytes32(mulmod(msgChallenge, signingPubKeyX, Q)));
|
||||
return nonceTimesGeneratorAddress == recoveredAddress;
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|||
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
import "./BytesLib.sol";
|
||||
import "./SchnorrSECP256K1.sol";
|
||||
import "./WrappedAsset.sol";
|
||||
|
||||
contract Wormhole {
|
||||
|
@ -26,8 +25,7 @@ contract Wormhole {
|
|||
address constant WETHAddress = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||
|
||||
struct GuardianSet {
|
||||
uint256 x;
|
||||
uint8 parity;
|
||||
address[] keys;
|
||||
uint32 expiration_time;
|
||||
}
|
||||
|
||||
|
@ -78,34 +76,35 @@ contract Wormhole {
|
|||
// Load 4 bytes starting from index 1
|
||||
uint32 vaa_guardian_set_index = vaa.toUint32(1);
|
||||
|
||||
uint256 signature = vaa.toUint256(5);
|
||||
address sig_address = vaa.toAddress(37);
|
||||
uint256 len_signers = vaa.toUint8(5);
|
||||
uint offset = 6 + 66 * len_signers;
|
||||
|
||||
// Load 4 bytes starting from index 77
|
||||
uint32 timestamp = vaa.toUint32(57);
|
||||
// Load 4 bytes timestamp
|
||||
uint32 timestamp = vaa.toUint32(offset);
|
||||
|
||||
// Verify that the VAA is still valid
|
||||
require(timestamp + vaa_expiry > block.timestamp, "VAA has expired");
|
||||
|
||||
// Hash the body
|
||||
bytes32 hash = keccak256(vaa.slice(57, vaa.length - 57));
|
||||
bytes32 hash = keccak256(vaa.slice(offset, vaa.length - offset));
|
||||
require(!consumedVAAs[hash], "VAA was already executed");
|
||||
|
||||
GuardianSet memory guardian_set = guardian_sets[vaa_guardian_set_index];
|
||||
require(guardian_set.expiration_time == 0 || guardian_set.expiration_time > block.timestamp, "guardian set has expired");
|
||||
require(
|
||||
Schnorr.verifySignature(
|
||||
guardian_set.x,
|
||||
guardian_set.parity,
|
||||
signature,
|
||||
uint256(hash),
|
||||
sig_address
|
||||
),
|
||||
"VAA signature invalid");
|
||||
require(guardian_set.keys.length * 3 / 4 + 1 <= len_signers, "no quorum");
|
||||
|
||||
uint8 action = vaa.toUint8(61);
|
||||
uint8 payload_len = vaa.toUint8(62);
|
||||
bytes memory payload = vaa.slice(63, payload_len);
|
||||
for (uint i = 0; i < len_signers; i++) {
|
||||
uint8 index = vaa.toUint8(6 + i * 66);
|
||||
bytes32 r = vaa.toBytes32(7 + i * 66);
|
||||
bytes32 s = vaa.toBytes32(39 + i * 66);
|
||||
uint8 v = vaa.toUint8(71 + i * 66);
|
||||
v += 27;
|
||||
require(ecrecover(hash, v, r, s) == guardian_set.keys[index], "VAA signature invalid");
|
||||
}
|
||||
|
||||
uint8 action = vaa.toUint8(offset + 4);
|
||||
uint8 payload_len = vaa.toUint8(offset + 5);
|
||||
bytes memory payload = vaa.slice(offset + 6, payload_len);
|
||||
|
||||
// Process VAA
|
||||
if (action == 0x01) {
|
||||
|
@ -122,17 +121,19 @@ contract Wormhole {
|
|||
}
|
||||
|
||||
function vaaUpdateGuardianSet(bytes memory data) private {
|
||||
uint256 new_key_x = data.toUint256(0);
|
||||
uint256 y_parity = data.toUint8(32);
|
||||
uint32 new_guardian_set_index = data.toUint32(33);
|
||||
uint32 new_guardian_set_index = data.toUint32(0);
|
||||
uint8 len = data.toUint8(4);
|
||||
|
||||
require(new_guardian_set_index > guardian_set_index, "index of new guardian set must be > current");
|
||||
require(y_parity <= 1, "invalid y parity");
|
||||
address[] memory new_guardians = new address[](len);
|
||||
for (uint i = 0; i < len; i++) {
|
||||
address addr = data.toAddress(5 + i * 20);
|
||||
new_guardians[i] = addr;
|
||||
}
|
||||
|
||||
uint32 old_guardian_set_index = guardian_set_index;
|
||||
guardian_set_index = new_guardian_set_index;
|
||||
|
||||
GuardianSet memory new_guardian_set = GuardianSet(new_key_x, uint8(y_parity), 0);
|
||||
GuardianSet memory new_guardian_set = GuardianSet(new_guardians, 0);
|
||||
guardian_sets[guardian_set_index] = new_guardian_set;
|
||||
guardian_sets[old_guardian_set_index].expiration_time = uint32(block.timestamp) + vaa_expiry;
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@ module.exports = async function (deployer) {
|
|||
await deployer.deploy(WrappedAsset);
|
||||
await deployer.link(Schnorr, Wormhole);
|
||||
await deployer.deploy(Wormhole, {
|
||||
x: "15420174358166353706216094226583628565375637765325964030087969534155416299009",
|
||||
parity: 1,
|
||||
keys: ["0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf"],
|
||||
expiration_time: 0
|
||||
}, WrappedAsset.address, 1000);
|
||||
};
|
||||
|
|
|
@ -54,7 +54,7 @@ contract("Wormhole", function () {
|
|||
it("should transfer tokens in on valid VAA", async function () {
|
||||
let bridge = await Wormhole.deployed();
|
||||
|
||||
await bridge.submitVAA("0x0100000000008df1ef2b367213cf591e6f6a8de37dd5a4ca771590f6f964a2c4a63b44c1e8532c0e595f4e6e0e784314724c85038af6576de0000007d01087000000330102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000004563918244f40000")
|
||||
await bridge.submitVAA("0x0100000000010092737a1504f3b3df8c93cb85c64a4860bb270e26026b6e37f095356a406f6af439c6b2e9775fa1c6669525f06edab033ba5d447308f4e3bdb33c0f361dc32ec3015f3700081087000000350102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000004563918244f40000")
|
||||
// Expect user to have a balance of a new wrapped asset
|
||||
let wa = new WrappedAsset("0x79183957Be84C0F4dA451E534d5bA5BA3FB9c696");
|
||||
assert.equal(await wa.assetChain(), 1)
|
||||
|
@ -66,8 +66,9 @@ contract("Wormhole", function () {
|
|||
it("should not accept the same VAA twice", async function () {
|
||||
let bridge = await Wormhole.deployed();
|
||||
try {
|
||||
await bridge.submitVAA("0x0100000000008df1ef2b367213cf591e6f6a8de37dd5a4ca771590f6f964a2c4a63b44c1e8532c0e595f4e6e0e784314724c85038af6576de0000007d01087000000330102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000004563918244f40000");
|
||||
await bridge.submitVAA("0x0100000000010092737a1504f3b3df8c93cb85c64a4860bb270e26026b6e37f095356a406f6af439c6b2e9775fa1c6669525f06edab033ba5d447308f4e3bdb33c0f361dc32ec3015f3700081087000000350102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000004563918244f40000");
|
||||
} catch (e) {
|
||||
assert.equal(e.reason, "VAA was already executed")
|
||||
return
|
||||
}
|
||||
assert.fail("did not fail")
|
||||
|
@ -103,7 +104,7 @@ contract("Wormhole", function () {
|
|||
// Transfer of that token out of the contract should not work
|
||||
let threw = false;
|
||||
try {
|
||||
await bridge.submitVAA("0x0100000000636e71c9cb08d64b6388a39d28779fab9dd42edad20331d022c9e90a43b78b1bfc737f2973136230a9e323fbd5d2f7d6cb599c2bfffff82f1087000000310102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1020000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000");
|
||||
await bridge.submitVAA("0x01000000000100f0c5e4e6087c6af17ce51d6e51842a766834e252266fcccd9ad39222a262af4725ff3cd3d954fca7b9964c09f0290dfacefdcaa441f62b5128ec10dce888c0cc005f37017a1087000000350102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1020000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000");
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
}
|
||||
|
@ -127,7 +128,7 @@ contract("Wormhole", function () {
|
|||
assert.equal(await token.balanceOf(bridge.address), "1000000000000000000");
|
||||
|
||||
// Transfer this token back
|
||||
await bridge.submitVAA("0x0100000000636e71c9cb08d64b6388a39d28779fab9dd42edad20331d022c9e90a43b78b1bfc737f2973136230a9e323fbd5d2f7d6cb599c2bfffff82f1087000000310102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1020000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000");
|
||||
await bridge.submitVAA("0x01000000000100f0c5e4e6087c6af17ce51d6e51842a766834e252266fcccd9ad39222a262af4725ff3cd3d954fca7b9964c09f0290dfacefdcaa441f62b5128ec10dce888c0cc005f37017a1087000000350102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1020000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000");
|
||||
assert.equal(await token.balanceOf("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"), "1000000000000000000");
|
||||
assert.equal(await token.balanceOf(bridge.address), "0");
|
||||
});
|
||||
|
@ -137,13 +138,12 @@ contract("Wormhole", function () {
|
|||
|
||||
// Push time by 1000
|
||||
await advanceTimeAndBlock(1000);
|
||||
let ev = await bridge.submitVAA("0x0100000000fe60d5766a84300effedd5362dcf6ff8f4ed75ab3dbe4c1ae07151ab48bc8cbf767b4aa42cf768477dc5bb45367044bd2de6d6b3000003e801253e2f87d126ef42ac22d284de7619d2c87437198a32887efeddb4debfd016747f0000000001")
|
||||
let ev = await bridge.submitVAA("0x010000000001003382c71a4c79e1518a6ce29c91569f6427a60a95696a3515b8c2340b6acffd723315bd1011aa779f22573882a4edfe1b8206548e134871a23f8ba0c1c7d0b5ed0100000bb801190000000101befa429d57cd18b7f8a4d91a2da9ab4af05d0fbe")
|
||||
assert.lengthOf(ev.logs, 1)
|
||||
assert.equal(ev.logs[0].event, "LogGuardianSetChanged")
|
||||
|
||||
// Expect guardian set to transition to 1
|
||||
assert.equal(await bridge.guardian_set_index(), 1);
|
||||
assert.equal((await bridge.guardian_sets(1)).x, "28127375798693063422362909717576839343810687066240716944661469189277081826431");
|
||||
});
|
||||
|
||||
it("should not accept guardian set change from old guardians", async function () {
|
||||
|
@ -152,7 +152,7 @@ contract("Wormhole", function () {
|
|||
// Test update guardian set VAA from guardian set 0; timestamp 2000
|
||||
let threw = false;
|
||||
try {
|
||||
await bridge.submitVAA("0x0100000000cbaa28896d5c77df474f007489b6a42b8815784c0b17d6171de4c55bce58bda8805f2948e4e78b3bad03ff50210f3ebd084263e1000003e801253e2f87d126ef42ac22d284de7619d2c87437198a32887efeddb4debfd016747f0000000002")
|
||||
await bridge.submitVAA("0x01000000000100686c37a81f0895d0db88c5c348bba8df53dedd579116327c999dc0229157c04e0304f9f8223b4e7b538ccf140de112d456d88e040bce025c1022bb840acb88390100000bb801190000000201befa429d57cd18b7f8a4d91a2da9ab4af05d0fbe")
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
assert.equal(e.reason, "only the current guardian set can change the guardian set")
|
||||
|
@ -164,14 +164,14 @@ contract("Wormhole", function () {
|
|||
let bridge = await Wormhole.deployed();
|
||||
|
||||
// Test VAA from guardian set 0; timestamp 1000
|
||||
await bridge.submitVAA("0x01000000004f871da18c25af540bf7ea0ef28df13ff8945903fa1b82aa5d11ff749f33dba57b6064666dfe07b627e5e1da1f4bf620f92c15c2000003e81087000000340102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000004563918244f40000")
|
||||
await bridge.submitVAA("0x01000000000100a60fd865ceee4cf34048fec8edc540f257d05c186d1ac6904d959d35ab2b6c0518feeb01fc3927b44d92746461d0ddb5ea0008de529b8a4862e18acf1fea364c00000003e81087000000360102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||
|
||||
await advanceTimeAndBlock(1000);
|
||||
|
||||
// Test VAA from guardian set 0; timestamp 2000 - should not work anymore
|
||||
let threw = false;
|
||||
try {
|
||||
await bridge.submitVAA("0x01000000004629dc39ea4b284d31f9c7d5350013aeed4b1c38a80fc65fb21e6c7da5ebd0eb13b46039f40a0ddd7c94c3e974b51cacf9eaa1bb000007d01087000000340102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000004563918244f40000")
|
||||
await bridge.submitVAA("0x010000000001002a17cefb8242bc6865d3e38abd764359fcb4cb774637d483aa8690a223b334217e75d1e808dcc6999fa73fabdf20d28455fe4c3abcf565db351456df418f0b7900000007d01087000000360102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
assert.equal(e.reason, "guardian set has expired")
|
||||
|
@ -179,7 +179,7 @@ contract("Wormhole", function () {
|
|||
assert.isTrue(threw, "guardian set did not expire")
|
||||
|
||||
// Test same transaction with guardian set 1; timestamp 2000
|
||||
await bridge.submitVAA("0x01000000011322402df3ec812a145aa2d9b0f627ff3654c9b3ca471622a1439e81da62ec384ad14db65ae4bee55a23b8082628590902e3d778000007d01087000000340102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000004563918244f40000")
|
||||
await bridge.submitVAA("0x010000000101005cae5dc08ebab209640fb5b8051261a5cff25bd84a69f93ec36a4106fde6a53e7275267596a4833607aae8ae9426b7bd10d8062f06c96dc9c820e30516e32e0400000007d01087000000350102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||
});
|
||||
|
||||
it("should expire VAA", async function () {
|
||||
|
@ -191,7 +191,7 @@ contract("Wormhole", function () {
|
|||
// Test same transaction with guardian set 1; timestamp 2000
|
||||
let threw = false;
|
||||
try {
|
||||
await bridge.submitVAA("0x01000000013faebdc02d6427d1e8d33919fbaa519ca402323723922c772e4e2da7fedc820c15b24aa5e4c99bec6a9f4c9b612970590ea3acd1000007d01087000000350102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000004563918244f40000")
|
||||
await bridge.submitVAA("0x01000000010100f69b3f6e31fbbe6ce9b9b1be8e8effded63b44ab8d7d2dc993c914d50d4bb6fe75cdf6ebb15e5bf209f2ea608e496283d8ff5a91a102f1cab42e9093cbb50b6201000007d01087000000360102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
assert.equal(e.reason, "VAA has expired")
|
||||
|
@ -206,11 +206,31 @@ contract("Wormhole", function () {
|
|||
// Test VAA signed by guardian set 0 but set guardian set index to 1
|
||||
let threw = false;
|
||||
try {
|
||||
await bridge.submitVAA("0x01000000015672c0a0e9f27f002bca12fb165e03b9e1d093bc1565eeefec11abbe5a420cf10fd932604a3075566d069f46b09d6a4c860f179300000bb801253e2f87d126ef42ac22d284de7619d2c87437198a32887efeddb4debfd016747f0000000003")
|
||||
await bridge.submitVAA("0x010000000101006f84df72f3f935543e9bda60d92f77e2e2c073655311f3fc00518bbe7e054ff87e5e6e3c9df9e5bd756ee033253d4513ddebf03ff844fdc0f48f7dcc1b3fd6e10000000fa01087000000370102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
assert.equal(e.reason, "VAA signature invalid")
|
||||
}
|
||||
assert.isTrue(threw, "invalid signature accepted")
|
||||
});
|
||||
|
||||
it("quorum should be honored", async function () {
|
||||
let bridge = await Wormhole.deployed();
|
||||
|
||||
// Update to validator set 2 with 6 signers
|
||||
await bridge.submitVAA("0x010000000101006ec1d2ab1b9c24fecfc43265366038ea06d465c422cb92348d757436846fe908068e92d0bdca740c583a717da7cd525e46d80b0b945a51baae72007e456b8a240100001388017d00000002067e5f4552091a69125d5dfcb7b8c2659029395bdfbefa429d57cd18b7f8a4d91a2da9ab4af05d0fbebefa429d57cd18b7f8a4d91a2da9ab4af05d0fbebefa429d57cd18b7f8a4d91a2da9ab4af05d0fbebefa429d57cd18b7f8a4d91a2da9ab4af05d0fbebefa429d57cd18b7f8a4d91a2da9ab4af05d0fbe")
|
||||
|
||||
// Test VAA signed by only 3 signers
|
||||
let threw = false;
|
||||
try {
|
||||
await bridge.submitVAA("0x01000000020300d943f7e2f94fdb2d23d8ce270c1b981ce8058a94c46358e4fb486f9b80c685d234fe7fee41c054c5ed5aa26548e39bc23875549e72100b02aabe1e9bd4c7d9b601018ce763f32e07d8d2d69907575425ad9ffbda4be1917a64ea2f90172ae8212e2c2c04917956ab5a72b6ef4642f1673e28465567de3ad3197d1a773e28c03c40c100028ce763f32e07d8d2d69907575425ad9ffbda4be1917a64ea2f90172ae8212e2c2c04917956ab5a72b6ef4642f1673e28465567de3ad3197d1a773e28c03c40c10000000fa01087000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
assert.equal(e.reason, "no quorum")
|
||||
}
|
||||
assert.isTrue(threw, "accepted only 3 signatures")
|
||||
|
||||
// Test VAA signed by 5 signers (all except i=3)
|
||||
await bridge.submitVAA("0x01000000020500d943f7e2f94fdb2d23d8ce270c1b981ce8058a94c46358e4fb486f9b80c685d234fe7fee41c054c5ed5aa26548e39bc23875549e72100b02aabe1e9bd4c7d9b601018ce763f32e07d8d2d69907575425ad9ffbda4be1917a64ea2f90172ae8212e2c2c04917956ab5a72b6ef4642f1673e28465567de3ad3197d1a773e28c03c40c100028ce763f32e07d8d2d69907575425ad9ffbda4be1917a64ea2f90172ae8212e2c2c04917956ab5a72b6ef4642f1673e28465567de3ad3197d1a773e28c03c40c100048ce763f32e07d8d2d69907575425ad9ffbda4be1917a64ea2f90172ae8212e2c2c04917956ab5a72b6ef4642f1673e28465567de3ad3197d1a773e28c03c40c100058ce763f32e07d8d2d69907575425ad9ffbda4be1917a64ea2f90172ae8212e2c2c04917956ab5a72b6ef4642f1673e28465567de3ad3197d1a773e28c03c40c10000000fa01087000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,79 +18,77 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// const HDWalletProvider = require('@truffle/hdwallet-provider');
|
||||
// const infuraKey = "fj4jll3k.....";
|
||||
//
|
||||
// const fs = require('fs');
|
||||
// const mnemonic = fs.readFileSync(".secret").toString().trim();
|
||||
const HDWalletProvider = require('@truffle/hdwallet-provider');
|
||||
const infuraKey = "94aa97cf706648c49af56e080b0f1bc3";
|
||||
let mnemonic = "own great grid romance once pilot draw chuckle network desert race dream";
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Networks define how you connect to your ethereum client and let you set the
|
||||
* defaults web3 uses to send transactions. If you don't specify one truffle
|
||||
* will spin up a development blockchain for you on port 9545 when you
|
||||
* run `develop` or `test`. You can ask a truffle command to use a specific
|
||||
* network from the command line, e.g
|
||||
*
|
||||
* $ truffle test --network <network-name>
|
||||
*/
|
||||
/**
|
||||
* Networks define how you connect to your ethereum client and let you set the
|
||||
* defaults web3 uses to send transactions. If you don't specify one truffle
|
||||
* will spin up a development blockchain for you on port 9545 when you
|
||||
* run `develop` or `test`. You can ask a truffle command to use a specific
|
||||
* network from the command line, e.g
|
||||
*
|
||||
* $ truffle test --network <network-name>
|
||||
*/
|
||||
|
||||
networks: {
|
||||
// Useful for testing. The `development` name is special - truffle uses it by default
|
||||
// if it's defined here and no other network is specified at the command line.
|
||||
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
|
||||
// tab if you use this network and you must also set the `host`, `port` and `network_id`
|
||||
// options below to some value.
|
||||
//
|
||||
development: {
|
||||
host: "127.0.0.1", // Localhost (default: none)
|
||||
port: 8545, // Standard Ethereum port (default: none)
|
||||
network_id: "*", // Any network (default: none)
|
||||
networks: {
|
||||
// Useful for testing. The `development` name is special - truffle uses it by default
|
||||
// if it's defined here and no other network is specified at the command line.
|
||||
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
|
||||
// tab if you use this network and you must also set the `host`, `port` and `network_id`
|
||||
// options below to some value.
|
||||
//
|
||||
development: {
|
||||
host: "127.0.0.1", // Localhost (default: none)
|
||||
port: 8545, // Standard Ethereum port (default: none)
|
||||
network_id: "*", // Any network (default: none)
|
||||
},
|
||||
// Another network with more advanced options...
|
||||
// advanced: {
|
||||
// port: 8777, // Custom port
|
||||
// network_id: 1342, // Custom network
|
||||
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
|
||||
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
|
||||
// from: <address>, // Account to send txs from (default: accounts[0])
|
||||
// websockets: true // Enable EventEmitter interface for web3 (default: false)
|
||||
// },
|
||||
// Useful for deploying to a public network.
|
||||
// NB: It's important to wrap the provider as a function.
|
||||
rinkeby: {
|
||||
provider: () => new HDWalletProvider(mnemonic, `https://rinkeby.infura.io/v3/94aa97cf706648c49af56e080b0f1bc3`),
|
||||
network_id: 4, // Ropsten's id
|
||||
gas: 5500000, // Ropsten has a lower block limit than mainnet
|
||||
confirmations: 2, // # of confs to wait between deployments. (default: 0)
|
||||
timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
|
||||
skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
|
||||
},
|
||||
// Useful for private networks
|
||||
// private: {
|
||||
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
|
||||
// network_id: 2111, // This network is yours, in the cloud.
|
||||
// production: true // Treats this network as if it was a public net. (default: false)
|
||||
// }
|
||||
},
|
||||
// Another network with more advanced options...
|
||||
// advanced: {
|
||||
// port: 8777, // Custom port
|
||||
// network_id: 1342, // Custom network
|
||||
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
|
||||
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
|
||||
// from: <address>, // Account to send txs from (default: accounts[0])
|
||||
// websockets: true // Enable EventEmitter interface for web3 (default: false)
|
||||
// },
|
||||
// Useful for deploying to a public network.
|
||||
// NB: It's important to wrap the provider as a function.
|
||||
// ropsten: {
|
||||
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
|
||||
// network_id: 3, // Ropsten's id
|
||||
// gas: 5500000, // Ropsten has a lower block limit than mainnet
|
||||
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
|
||||
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
|
||||
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
|
||||
// },
|
||||
// Useful for private networks
|
||||
// private: {
|
||||
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
|
||||
// network_id: 2111, // This network is yours, in the cloud.
|
||||
// production: true // Treats this network as if it was a public net. (default: false)
|
||||
// }
|
||||
},
|
||||
|
||||
// Set default mocha options here, use special reporters etc.
|
||||
mocha: {
|
||||
// timeout: 100000
|
||||
},
|
||||
|
||||
// Configure your compilers
|
||||
compilers: {
|
||||
solc: {
|
||||
version: "0.6.12", // Fetch exact version from solc-bin (default: truffle's version)
|
||||
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
|
||||
// settings: { // See the solidity docs for advice about optimization and evmVersion
|
||||
// optimizer: {
|
||||
// enabled: false,
|
||||
// runs: 200
|
||||
// },
|
||||
// evmVersion: "byzantium"
|
||||
// }
|
||||
// Set default mocha options here, use special reporters etc.
|
||||
mocha: {
|
||||
// timeout: 100000
|
||||
},
|
||||
|
||||
// Configure your compilers
|
||||
compilers: {
|
||||
solc: {
|
||||
version: "0.6.12", // Fetch exact version from solc-bin (default: truffle's version)
|
||||
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
|
||||
// settings: { // See the solidity docs for advice about optimization and evmVersion
|
||||
// optimizer: {
|
||||
// enabled: false,
|
||||
// runs: 200
|
||||
// },
|
||||
// evmVersion: "byzantium"
|
||||
// }
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue