wormhole/node/pkg/governor/governor_test.go

1722 lines
57 KiB
Go

package governor
import (
"context"
"encoding/binary"
"fmt"
"math"
"math/big"
"testing"
"time"
eth_common "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/certusone/wormhole/node/pkg/common"
"github.com/certusone/wormhole/node/pkg/db"
"github.com/wormhole-foundation/wormhole/sdk/vaa"
"go.uber.org/zap"
)
// This is so we can have consistent config data for unit tests.
func (gov *ChainGovernor) initConfigForTest(
emitterChainID vaa.ChainID,
emitterAddr vaa.Address,
dailyLimit uint64,
tokenChainID vaa.ChainID,
tokenAddr vaa.Address,
tokenSymbol string,
tokenPrice float64,
tokenDecimals int64,
) {
gov.chains[emitterChainID] = &chainEntry{emitterChainId: emitterChainID, emitterAddr: emitterAddr, dailyLimit: dailyLimit}
price := big.NewFloat(tokenPrice)
decimalsFloat := big.NewFloat(math.Pow(10.0, float64(tokenDecimals)))
decimals, _ := decimalsFloat.Int(nil)
key := tokenKey{chain: tokenChainID, addr: tokenAddr}
gov.tokens[key] = &tokenEntry{price: price, decimals: decimals, symbol: tokenSymbol, token: key}
}
func (gov *ChainGovernor) setDayLengthInMinutes(min int) {
gov.dayLengthInMinutes = min
}
func (gov *ChainGovernor) setChainForTesting(emitterChainId vaa.ChainID, emitterAddrStr string, dailyLimit uint64, bigTransactionSize uint64) error {
gov.mutex.Lock()
defer gov.mutex.Unlock()
emitterAddr, err := vaa.StringToAddress(emitterAddrStr)
if err != nil {
return err
}
ce := &chainEntry{
emitterChainId: emitterChainId,
emitterAddr: emitterAddr,
dailyLimit: dailyLimit,
bigTransactionSize: bigTransactionSize,
checkForBigTransactions: bigTransactionSize != 0,
}
gov.chains[emitterChainId] = ce
return nil
}
func (gov *ChainGovernor) setTokenForTesting(tokenChainID vaa.ChainID, tokenAddrStr string, symbol string, price float64) error {
gov.mutex.Lock()
defer gov.mutex.Unlock()
tokenAddr, err := vaa.StringToAddress(tokenAddrStr)
if err != nil {
return err
}
bigPrice := big.NewFloat(price)
decimalsFloat := big.NewFloat(math.Pow(10.0, float64(8)))
decimals, _ := decimalsFloat.Int(nil)
key := tokenKey{chain: vaa.ChainID(tokenChainID), addr: tokenAddr}
te := &tokenEntry{cfgPrice: bigPrice, price: bigPrice, decimals: decimals, symbol: symbol, coinGeckoId: symbol, token: key}
gov.tokens[key] = te
cge, cgExists := gov.tokensByCoinGeckoId[te.coinGeckoId]
if !cgExists {
gov.tokensByCoinGeckoId[te.coinGeckoId] = []*tokenEntry{te}
} else {
cge = append(cge, te)
gov.tokensByCoinGeckoId[te.coinGeckoId] = cge
}
return nil
}
func (gov *ChainGovernor) getStatsForAllChains() (numTrans int, valueTrans uint64, numPending int, valuePending uint64) {
gov.mutex.Lock()
defer gov.mutex.Unlock()
for _, ce := range gov.chains {
numTrans += len(ce.transfers)
for _, te := range ce.transfers {
valueTrans += te.Value
}
numPending += len(ce.pending)
for _, pe := range ce.pending {
value, _ := computeValue(pe.amount, pe.token)
valuePending += value
}
}
return
}
func TestTrimEmptyTransfers(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
now, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:00pm (CST)")
require.NoError(t, err)
var transfers []*db.Transfer
sum, updatedTransfers, err := gov.TrimAndSumValue(transfers, now)
require.NoError(t, err)
assert.Equal(t, uint64(0), sum)
assert.Equal(t, 0, len(updatedTransfers))
}
func TestSumAllFromToday(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
now, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:00pm (CST)")
require.NoError(t, err)
var transfers []*db.Transfer
transferTime, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 11:00am (CST)")
require.NoError(t, err)
transfers = append(transfers, &db.Transfer{Value: 125000, Timestamp: transferTime})
sum, updatedTransfers, err := gov.TrimAndSumValue(transfers, now.Add(-time.Hour*24))
require.NoError(t, err)
assert.Equal(t, uint64(125000), sum)
assert.Equal(t, 1, len(updatedTransfers))
}
func TestTrimOneOfTwoTransfers(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
now, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:00pm (CST)")
require.NoError(t, err)
var transfers []*db.Transfer
// The first transfer should be expired.
transferTime1, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "May 31, 2022 at 11:59am (CST)")
require.NoError(t, err)
transfers = append(transfers, &db.Transfer{Value: 125000, Timestamp: transferTime1})
// But the second should not.
transferTime2, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "May 31, 2022 at 1:00pm (CST)")
require.NoError(t, err)
transfers = append(transfers, &db.Transfer{Value: 225000, Timestamp: transferTime2})
assert.Equal(t, 2, len(transfers))
sum, updatedTransfers, err := gov.TrimAndSumValue(transfers, now.Add(-time.Hour*24))
require.NoError(t, err)
assert.Equal(t, 1, len(updatedTransfers))
assert.Equal(t, uint64(225000), sum)
}
func TestTrimSeveralTransfers(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
now, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:00pm (CST)")
require.NoError(t, err)
var transfers []*db.Transfer
// The first two transfers should be expired.
transferTime1, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "May 31, 2022 at 10:00am (CST)")
require.NoError(t, err)
transfers = append(transfers, &db.Transfer{Value: 125000, Timestamp: transferTime1})
transferTime2, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "May 31, 2022 at 11:00am (CST)")
require.NoError(t, err)
transfers = append(transfers, &db.Transfer{Value: 135000, Timestamp: transferTime2})
// But the next three should not.
transferTime3, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "May 31, 2022 at 1:00pm (CST)")
require.NoError(t, err)
transfers = append(transfers, &db.Transfer{Value: 145000, Timestamp: transferTime3})
transferTime4, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "May 31, 2022 at 2:00pm (CST)")
require.NoError(t, err)
transfers = append(transfers, &db.Transfer{Value: 155000, Timestamp: transferTime4})
transferTime5, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "May 31, 2022 at 2:00pm (CST)")
require.NoError(t, err)
transfers = append(transfers, &db.Transfer{Value: 165000, Timestamp: transferTime5})
assert.Equal(t, 5, len(transfers))
sum, updatedTransfers, err := gov.TrimAndSumValue(transfers, now.Add(-time.Hour*24))
require.NoError(t, err)
assert.Equal(t, 3, len(updatedTransfers))
assert.Equal(t, uint64(465000), sum)
}
func TestTrimmingAllTransfersShouldReturnZero(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
now, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:00pm (CST)")
require.NoError(t, err)
var transfers []*db.Transfer
transferTime1, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "May 31, 2022 at 11:00am (CST)")
require.NoError(t, err)
transfers = append(transfers, &db.Transfer{Value: 125000, Timestamp: transferTime1})
transferTime2, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "May 31, 2022 at 11:45am (CST)")
require.NoError(t, err)
transfers = append(transfers, &db.Transfer{Value: 225000, Timestamp: transferTime2})
assert.Equal(t, 2, len(transfers))
sum, updatedTransfers, err := gov.TrimAndSumValue(transfers, now)
require.NoError(t, err)
assert.Equal(t, 0, len(updatedTransfers))
assert.Equal(t, uint64(0), sum)
}
func newChainGovernorForTest(ctx context.Context) (*ChainGovernor, error) {
if ctx == nil {
return nil, fmt.Errorf("ctx is nil")
}
logger := zap.NewNop()
var db db.MockGovernorDB
gov := NewChainGovernor(logger, &db, GoTestMode)
err := gov.Run(ctx)
if err != nil {
return gov, nil
}
emitterAddr, err := vaa.StringToAddress("0x0290fb167208af455bb137780163b7b7a9a10c16")
if err != nil {
return gov, nil
}
tokenAddr, err := vaa.StringToAddress("0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E")
if err != nil {
return gov, nil
}
gov.initConfigForTest(
vaa.ChainIDEthereum,
emitterAddr,
1000000,
vaa.ChainIDEthereum,
tokenAddr,
"WETH",
1774.62,
8,
)
return gov, nil
}
// Converts a string into a go-ethereum Hash object used as test input.
func hashFromString(str string) eth_common.Hash {
if (len(str) > 2) && (str[0] == '0') && (str[1] == 'x') {
str = str[2:]
}
return eth_common.HexToHash(str)
}
func TestVaaForUninterestingEmitterChain(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
emitterAddr, _ := vaa.StringToAddress("0x00")
var payload = []byte{1, 97, 97, 97, 97, 97}
msg := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDSolana,
EmitterAddress: emitterAddr,
ConsistencyLevel: uint8(32),
Payload: payload,
}
canPost, err := gov.ProcessMsgForTime(&msg, time.Now())
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
}
func TestVaaForUninterestingEmitterAddress(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
emitterAddr, _ := vaa.StringToAddress("0x00")
var payload = []byte{1, 97, 97, 97, 97, 97}
msg := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: emitterAddr,
ConsistencyLevel: uint8(32),
Payload: payload,
}
canPost, err := gov.ProcessMsgForTime(&msg, time.Now())
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 0, len(gov.msgsSeen))
}
func TestVaaForUninterestingPayloadType(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
emitterAddr, _ := vaa.StringToAddress("0x0290fb167208af455bb137780163b7b7a9a10c16")
var payload = []byte{2, 97, 97, 97, 97, 97}
msg := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: emitterAddr,
ConsistencyLevel: uint8(32),
Payload: payload,
}
canPost, err := gov.ProcessMsgForTime(&msg, time.Now())
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 0, len(gov.msgsSeen))
}
// Note this method assumes 18 decimals for the amount.
func buildMockTransferPayloadBytes(
t uint8,
tokenChainID vaa.ChainID,
tokenAddrStr string,
toChainID vaa.ChainID,
toAddrStr string,
amtFloat float64,
) []byte {
bytes := make([]byte, 101)
bytes[0] = t
amtBigFloat := big.NewFloat(amtFloat)
amtBigFloat = amtBigFloat.Mul(amtBigFloat, big.NewFloat(100000000))
amount, _ := amtBigFloat.Int(nil)
amtBytes := amount.Bytes()
if len(amtBytes) > 32 {
panic("amount will not fit in 32 bytes!")
}
copy(bytes[33-len(amtBytes):33], amtBytes)
tokenAddr, _ := vaa.StringToAddress(tokenAddrStr)
copy(bytes[33:65], tokenAddr.Bytes())
binary.BigEndian.PutUint16(bytes[65:67], uint16(tokenChainID))
toAddr, _ := vaa.StringToAddress(toAddrStr)
copy(bytes[67:99], toAddr.Bytes())
binary.BigEndian.PutUint16(bytes[99:101], uint16(toChainID))
return bytes
}
func TestBuidMockTransferPayload(t *testing.T) {
tokenAddrStr := "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E" //nolint:gosec
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
payloadBytes := buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
1.25,
)
payload, err := vaa.DecodeTransferPayloadHdr(payloadBytes)
require.NoError(t, err)
expectedTokenAddr, err := vaa.StringToAddress(tokenAddrStr)
require.NoError(t, err)
expectedToAddr, err := vaa.StringToAddress(toAddrStr)
require.NoError(t, err)
expected := &vaa.TransferPayloadHdr{
Type: 1,
Amount: big.NewInt(125000000),
OriginAddress: expectedTokenAddr,
OriginChain: vaa.ChainIDEthereum,
TargetAddress: expectedToAddr,
TargetChain: vaa.ChainIDPolygon,
}
assert.Equal(t, expected, payload)
}
func TestVaaForUninterestingToken(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
uninterestingTokenAddrStr := "0x42"
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
payloadBytes := buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
uninterestingTokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
1.25,
)
tokenBridgeAddr, _ := vaa.StringToAddress("0x0290fb167208af455bb137780163b7b7a9a10c16")
msg := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: payloadBytes,
}
canPost, err := gov.ProcessMsgForTime(&msg, time.Now())
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 0, len(gov.msgsSeen))
}
func TestTransfersUpToAndOverTheLimit(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
tokenAddrStr := "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E" //nolint:gosec
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
tokenBridgeAddrStr := "0x0290fb167208af455bb137780163b7b7a9a10c16" //nolint:gosec
tokenBridgeAddr, err := vaa.StringToAddress(tokenBridgeAddrStr)
require.NoError(t, err)
gov.setDayLengthInMinutes(24 * 60)
err = gov.setChainForTesting(vaa.ChainIDEthereum, tokenBridgeAddrStr, 1000000, 0)
require.NoError(t, err)
err = gov.setTokenForTesting(vaa.ChainIDEthereum, tokenAddrStr, "WETH", 1774.62)
require.NoError(t, err)
payloadBytes1 := buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
1.25,
)
// The first two transfers should be accepted.
msg1 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: payloadBytes1,
}
msg2 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(2),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: payloadBytes1,
}
canPost, err := gov.ProcessMsgForTime(&msg1, time.Now())
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 1, numTrans)
assert.Equal(t, uint64(2218), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
canPost, err = gov.ProcessMsgForTime(&msg2, time.Now())
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(4436), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 2, len(gov.msgsSeen))
// But the third one should be queued up.
payloadBytes2 := buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
1250,
)
msg3 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(3),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: payloadBytes2,
}
canPost, err = gov.ProcessMsgForTime(&msg3, time.Now())
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(4436), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(2218274), valuePending)
assert.Equal(t, 3, len(gov.msgsSeen))
// But a small one should still go through.
msg4 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(4),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: payloadBytes1,
}
canPost, err = gov.ProcessMsgForTime(&msg4, time.Now())
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 3, numTrans)
assert.Equal(t, uint64(4436+2218), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(2218274), valuePending)
assert.Equal(t, 4, len(gov.msgsSeen))
}
func TestPendingTransferBeingReleased(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
tokenAddrStr := "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E" //nolint:gosec
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
tokenBridgeAddrStr := "0x0290fb167208af455bb137780163b7b7a9a10c16" //nolint:gosec
tokenBridgeAddr, err := vaa.StringToAddress(tokenBridgeAddrStr)
require.NoError(t, err)
gov.setDayLengthInMinutes(24 * 60)
err = gov.setChainForTesting(vaa.ChainIDEthereum, tokenBridgeAddrStr, 1000000, 0)
require.NoError(t, err)
err = gov.setTokenForTesting(vaa.ChainIDEthereum, tokenAddrStr, "WETH", 1774.62)
require.NoError(t, err)
// The first VAA should be accepted.
payloadBytes1 := buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
270,
)
msg1 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: payloadBytes1,
}
now, _ := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:00pm (CST)")
canPost, err := gov.ProcessMsgForTime(&msg1, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 1, numTrans)
assert.Equal(t, uint64(479147), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
// And so should the second.
payloadBytes2 := buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
275,
)
msg2 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: payloadBytes2,
}
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 6:00pm (CST)")
canPost, err = gov.ProcessMsgForTime(&msg2, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(479147+488020), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 2, len(gov.msgsSeen))
// But the third one should be queued up.
payloadBytes3 := buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
280,
)
msg3 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: payloadBytes3,
}
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 2:00am (CST)")
canPost, err = gov.ProcessMsgForTime(&msg3, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(479147+488020), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(496893), valuePending)
assert.Equal(t, 3, len(gov.msgsSeen))
// And so should the fourth one.
payloadBytes4 := buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
300,
)
msg4 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: payloadBytes4,
}
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 8:00am (CST)")
canPost, err = gov.ProcessMsgForTime(&msg4, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(479147+488020), valueTrans)
assert.Equal(t, 2, numPending)
assert.Equal(t, uint64(496893+532385), valuePending)
assert.Equal(t, 4, len(gov.msgsSeen))
// If we check pending before noon, nothing should happen.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 9:00am (CST)")
toBePublished, err := gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 0, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(479147+488020), valueTrans)
assert.Equal(t, 2, numPending)
assert.Equal(t, uint64(496893+532385), valuePending)
assert.Equal(t, 4, len(gov.msgsSeen))
// But at 3pm, the first one should drop off and the first queued one should get posted.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 3:00pm (CST)")
toBePublished, err = gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 1, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
require.NoError(t, err)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(488020+496893), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(532385), valuePending)
assert.Equal(t, 3, len(gov.msgsSeen))
}
func TestSmallerPendingTransfersAfterBigOneShouldGetReleased(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
tokenAddrStr := "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E" //nolint:gosec
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
tokenBridgeAddrStr := "0x0290fb167208af455bb137780163b7b7a9a10c16" //nolint:gosec
tokenBridgeAddr, err := vaa.StringToAddress(tokenBridgeAddrStr)
require.NoError(t, err)
gov.setDayLengthInMinutes(24 * 60)
err = gov.setChainForTesting(vaa.ChainIDEthereum, tokenBridgeAddrStr, 1000000, 0)
require.NoError(t, err)
err = gov.setTokenForTesting(vaa.ChainIDEthereum, tokenAddrStr, "WETH", 1774.62)
require.NoError(t, err)
// The first VAA should be accepted.
msg1 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
270,
),
}
now, _ := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:00pm (CST)")
canPost, err := gov.ProcessMsgForTime(&msg1, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 1, numTrans)
assert.Equal(t, uint64(479147), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
// And so should the second.
msg2 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
275,
),
}
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 6:00pm (CST)")
canPost, err = gov.ProcessMsgForTime(&msg2, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(479147+488020), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 2, len(gov.msgsSeen))
// But the third, big one should be queued up.
msg3 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
500,
),
}
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 2:00am (CST)")
canPost, err = gov.ProcessMsgForTime(&msg3, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(479147+488020), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(887309), valuePending)
assert.Equal(t, 3, len(gov.msgsSeen))
// A fourth, smaller, but still too big one, should get enqueued.
msg4 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
100,
),
}
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 8:00am (CST)")
canPost, err = gov.ProcessMsgForTime(&msg4, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(479147+488020), valueTrans)
assert.Equal(t, 2, numPending)
assert.Equal(t, uint64(887309+177461), valuePending)
assert.Equal(t, 4, len(gov.msgsSeen))
// A fifth, smaller, but still too big one, should also get enqueued.
msg5 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
101,
),
}
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 8:00am (CST)")
canPost, err = gov.ProcessMsgForTime(&msg5, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(479147+488020), valueTrans)
assert.Equal(t, 3, numPending)
assert.Equal(t, uint64(887309+177461+179236), valuePending)
assert.Equal(t, 5, len(gov.msgsSeen))
// A sixth, big one should also get enqueued.
msg6 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
501,
),
}
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 2:00am (CST)")
canPost, err = gov.ProcessMsgForTime(&msg6, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(479147+488020), valueTrans)
assert.Equal(t, 4, numPending)
assert.Equal(t, uint64(887309+177461+179236+889084), valuePending)
assert.Equal(t, 6, len(gov.msgsSeen))
// If we check pending before noon, nothing should happen.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 9:00am (CST)")
toBePublished, err := gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 0, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(479147+488020), valueTrans)
assert.Equal(t, 4, numPending)
assert.Equal(t, uint64(887309+177461+179236+889084), valuePending)
assert.Equal(t, 6, len(gov.msgsSeen))
// But at 3pm, the first one should drop off. This should result in the second and third, smaller pending ones being posted, but not the two big ones.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 3:00pm (CST)")
toBePublished, err = gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 2, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
require.NoError(t, err)
assert.Equal(t, 3, numTrans)
assert.Equal(t, uint64(488020+177461+179236), valueTrans)
assert.Equal(t, 2, numPending)
assert.Equal(t, uint64(887309+889084), valuePending)
assert.Equal(t, 5, len(gov.msgsSeen))
}
func TestMainnetConfigIsValid(t *testing.T) {
logger := zap.NewNop()
var db db.MockGovernorDB
gov := NewChainGovernor(logger, &db, GoTestMode)
gov.env = MainNetMode
err := gov.initConfig()
require.NoError(t, err)
}
func TestTestnetConfigIsValid(t *testing.T) {
logger := zap.NewNop()
var db db.MockGovernorDB
gov := NewChainGovernor(logger, &db, GoTestMode)
gov.env = TestNetMode
err := gov.initConfig()
require.NoError(t, err)
}
func TestLargeTransactionGetsEnqueuedAndReleasedWhenTheTimerExpires(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
tokenAddrStr := "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E" //nolint:gosec
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
tokenBridgeAddrStr := "0x0290fb167208af455bb137780163b7b7a9a10c16" //nolint:gosec
tokenBridgeAddr, err := vaa.StringToAddress(tokenBridgeAddrStr)
require.NoError(t, err)
gov.setDayLengthInMinutes(24 * 60)
err = gov.setChainForTesting(vaa.ChainIDEthereum, tokenBridgeAddrStr, 1000000, 100000)
require.NoError(t, err)
err = gov.setTokenForTesting(vaa.ChainIDEthereum, tokenAddrStr, "WETH", 1774.62)
require.NoError(t, err)
// The first small transfer should be accepted.
msg1 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
50,
),
}
now, _ := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:00pm (CST)")
canPost, err := gov.ProcessMsgForTime(&msg1, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 1, numTrans)
assert.Equal(t, uint64(88730), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
// And so should the second.
msg2 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(2),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
50,
),
}
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 6:00pm (CST)")
canPost, err = gov.ProcessMsgForTime(&msg2, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(88730+88730), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 2, len(gov.msgsSeen))
// But the third big one should get enqueued.
msg3 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(3),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
100,
),
}
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 2:00am (CST)")
canPost, err = gov.ProcessMsgForTime(&msg3, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(88730+88730), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(177461), valuePending)
assert.Equal(t, 3, len(gov.msgsSeen))
// If we check pending before noon, nothing should happen.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 9:00am (CST)")
toBePublished, err := gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 0, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(88730+88730), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(177461), valuePending)
assert.Equal(t, 3, len(gov.msgsSeen))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(88730+88730), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(177461), valuePending)
// But just after noon, the first one should drop off. The big pending one should not be affected.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 12:01pm (CST)")
toBePublished, err = gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 0, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
require.NoError(t, err)
assert.Equal(t, 1, numTrans)
assert.Equal(t, uint64(88730), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(177461), valuePending)
assert.Equal(t, 2, len(gov.msgsSeen))
// And Just after 6pm, the second one should drop off. The big pending one should still not be affected.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 6:01pm (CST)")
toBePublished, err = gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 0, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
require.NoError(t, err)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(177461), valuePending)
// 23 hours after the big transaction is enqueued, it should still be there.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 3, 2022 at 1:01am (CST)")
toBePublished, err = gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 0, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
require.NoError(t, err)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(177461), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
// But then the operator resets the release time.
_, err = gov.resetReleaseTimerForTime(msg3.MessageIDString(), now)
require.NoError(t, err)
// So now, 12 hours later the big transaction is enqueued, it still won't get released.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 3, 2022 at 1:00pm (CST)")
toBePublished, err = gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 0, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
require.NoError(t, err)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(177461), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
// But finally, a full 24hrs, it should get released.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 4, 2022 at 1:01am (CST)")
toBePublished, err = gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 1, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
require.NoError(t, err)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 0, len(gov.msgsSeen))
// But the big transaction should not affect the daily notional.
ce, exists := gov.chains[vaa.ChainIDEthereum]
require.Equal(t, true, exists)
valueTrans = sumValue(ce.transfers, now)
assert.Equal(t, uint64(0), valueTrans)
}
func TestSmallTransactionsGetReleasedWhenTheTimerExpires(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
tokenAddrStr := "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E" //nolint:gosec
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
tokenBridgeAddrStr := "0x0290fb167208af455bb137780163b7b7a9a10c16" //nolint:gosec
tokenBridgeAddr, err := vaa.StringToAddress(tokenBridgeAddrStr)
require.NoError(t, err)
gov.setDayLengthInMinutes(24 * 60)
// This configuration does not make sense for real, but allows for this test.
// We are setting the big transfer size smaller than the daily limit, so we can
// easily enqueue a transfer that is not considered big and confirm that it eventually
// gets released after the release time passes.
err = gov.setChainForTesting(vaa.ChainIDEthereum, tokenBridgeAddrStr, 10000, 100000)
require.NoError(t, err)
err = gov.setTokenForTesting(vaa.ChainIDEthereum, tokenAddrStr, "WETH", 1774.62)
require.NoError(t, err)
// Submit a small transfer that will get enqueued due to the low daily limit.
msg1 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
50,
),
}
now, _ := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:00pm (CST)")
canPost, err := gov.ProcessMsgForTime(&msg1, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(88730), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
// If we check 23hrs later, nothing should happen.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 11:00am (CST)")
toBePublished, err := gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 0, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(88730), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
// But after 24hrs, it should get released.
now, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 12:01pm (CST)")
toBePublished, err = gov.CheckPendingForTime(now)
require.NoError(t, err)
assert.Equal(t, 1, len(toBePublished))
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 0, len(gov.msgsSeen))
}
func TestIsBigTransfer(t *testing.T) {
emitterAddr := vaa.Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}
bigTransactionSize := uint64(5_000_000)
ce := chainEntry{
emitterChainId: vaa.ChainIDEthereum,
emitterAddr: emitterAddr,
dailyLimit: uint64(50_000_000),
bigTransactionSize: bigTransactionSize,
checkForBigTransactions: bigTransactionSize != 0,
}
assert.Equal(t, false, ce.isBigTransfer(uint64(4_999_999)))
assert.Equal(t, true, ce.isBigTransfer(uint64(5_000_000)))
assert.Equal(t, true, ce.isBigTransfer(uint64(5_000_001)))
}
func TestTransferPayloadTooShort(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
tokenAddrStr := "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E" //nolint:gosec
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
tokenBridgeAddrStr := "0x0290fb167208af455bb137780163b7b7a9a10c16" //nolint:gosec
tokenBridgeAddr, err := vaa.StringToAddress(tokenBridgeAddrStr)
require.NoError(t, err)
gov.setDayLengthInMinutes(24 * 60)
err = gov.setChainForTesting(vaa.ChainIDEthereum, tokenBridgeAddrStr, 1000000, 0)
require.NoError(t, err)
err = gov.setTokenForTesting(vaa.ChainIDEthereum, tokenAddrStr, "WETH", 1774.62)
require.NoError(t, err)
payloadBytes1 := buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
1.25,
)
payloadBytes1 = payloadBytes1[0 : len(payloadBytes1)-1]
msg := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: payloadBytes1,
}
// The low level method should return an error.
_, err = gov.ProcessMsgForTime(&msg, time.Now())
assert.EqualError(t, err, "buffer too short")
assert.Equal(t, 0, len(gov.msgsSeen))
// The higher level method should return false, saying we should not publish.
canPost := gov.ProcessMsg(&msg)
assert.Equal(t, false, canPost)
assert.Equal(t, 0, len(gov.msgsSeen))
}
func TestDontReloadDuplicates(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
emitterAddrStr := "0x0290fb167208af455bb137780163b7b7a9a10c16" //nolint:gosec
emitterAddr, err := vaa.StringToAddress(emitterAddrStr)
require.NoError(t, err)
tokenAddrStr := "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E" //nolint:gosec
tokenAddr, err := vaa.StringToAddress(tokenAddrStr)
require.NoError(t, err)
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
require.NoError(t, err)
gov.setDayLengthInMinutes(24 * 60)
err = gov.setChainForTesting(vaa.ChainIDEthereum, emitterAddrStr, 1000000, 0)
require.NoError(t, err)
err = gov.setTokenForTesting(vaa.ChainIDEthereum, emitterAddrStr, "WETH", 1774.62)
require.NoError(t, err)
now, _ := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 2, 2022 at 12:01pm (CST)")
startTime := now.Add(-time.Minute * time.Duration(gov.dayLengthInMinutes))
var xfers []*db.Transfer
xfer1 := &db.Transfer{
Timestamp: startTime.Add(time.Minute * 5),
Value: uint64(1000),
OriginChain: vaa.ChainIDEthereum,
OriginAddress: tokenAddr,
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: emitterAddr,
MsgID: "2/" + emitterAddrStr + "/125",
Hash: "Hash1",
}
xfers = append(xfers, xfer1)
xfer2 := &db.Transfer{
Timestamp: startTime.Add(time.Minute * 5),
Value: uint64(2000),
OriginChain: vaa.ChainIDEthereum,
OriginAddress: tokenAddr,
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: emitterAddr,
MsgID: "2/" + emitterAddrStr + "/126",
Hash: "Hash2",
}
xfers = append(xfers, xfer2)
// Add a duplicate of each transfer
xfers = append(xfers, xfer1)
xfers = append(xfers, xfer2)
assert.Equal(t, 4, len(xfers))
payload1 := buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
1.25,
)
var pendings []*db.PendingTransfer
pending1 := &db.PendingTransfer{
ReleaseTime: now.Add(time.Hour * 24),
Msg: common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(200),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: emitterAddr,
ConsistencyLevel: uint8(32),
Payload: payload1,
},
}
pendings = append(pendings, pending1)
pending2 := &db.PendingTransfer{
ReleaseTime: now.Add(time.Hour * 24),
Msg: common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(201),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: emitterAddr,
ConsistencyLevel: uint8(32),
Payload: payload1,
},
}
pendings = append(pendings, pending2)
// Add a duplicate of each pending transfer
pendings = append(pendings, pending1)
pendings = append(pendings, pending2)
assert.Equal(t, 4, len(pendings))
for _, p := range xfers {
gov.reloadTransfer(p, now, startTime)
}
for _, p := range pendings {
gov.reloadPendingTransfer(p, now)
}
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(3000), valueTrans)
assert.Equal(t, 2, numPending)
assert.Equal(t, uint64(4436), valuePending)
}
func TestReobservationOfPublishedMsg(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
tokenAddrStr := "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E" //nolint:gosec
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
tokenBridgeAddrStr := "0x0290fb167208af455bb137780163b7b7a9a10c16" //nolint:gosec
tokenBridgeAddr, err := vaa.StringToAddress(tokenBridgeAddrStr)
require.NoError(t, err)
gov.setDayLengthInMinutes(24 * 60)
err = gov.setChainForTesting(vaa.ChainIDEthereum, tokenBridgeAddrStr, 1000000, 100000)
require.NoError(t, err)
err = gov.setTokenForTesting(vaa.ChainIDEthereum, tokenAddrStr, "WETH", 1774.62)
require.NoError(t, err)
// The first transfer should be accepted.
msg := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
50,
),
}
now, _ := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:10pm (CST)")
canPost, err := gov.ProcessMsgForTime(&msg, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 1, numTrans)
assert.Equal(t, uint64(88730), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
// A reobservation of the same message should get published but should not affect the notional value.
canPost, err = gov.ProcessMsgForTime(&msg, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 1, numTrans)
assert.Equal(t, uint64(88730), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
}
func TestReobservationOfEnqueued(t *testing.T) {
// The duplicate should not get published and not get enqueued again.
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
tokenAddrStr := "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E" //nolint:gosec
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
tokenBridgeAddrStr := "0x0290fb167208af455bb137780163b7b7a9a10c16" //nolint:gosec
tokenBridgeAddr, err := vaa.StringToAddress(tokenBridgeAddrStr)
require.NoError(t, err)
gov.setDayLengthInMinutes(24 * 60)
err = gov.setChainForTesting(vaa.ChainIDEthereum, tokenBridgeAddrStr, 1000000, 100000)
require.NoError(t, err)
err = gov.setTokenForTesting(vaa.ChainIDEthereum, tokenAddrStr, "WETH", 1774.62)
require.NoError(t, err)
// A big transfer should get enqueued.
msg := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
5000,
),
}
now, _ := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:10pm (CST)")
canPost, err := gov.ProcessMsgForTime(&msg, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(8_873_099), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
// A reobservation of the same message should not get published and should not get enqueued again.
canPost, err = gov.ProcessMsgForTime(&msg, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, false, canPost)
assert.Equal(t, 0, numTrans)
assert.Equal(t, uint64(0), valueTrans)
assert.Equal(t, 1, numPending)
assert.Equal(t, uint64(8_873_099), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
}
func TestReusedMsgIdWithDifferentPayloadGetsProcessed(t *testing.T) {
ctx := context.Background()
gov, err := newChainGovernorForTest(ctx)
require.NoError(t, err)
assert.NotNil(t, gov)
tokenAddrStr := "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E" //nolint:gosec
toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8"
tokenBridgeAddrStr := "0x0290fb167208af455bb137780163b7b7a9a10c16" //nolint:gosec
tokenBridgeAddr, err := vaa.StringToAddress(tokenBridgeAddrStr)
require.NoError(t, err)
gov.setDayLengthInMinutes(24 * 60)
err = gov.setChainForTesting(vaa.ChainIDEthereum, tokenBridgeAddrStr, 1000000, 100000)
require.NoError(t, err)
err = gov.setTokenForTesting(vaa.ChainIDEthereum, tokenAddrStr, "WETH", 1774.62)
require.NoError(t, err)
// The first transfer should be accepted.
msg1 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
50,
),
}
now, _ := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jun 1, 2022 at 12:10pm (CST)")
canPost, err := gov.ProcessMsgForTime(&msg1, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending := gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 1, numTrans)
assert.Equal(t, uint64(88730), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 1, len(gov.msgsSeen))
// A second message with the same msgId but a different payload should also get published and apply to the notional value.
msg2 := common.MessagePublication{
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
Timestamp: time.Unix(int64(1654543099), 0),
Nonce: uint32(1),
Sequence: uint64(1),
EmitterChain: vaa.ChainIDEthereum,
EmitterAddress: tokenBridgeAddr,
ConsistencyLevel: uint8(32),
Payload: buildMockTransferPayloadBytes(1,
vaa.ChainIDEthereum,
tokenAddrStr,
vaa.ChainIDPolygon,
toAddrStr,
5,
),
}
canPost, err = gov.ProcessMsgForTime(&msg2, now)
require.NoError(t, err)
numTrans, valueTrans, numPending, valuePending = gov.getStatsForAllChains()
assert.Equal(t, true, canPost)
assert.Equal(t, 2, numTrans)
assert.Equal(t, uint64(97603), valueTrans)
assert.Equal(t, 0, numPending)
assert.Equal(t, uint64(0), valuePending)
assert.Equal(t, 2, len(gov.msgsSeen))
}