Merge 35fd22c0a3
into d44451d7b5
This commit is contained in:
commit
ab9b884ee6
|
@ -0,0 +1,57 @@
|
|||
package governor
|
||||
|
||||
// FlowCancelTokenList Returns a list of `tokenConfigEntry`s representing tokens that can 'Flow Cancel'. This means that incoming transfers
|
||||
// that use these tokens can reduce the 'daily limit' of the Governor configured for the destination chain.
|
||||
// The list of tokens was generated by grepping the file `generated_mainnet_tokens.go` for "USDC", "USDT", and "DAI".
|
||||
//
|
||||
// Note that the field `symbol` is unused. It is retained in this file only for convenience.
|
||||
func FlowCancelTokenList() []tokenConfigEntry {
|
||||
return []tokenConfigEntry{
|
||||
// USDC variants
|
||||
{chain: 2, addr: "000000000000000000000000bcca60bb61934080951369a648fb03df4f96263c", symbol: "aUSDC"},
|
||||
{chain: 4, addr: "0000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3", symbol: "axlUSDC"},
|
||||
{chain: 5, addr: "0000000000000000000000001a13f4ca1d028320a707d99520abfefca3998b7f", symbol: "amUSDC"},
|
||||
{chain: 5, addr: "000000000000000000000000750e4c4984a9e0f12978ea6742bc1c5d248f40ed", symbol: "axlUSDC"},
|
||||
{chain: 6, addr: "000000000000000000000000fab550568c688d5d8a52c7d794cb93edc26ec0ec", symbol: "axlUSDC"},
|
||||
{chain: 6, addr: "000000000000000000000000a7d7079b0fead91f3e65f86e8915cb59c1a4c664", symbol: "USDC.e"},
|
||||
{chain: 10, addr: "0000000000000000000000001b6382dbdea11d97f24495c9a90b7c88469134a4", symbol: "axlUSDC"},
|
||||
{chain: 10, addr: "00000000000000000000000028a92dde19d9989f39a49905d7c9c2fac7799bdf", symbol: "USDC"},
|
||||
{chain: 10, addr: "00000000000000000000000027e611fd27b276acbd5ffd632e5eaebec9761e40", symbol: "DAI+USDC"},
|
||||
{chain: 13, addr: "000000000000000000000000754288077d0ff82af7a5317c7cb8c444d421d103", symbol: "oUSDC"},
|
||||
{chain: 14, addr: "000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215", symbol: "axlUSDC"},
|
||||
{chain: 16, addr: "000000000000000000000000ca01a1d0993565291051daff390892518acfad3a", symbol: "axlUSDC"},
|
||||
{chain: 23, addr: "000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd", symbol: "aArbUSDC"},
|
||||
{chain: 24, addr: "000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd", symbol: "aOptUSDC"},
|
||||
{chain: 30, addr: "000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215", symbol: "axlUSDC"},
|
||||
|
||||
// USDT variants
|
||||
{chain: 1, addr: "b7db4e83eb727f1187bd7a50303f5b4e4e943503da8571ad6564a51131504792", symbol: ""},
|
||||
{chain: 1, addr: "ce010e60afedb22717bd63192f54145a3f965a33bb82d2c7029eb2ce1e208264", symbol: "USDT"},
|
||||
{chain: 2, addr: "000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7", symbol: "USDT"},
|
||||
{chain: 4, addr: "00000000000000000000000055d398326f99059ff775485246999027b3197955", symbol: "USDT"},
|
||||
{chain: 5, addr: "000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f", symbol: "USDT"},
|
||||
{chain: 6, addr: "0000000000000000000000009702230a8ea53601f5cd2dc00fdbc13d4df4a8c7", symbol: "USDt"},
|
||||
{chain: 6, addr: "000000000000000000000000c7198437980c041c805a1edcba50c1ce5db95118", symbol: "USDT.e"},
|
||||
{chain: 8, addr: "000000000000000000000000000000000000000000000000000000000004c5c1", symbol: "USDt"},
|
||||
{chain: 9, addr: "0000000000000000000000004988a896b1227218e4a686fde5eabdcabd91571f", symbol: "USDT"},
|
||||
{chain: 10, addr: "000000000000000000000000cc1b99ddac1a33c201a742a1851662e87bc7f22c", symbol: "USDT"},
|
||||
{chain: 10, addr: "000000000000000000000000049d68029688eabf473097a2fc38ef61633a3c7a", symbol: "fUSDT"},
|
||||
{chain: 13, addr: "000000000000000000000000cee8faf64bb97a73bb51e115aa89c17ffa8dd167", symbol: "oUSDT"},
|
||||
{chain: 16, addr: "000000000000000000000000efaeee334f0fd1712f9a8cc375f427d9cdd40d73", symbol: "USDT"},
|
||||
{chain: 16, addr: "000000000000000000000000ffffffffea09fb06d082fd1275cd48b191cbcd1d", symbol: "xcUSDT"},
|
||||
{chain: 23, addr: "000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", symbol: "USDT"},
|
||||
{chain: 24, addr: "00000000000000000000000094b008aa00579c1307b0ef2c499ad98a8ce58e58", symbol: "USDT"},
|
||||
|
||||
// DAI variants (DAI+USDC is included under the USDC list above)
|
||||
{chain: 2, addr: "0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", symbol: "DAI"},
|
||||
{chain: 4, addr: "0000000000000000000000001af3f329e8be154074d8769d1ffa4ee058b1dbc3", symbol: "DAI"},
|
||||
{chain: 5, addr: "0000000000000000000000008f3cf7ad23cd3cadbd9735aff958023239c6a063", symbol: "DAI"},
|
||||
{chain: 6, addr: "000000000000000000000000d586e7f844cea2f87f50152665bcbc2c279d8d70", symbol: "DAI.e"},
|
||||
{chain: 10, addr: "0000000000000000000000008d11ec38a3eb5e956b052f67da8bdc9bef8abf3e", symbol: "DAI"},
|
||||
{chain: 13, addr: "0000000000000000000000005c74070fdea071359b86082bd9f9b3deaafbe32b", symbol: "KDAI"},
|
||||
{chain: 16, addr: "000000000000000000000000765277eebeca2e31912c9946eae1021199b39c61", symbol: "DAI"},
|
||||
{chain: 23, addr: "000000000000000000000000da10009cbd5d07dd0cecc66161fc93d7c9000da1", symbol: "DAI"},
|
||||
{chain: 24, addr: "000000000000000000000000da10009cbd5d07dd0cecc66161fc93d7c9000da1", symbol: "DAI"},
|
||||
{chain: 30, addr: "00000000000000000000000050c5725949a6f0c72e6c4a641f24049a917db0cb", symbol: "DAI"},
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
package governor
|
||||
|
||||
// The purpose of the Chain Governor is to limit the notional TVL that can leave a chain in a single day.
|
||||
// It works by tracking transfers (types one and three) for a configured set of tokens from a configured set of emitters (chains).
|
||||
//
|
||||
|
@ -23,11 +25,10 @@
|
|||
//
|
||||
// To enable the chain governor, you must specified the --chainGovernorEnabled guardiand command line argument.
|
||||
|
||||
package governor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
|
@ -204,7 +205,14 @@ func (gov *ChainGovernor) initConfig() error {
|
|||
}
|
||||
|
||||
key := tokenKey{chain: vaa.ChainID(ct.chain), addr: addr}
|
||||
te := &tokenEntry{cfgPrice: cfgPrice, price: initialPrice, decimals: decimals, symbol: symbol, coinGeckoId: ct.coinGeckoId, token: key}
|
||||
te := &tokenEntry{
|
||||
cfgPrice: cfgPrice,
|
||||
price: initialPrice,
|
||||
decimals: decimals,
|
||||
symbol: symbol,
|
||||
coinGeckoId: ct.coinGeckoId,
|
||||
token: key,
|
||||
}
|
||||
te.updatePrice()
|
||||
|
||||
gov.tokens[key] = te
|
||||
|
@ -294,6 +302,10 @@ func (gov *ChainGovernor) ProcessMsg(msg *common.MessagePublication) bool {
|
|||
}
|
||||
|
||||
func (gov *ChainGovernor) ProcessMsgForTime(msg *common.MessagePublication, now time.Time) (bool, error) {
|
||||
// Validation:
|
||||
// - ensure MessagePublication is not nil
|
||||
// - check that the MessagePublication is governed
|
||||
// - check that the message is not a duplicate
|
||||
if msg == nil {
|
||||
return false, fmt.Errorf("msg is nil")
|
||||
}
|
||||
|
@ -301,7 +313,7 @@ func (gov *ChainGovernor) ProcessMsgForTime(msg *common.MessagePublication, now
|
|||
gov.mutex.Lock()
|
||||
defer gov.mutex.Unlock()
|
||||
|
||||
msgIsGoverned, ce, token, payload, err := gov.parseMsgAlreadyLocked(msg)
|
||||
msgIsGoverned, emitterChainEntry, token, payload, err := gov.parseMsgAlreadyLocked(msg)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -330,10 +342,11 @@ func (gov *ChainGovernor) ProcessMsgForTime(msg *common.MessagePublication, now
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// Get all outgoing transfers for `emitterChainEntry` that happened within the last 24 hours
|
||||
startTime := now.Add(-time.Minute * time.Duration(gov.dayLengthInMinutes))
|
||||
prevTotalValue, err := gov.TrimAndSumValueForChain(ce, startTime)
|
||||
prevTotalValue, err := gov.TrimAndSumValueForChain(emitterChainEntry, startTime)
|
||||
if err != nil {
|
||||
gov.logger.Error("failed to trim transfers",
|
||||
gov.logger.Error("Error when attempting to trim and sum transfers",
|
||||
zap.String("msgID", msg.MessageIDString()),
|
||||
zap.String("hash", hash),
|
||||
zap.Stringer("txHash", msg.TxHash),
|
||||
|
@ -342,6 +355,7 @@ func (gov *ChainGovernor) ProcessMsgForTime(msg *common.MessagePublication, now
|
|||
return false, err
|
||||
}
|
||||
|
||||
// Compute the notional USD value of the transfers
|
||||
value, err := computeValue(payload.Amount, token)
|
||||
if err != nil {
|
||||
gov.logger.Error("failed to compute value of transfer",
|
||||
|
@ -367,7 +381,7 @@ func (gov *ChainGovernor) ProcessMsgForTime(msg *common.MessagePublication, now
|
|||
|
||||
enqueueIt := false
|
||||
var releaseTime time.Time
|
||||
if ce.isBigTransfer(value) {
|
||||
if emitterChainEntry.isBigTransfer(value) {
|
||||
enqueueIt = true
|
||||
releaseTime = now.Add(maxEnqueuedTime)
|
||||
gov.logger.Error("enqueuing vaa because it is a big transaction",
|
||||
|
@ -376,11 +390,11 @@ func (gov *ChainGovernor) ProcessMsgForTime(msg *common.MessagePublication, now
|
|||
zap.Uint64("newTotalValue", newTotalValue),
|
||||
zap.String("msgID", msg.MessageIDString()),
|
||||
zap.Stringer("releaseTime", releaseTime),
|
||||
zap.Uint64("bigTransactionSize", ce.bigTransactionSize),
|
||||
zap.Uint64("bigTransactionSize", emitterChainEntry.bigTransactionSize),
|
||||
zap.String("hash", hash),
|
||||
zap.Stringer("txHash", msg.TxHash),
|
||||
)
|
||||
} else if newTotalValue > ce.dailyLimit {
|
||||
} else if newTotalValue > emitterChainEntry.dailyLimit {
|
||||
enqueueIt = true
|
||||
releaseTime = now.Add(maxEnqueuedTime)
|
||||
gov.logger.Error("enqueuing vaa because it would exceed the daily limit",
|
||||
|
@ -407,7 +421,10 @@ func (gov *ChainGovernor) ProcessMsgForTime(msg *common.MessagePublication, now
|
|||
return false, err
|
||||
}
|
||||
|
||||
ce.pending = append(ce.pending, &pendingEntry{token: token, amount: payload.Amount, hash: hash, dbData: dbData})
|
||||
emitterChainEntry.pending = append(
|
||||
emitterChainEntry.pending,
|
||||
&pendingEntry{token: token, amount: payload.Amount, hash: hash, dbData: dbData},
|
||||
)
|
||||
gov.msgsSeen[hash] = transferEnqueued
|
||||
return false, nil
|
||||
}
|
||||
|
@ -421,7 +438,8 @@ func (gov *ChainGovernor) ProcessMsgForTime(msg *common.MessagePublication, now
|
|||
zap.Stringer("txHash", msg.TxHash),
|
||||
)
|
||||
|
||||
xfer := db.Transfer{Timestamp: now,
|
||||
xfer := db.Transfer{
|
||||
Timestamp: now,
|
||||
Value: value,
|
||||
OriginChain: token.token.chain,
|
||||
OriginAddress: token.token.addr,
|
||||
|
@ -442,7 +460,7 @@ func (gov *ChainGovernor) ProcessMsgForTime(msg *common.MessagePublication, now
|
|||
return false, err
|
||||
}
|
||||
|
||||
ce.transfers = append(ce.transfers, &xfer)
|
||||
emitterChainEntry.transfers = append(emitterChainEntry.transfers, &xfer)
|
||||
gov.msgsSeen[hash] = transferComplete
|
||||
return true, nil
|
||||
}
|
||||
|
@ -456,19 +474,27 @@ func (gov *ChainGovernor) IsGovernedMsg(msg *common.MessagePublication) (msgIsGo
|
|||
}
|
||||
|
||||
// parseMsgAlreadyLocked determines if the message applies to the governor and also returns data useful to the governor. It assumes the caller holds the lock.
|
||||
func (gov *ChainGovernor) parseMsgAlreadyLocked(msg *common.MessagePublication) (bool, *chainEntry, *tokenEntry, *vaa.TransferPayloadHdr, error) {
|
||||
func (gov *ChainGovernor) parseMsgAlreadyLocked(
|
||||
msg *common.MessagePublication,
|
||||
) (bool, *chainEntry, *tokenEntry, *vaa.TransferPayloadHdr, error) {
|
||||
// If we don't care about this chain, the VAA can be published.
|
||||
ce, exists := gov.chains[msg.EmitterChain]
|
||||
if !exists {
|
||||
if msg.EmitterChain != vaa.ChainIDPythNet {
|
||||
gov.logger.Info("ignoring vaa because the emitter chain is not configured", zap.String("msgID", msg.MessageIDString()))
|
||||
gov.logger.Info(
|
||||
"ignoring vaa because the emitter chain is not configured",
|
||||
zap.String("msgID", msg.MessageIDString()),
|
||||
)
|
||||
}
|
||||
return false, nil, nil, nil, nil
|
||||
}
|
||||
|
||||
// If we don't care about this emitter, the VAA can be published.
|
||||
if msg.EmitterAddress != ce.emitterAddr {
|
||||
gov.logger.Info("ignoring vaa because the emitter address is not configured", zap.String("msgID", msg.MessageIDString()))
|
||||
gov.logger.Info(
|
||||
"ignoring vaa because the emitter address is not configured",
|
||||
zap.String("msgID", msg.MessageIDString()),
|
||||
)
|
||||
return false, nil, nil, nil, nil
|
||||
}
|
||||
|
||||
|
@ -519,7 +545,7 @@ func (gov *ChainGovernor) CheckPendingForTime(now time.Time) ([]*common.MessageP
|
|||
foundOne := false
|
||||
prevTotalValue, err := gov.TrimAndSumValueForChain(ce, startTime)
|
||||
if err != nil {
|
||||
gov.logger.Error("failed to trim transfers", zap.Error(err))
|
||||
gov.logger.Error("Error when attempting to trim and sum transfers", zap.Error(err))
|
||||
gov.msgsToPublish = msgsToPublish
|
||||
return nil, err
|
||||
}
|
||||
|
@ -656,11 +682,110 @@ func computeValue(amount *big.Int, token *tokenEntry) (uint64, error) {
|
|||
return value, nil
|
||||
}
|
||||
|
||||
func (gov *ChainGovernor) TrimAndSumValueForChain(ce *chainEntry, startTime time.Time) (sum uint64, err error) {
|
||||
sum, ce.transfers, err = gov.TrimAndSumValue(ce.transfers, startTime)
|
||||
return sum, err
|
||||
// TrimAndSumValueForChain calculates the `sum` of `Transfer`s for a given chain `emitter`. In effect, it represents a
|
||||
// chain's "Governor Usage" for a given 24 hour period.
|
||||
// This sum may be reduced by the sum of 'flow cancelling' transfers: that is, transfers of an allow-listed token
|
||||
// that have the `emitter` as their destination chain.
|
||||
// The resulting `sum` return value therefore represents the net flow across a chain when taking flow-cancelling tokens
|
||||
// into account. Therefore, this value should never be less than 0 and should never exceed the "Governor limit" for the chain.
|
||||
//
|
||||
// As a side-effect, this function modifies the parameter `emitter`, upating its `transfers` field so that it only includes
|
||||
// filtered `Transfer`s (i.e. outgoing `Transfer`s newer than `startTime`).
|
||||
//
|
||||
// SECURITY Invariant: The `sum` return value should never be less than 0
|
||||
// SECURITY Invariant: The `sum` return value should never exceed the "Governor limit" for the chain
|
||||
func (gov *ChainGovernor) TrimAndSumValueForChain(emitter *chainEntry, startTime time.Time) (sum uint64, err error) {
|
||||
// Sum the value of all outgoing transfers
|
||||
var sumOutgoing uint64
|
||||
sumOutgoing, emitter.transfers, err = gov.TrimAndSumValue(emitter.transfers, startTime)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Subtract the sum of all flow cancelling transfers. Here the emitter's chainID is actually used as the destination
|
||||
// chain in in the context of FlowCancellingTransfersForChain.
|
||||
flowCancelSum, err := gov.SumTransferValues(gov.FlowCancellingTransfersForChain(emitter.emitterChainId, startTime))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// If flowCancelSum is larger than or equal to sumOutgoing, simply return 0 as this means that all outgoing
|
||||
// transfers have been cancelled by incoming transfers.
|
||||
// This also avoids integer underflow.
|
||||
if flowCancelSum >= sumOutgoing {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
sum = sumOutgoing - flowCancelSum
|
||||
if sum > emitter.dailyLimit {
|
||||
return 0, fmt.Errorf(
|
||||
"invariant violation: calculated sum %d exceeds Governor limit %d",
|
||||
sum,
|
||||
emitter.dailyLimit,
|
||||
)
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
// SumTransferValues iterates over a slice of transfers and sums their value.
|
||||
func (gov *ChainGovernor) SumTransferValues(transfers []*db.Transfer) (sum uint64, err error) {
|
||||
sum = 0
|
||||
// Iterate over all transfers usin tokens that have flow cancelling enabled.
|
||||
// If the destination chain of the transfer is equal to the `ce` parameter, add the value
|
||||
// of the transfer to the flow cancelling sum.
|
||||
for _, transfer := range transfers {
|
||||
// Overflow check. Note that transfer.Value cannot be negative
|
||||
if (sum + transfer.Value) < sum {
|
||||
return 0, errors.New("overflow when calculating flow cancelling sum")
|
||||
}
|
||||
sum += transfer.Value
|
||||
|
||||
}
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
// FlowCancellingTransfersForChain builds a list of Transfers that contain assets that can 'flow cancel'
|
||||
// (reduce the Governor usage of) a governed chain represented by `destinationChainID`.
|
||||
func (gov *ChainGovernor) FlowCancellingTransfersForChain(
|
||||
destinationChainID vaa.ChainID,
|
||||
startTime time.Time,
|
||||
) (transfers []*db.Transfer) {
|
||||
flowCancelTokens := FlowCancelTokenList()
|
||||
// transfers = make([]*db.Transfer, 0)
|
||||
// Iterate over all transfers for all governed chains
|
||||
for _, emitterChainEntry := range gov.chains {
|
||||
for _, transfer := range emitterChainEntry.transfers {
|
||||
// We care about the transfer if:
|
||||
// - Its target chain is equal to the `destinationChainID` parameter
|
||||
// - It happened after `startTime`
|
||||
// - It is a flow cancelling token (The transfer's origin chain and
|
||||
// origin address match a hard-coded flow cancelling asset)
|
||||
if transfer.TargetChain != destinationChainID {
|
||||
continue
|
||||
}
|
||||
|
||||
if transfer.Timestamp.Before(startTime) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, flowCancelToken := range flowCancelTokens {
|
||||
// Compare flow cancel fields with transfer fields. This requires conversions:
|
||||
// - vaa.ChainID to uint16
|
||||
// - vaa.Address to String
|
||||
if uint16(transfer.OriginChain) == flowCancelToken.chain &&
|
||||
transfer.OriginAddress.String() == flowCancelToken.addr {
|
||||
transfers = append(transfers, transfer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return transfers
|
||||
}
|
||||
|
||||
// TrimAndSumValue iterates over a slice of db.Transfer structs. It filters out transfers that have a Timestamp value that
|
||||
// is earlier than the parameter `startTime`. The function then iterates over the remaining transfers, sums their Value,
|
||||
// and returns the sum and the filtered transfers.
|
||||
func (gov *ChainGovernor) TrimAndSumValue(transfers []*db.Transfer, startTime time.Time) (uint64, []*db.Transfer, error) {
|
||||
if len(transfers) == 0 {
|
||||
return 0, transfers, nil
|
||||
|
|
|
@ -49,7 +49,12 @@ func (gov *ChainGovernor) setDayLengthInMinutes(min int) {
|
|||
gov.dayLengthInMinutes = min
|
||||
}
|
||||
|
||||
func (gov *ChainGovernor) setChainForTesting(emitterChainId vaa.ChainID, emitterAddrStr string, dailyLimit uint64, bigTransactionSize uint64) error {
|
||||
func (gov *ChainGovernor) setChainForTesting(
|
||||
emitterChainId vaa.ChainID,
|
||||
emitterAddrStr string,
|
||||
dailyLimit uint64,
|
||||
bigTransactionSize uint64,
|
||||
) error {
|
||||
gov.mutex.Lock()
|
||||
defer gov.mutex.Unlock()
|
||||
|
||||
|
@ -70,7 +75,12 @@ func (gov *ChainGovernor) setChainForTesting(emitterChainId vaa.ChainID, emitter
|
|||
return nil
|
||||
}
|
||||
|
||||
func (gov *ChainGovernor) setTokenForTesting(tokenChainID vaa.ChainID, tokenAddrStr string, symbol string, price float64) error {
|
||||
func (gov *ChainGovernor) setTokenForTesting(
|
||||
tokenChainID vaa.ChainID,
|
||||
tokenAddrStr string,
|
||||
symbol string,
|
||||
price float64,
|
||||
) error {
|
||||
gov.mutex.Lock()
|
||||
defer gov.mutex.Unlock()
|
||||
|
||||
|
@ -163,6 +173,214 @@ func TestSumAllFromToday(t *testing.T) {
|
|||
assert.Equal(t, 1, len(updatedTransfers))
|
||||
}
|
||||
|
||||
func TestSumWithFlowCancelling(t *testing.T) {
|
||||
// NOTE: Replace this Chain:Address pair if the Flow Cancel Token List is modified
|
||||
var originChain vaa.ChainID = 2
|
||||
var originAddress vaa.Address
|
||||
originAddress, err := vaa.StringToAddress("000000000000000000000000bcca60bb61934080951369a648fb03df4f96263c")
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
gov, err := newChainGovernorForTest(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, gov)
|
||||
|
||||
now, err := time.Parse("2006-Jan-02", "2024-Feb-19")
|
||||
require.NoError(t, err)
|
||||
|
||||
var transfers_from_emitter []*db.Transfer
|
||||
var transfers_that_flow_cancel []*db.Transfer
|
||||
transferTime, err := time.Parse("2006-Jan-02", "2024-Feb-19")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set up values and governor limit
|
||||
emitterTransferValue := uint64(125000)
|
||||
flowCancelValue := uint64(100000)
|
||||
|
||||
emitterLimit := emitterTransferValue * 2 // make sure the limit always exceeds the transfer value
|
||||
emitterChainId := 1
|
||||
|
||||
// Setup transfers
|
||||
// - Transfer from emitter: we only care about Value
|
||||
// - Transfer that flow cancels: Transfer must be a valid entry from FlowCancelTokenList() (based on origin chain and origin address)
|
||||
// and the desintation chain must be the same as the emitter chain
|
||||
transfers_from_emitter = append(transfers_from_emitter, &db.Transfer{Value: emitterTransferValue, Timestamp: transferTime})
|
||||
transfers_that_flow_cancel = append(
|
||||
transfers_that_flow_cancel,
|
||||
&db.Transfer{
|
||||
OriginChain: originChain,
|
||||
OriginAddress: originAddress,
|
||||
TargetChain: vaa.ChainID(emitterChainId),
|
||||
Value: flowCancelValue,
|
||||
Timestamp: transferTime,
|
||||
},
|
||||
)
|
||||
|
||||
// Populate chainEntrys and ChainGovernor
|
||||
emitter := &chainEntry{
|
||||
transfers: transfers_from_emitter,
|
||||
emitterChainId: vaa.ChainID(emitterChainId),
|
||||
dailyLimit: emitterLimit,
|
||||
}
|
||||
chain_with_flow_cancel_transfers := &chainEntry{transfers: transfers_that_flow_cancel, emitterChainId: 2}
|
||||
gov.chains[emitter.emitterChainId] = emitter
|
||||
gov.chains[chain_with_flow_cancel_transfers.emitterChainId] = chain_with_flow_cancel_transfers
|
||||
|
||||
// XXX: sanity check
|
||||
expectedNumTransfers := 1
|
||||
sum, transfers, err := gov.TrimAndSumValue(emitter.transfers, now)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedNumTransfers, len(transfers))
|
||||
assert.NotZero(t, sum)
|
||||
|
||||
// Calculate Governor Usage for emitter, including flow cancelling
|
||||
sum, err = gov.TrimAndSumValueForChain(emitter, now.Add(-time.Hour*24))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, emitterTransferValue-flowCancelValue, sum)
|
||||
}
|
||||
|
||||
// Flow cancelling transfers are subtracted from the overall sum of all transfers from a given
|
||||
// emitter chain. Since we are working with uint64 values, ensure that there is no underflow.
|
||||
// When the sum of all flow cancelling transfers is greater than emitted transfers for a chain,
|
||||
// the expected result is that the resulting Governor Usage equals 0 (and not a negative number
|
||||
// or a very large underflow result).
|
||||
// Also, the function should not return an error in this case.
|
||||
func TestFlowCancelCannotUnderflow(t *testing.T) {
|
||||
// NOTE: Replace this Chain:Address pair if the Flow Cancel Token List is modified
|
||||
var originChain vaa.ChainID = 2
|
||||
var originAddress vaa.Address
|
||||
originAddress, err := vaa.StringToAddress("000000000000000000000000bcca60bb61934080951369a648fb03df4f96263c")
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
gov, err := newChainGovernorForTest(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, gov)
|
||||
|
||||
now, err := time.Parse("2006-Jan-02", "2024-Feb-19")
|
||||
require.NoError(t, err)
|
||||
|
||||
var transfers_from_emitter []*db.Transfer
|
||||
var transfers_that_flow_cancel []*db.Transfer
|
||||
transferTime, err := time.Parse("2006-Jan-02", "2024-Feb-19")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set up values and governor limit
|
||||
emitterTransferValue := uint64(100000)
|
||||
flowCancelValue := emitterTransferValue + 25000 // make sure this value is higher than `emitterTransferValue`
|
||||
|
||||
emitterLimit := emitterTransferValue * 2 // make sure the limit always exceeds the transfer value
|
||||
emitterChainId := 1
|
||||
|
||||
// Setup transfers
|
||||
// - Transfer from emitter: we only care about Value
|
||||
// - Transfer that flow cancels: Transfer must be a valid entry from FlowCancelTokenList() (based on origin chain and origin address)
|
||||
// and the destination chain must be the same as the emitter chain
|
||||
transfers_from_emitter = append(transfers_from_emitter, &db.Transfer{Value: emitterTransferValue, Timestamp: transferTime})
|
||||
transfers_that_flow_cancel = append(
|
||||
transfers_that_flow_cancel,
|
||||
&db.Transfer{
|
||||
OriginChain: originChain,
|
||||
OriginAddress: originAddress,
|
||||
TargetChain: vaa.ChainID(emitterChainId),
|
||||
Value: flowCancelValue,
|
||||
Timestamp: transferTime,
|
||||
},
|
||||
)
|
||||
|
||||
// Populate chainEntrys and ChainGovernor
|
||||
emitter := &chainEntry{
|
||||
transfers: transfers_from_emitter,
|
||||
emitterChainId: vaa.ChainID(emitterChainId),
|
||||
dailyLimit: emitterLimit,
|
||||
}
|
||||
chain_with_flow_cancel_transfers := &chainEntry{transfers: transfers_that_flow_cancel, emitterChainId: 2}
|
||||
gov.chains[emitter.emitterChainId] = emitter
|
||||
gov.chains[chain_with_flow_cancel_transfers.emitterChainId] = chain_with_flow_cancel_transfers
|
||||
|
||||
// XXX: sanity check: Sum of transfers without flow cancelling should be positive.
|
||||
expectedNumTransfers := 1
|
||||
sum, transfers, err := gov.TrimAndSumValue(emitter.transfers, now)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedNumTransfers, len(transfers))
|
||||
assert.Greater(t, sum, uint64(0))
|
||||
|
||||
// Calculate Governor Usage for emitter, including flow cancelling
|
||||
sum, err = gov.TrimAndSumValueForChain(emitter, now.Add(-time.Hour*24))
|
||||
require.NoError(t, err)
|
||||
assert.Zero(t, sum)
|
||||
}
|
||||
|
||||
// Simulate a case where the total sum of transfers for a chain in a 24 hour period exceeds
|
||||
// the configured Governor limit. This should never happen, so we make sure that an error
|
||||
// is returned if the system is in this state
|
||||
func TestInvariantGovernorLimit(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
gov, err := newChainGovernorForTest(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, gov)
|
||||
|
||||
now, err := time.Parse("2006-Jan-02", "2024-Feb-19")
|
||||
require.NoError(t, err)
|
||||
|
||||
var transfers_from_emitter []*db.Transfer
|
||||
transferTime, err := time.Parse("2006-Jan-02", "2024-Feb-19")
|
||||
require.NoError(t, err)
|
||||
|
||||
emitterTransferValue := uint64(125000)
|
||||
|
||||
emitterLimit := emitterTransferValue * 20
|
||||
emitterChainId := 1
|
||||
|
||||
// Create a lot of transfers. Their total value should exceed `emitterLimit`
|
||||
for i := 0; i < 25; i++ {
|
||||
transfers_from_emitter = append(
|
||||
transfers_from_emitter,
|
||||
&db.Transfer{Value: emitterTransferValue, Timestamp: transferTime},
|
||||
)
|
||||
}
|
||||
|
||||
// Populate chainEntry and ChainGovernor
|
||||
emitter := &chainEntry{
|
||||
transfers: transfers_from_emitter,
|
||||
emitterChainId: vaa.ChainID(emitterChainId),
|
||||
dailyLimit: emitterLimit,
|
||||
}
|
||||
gov.chains[emitter.emitterChainId] = emitter
|
||||
|
||||
// XXX: sanity check
|
||||
expectedNumTransfers := 25
|
||||
sum, transfers, err := gov.TrimAndSumValue(emitter.transfers, now)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedNumTransfers, len(transfers))
|
||||
assert.NotZero(t, sum)
|
||||
|
||||
// Make sure we trigger the Invariant
|
||||
sum, err = gov.TrimAndSumValueForChain(emitter, now.Add(-time.Hour*24))
|
||||
require.ErrorContains(t, err, "invariant violation: calculated sum")
|
||||
assert.Zero(t, sum)
|
||||
}
|
||||
|
||||
func TestInvariantSumOverflow(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
gov, err := newChainGovernorForTest(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, gov)
|
||||
|
||||
transferTime, err := time.Parse("2006-Jan-02", "2024-Feb-19")
|
||||
require.NoError(t, err)
|
||||
|
||||
var transfers []*db.Transfer
|
||||
|
||||
// Add two transfers. When summed, they should trigger an overflow
|
||||
transfers = append(transfers, &db.Transfer{Value: math.MaxUint64, Timestamp: transferTime})
|
||||
transfers = append(transfers, &db.Transfer{Value: 1, Timestamp: transferTime})
|
||||
|
||||
sum, err := gov.SumTransferValues(transfers)
|
||||
require.ErrorContains(t, err, "overflow when calculating flow cancelling sum")
|
||||
assert.Zero(t, sum)
|
||||
}
|
||||
|
||||
func TestTrimOneOfTwoTransfers(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
gov, err := newChainGovernorForTest(ctx)
|
||||
|
@ -316,7 +534,7 @@ func TestVaaForUninterestingEmitterChain(t *testing.T) {
|
|||
assert.NotNil(t, gov)
|
||||
|
||||
emitterAddr, _ := vaa.StringToAddress("0x00")
|
||||
var payload = []byte{1, 97, 97, 97, 97, 97}
|
||||
payload := []byte{1, 97, 97, 97, 97, 97}
|
||||
|
||||
msg := common.MessagePublication{
|
||||
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
|
||||
|
@ -348,7 +566,7 @@ func TestVaaForUninterestingEmitterAddress(t *testing.T) {
|
|||
assert.NotNil(t, gov)
|
||||
|
||||
emitterAddr, _ := vaa.StringToAddress("0x00")
|
||||
var payload = []byte{1, 97, 97, 97, 97, 97}
|
||||
payload := []byte{1, 97, 97, 97, 97, 97}
|
||||
|
||||
msg := common.MessagePublication{
|
||||
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
|
||||
|
@ -381,7 +599,7 @@ func TestVaaForUninterestingPayloadType(t *testing.T) {
|
|||
assert.NotNil(t, gov)
|
||||
|
||||
emitterAddr, _ := vaa.StringToAddress("0x0290fb167208af455bb137780163b7b7a9a10c16")
|
||||
var payload = []byte{2, 97, 97, 97, 97, 97}
|
||||
payload := []byte{2, 97, 97, 97, 97, 97}
|
||||
|
||||
msg := common.MessagePublication{
|
||||
TxHash: hashFromString("0x06f541f5ecfc43407c31587aa6ac3a689e8960f36dc23c332db5510dfc6a4063"),
|
||||
|
|
Loading…
Reference in New Issue