github.com/okex/exchain@v1.8.0/libs/ibc-go/modules/apps/transfer/keeper/mbt_relay_test.go (about)

     1  package keeper_test
     2  
     3  /// This file is a test driver for model-based tests generated from the TLA+ model of token transfer
     4  /// Written by Andrey Kuprianov within the scope of IBC Audit performed by Informal Systems.
     5  /// In case of any questions please don't hesitate to contact andrey@informal.systems.
     6  
     7  import (
     8  	"fmt"
     9  	sdk "github.com/okex/exchain/libs/cosmos-sdk/types"
    10  	sdkerrors "github.com/okex/exchain/libs/cosmos-sdk/types/errors"
    11  	"github.com/okex/exchain/libs/tendermint/crypto"
    12  	"strings"
    13  
    14  	// "github.com/tendermint/tendermint/crypto"
    15  
    16  	// sdk "github.com/cosmos/cosmos-sdk/types"
    17  	// sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
    18  	"github.com/okex/exchain/libs/ibc-go/modules/apps/transfer/types"
    19  	ibctesting "github.com/okex/exchain/libs/ibc-go/testing"
    20  )
    21  
    22  type TlaBalance struct {
    23  	Address []string `json:"address"`
    24  	Denom   []string `json:"denom"`
    25  	Amount  int64    `json:"amount"`
    26  }
    27  
    28  type TlaFungibleTokenPacketData struct {
    29  	Sender   string   `json:"sender"`
    30  	Receiver string   `json:"receiver"`
    31  	Amount   string   `json:"amount"`
    32  	Denom    []string `json:"denom"`
    33  }
    34  
    35  type TlaFungibleTokenPacket struct {
    36  	SourceChannel string                     `json:"sourceChannel"`
    37  	SourcePort    string                     `json:"sourcePort"`
    38  	DestChannel   string                     `json:"destChannel"`
    39  	DestPort      string                     `json:"destPort"`
    40  	Data          TlaFungibleTokenPacketData `json:"data"`
    41  }
    42  
    43  type TlaOnRecvPacketTestCase = struct {
    44  	// The required subset of bank balances
    45  	BankBefore []TlaBalance `json:"bankBefore"`
    46  	// The packet to process
    47  	Packet TlaFungibleTokenPacket `json:"packet"`
    48  	// The handler to call
    49  	Handler string `json:"handler"`
    50  	// The expected changes in the bank
    51  	BankAfter []TlaBalance `json:"bankAfter"`
    52  	// Whether OnRecvPacket should fail or not
    53  	Error bool `json:"error"`
    54  }
    55  
    56  type FungibleTokenPacket struct {
    57  	SourceChannel string
    58  	SourcePort    string
    59  	DestChannel   string
    60  	DestPort      string
    61  	Data          types.FungibleTokenPacketData
    62  }
    63  
    64  type OnRecvPacketTestCase = struct {
    65  	description string
    66  	// The required subset of bank balances
    67  	bankBefore []Balance
    68  	// The packet to process
    69  	packet FungibleTokenPacket
    70  	// The handler to call
    71  	handler string
    72  	// The expected bank state after processing (wrt. bankBefore)
    73  	bankAfter []Balance
    74  	// Whether OnRecvPacket should pass or fail
    75  	pass bool
    76  }
    77  
    78  type OwnedCoin struct {
    79  	Address string
    80  	Denom   string
    81  }
    82  
    83  type Balance struct {
    84  	Id      string
    85  	Address string
    86  	Denom   string
    87  	Amount  sdk.Int
    88  }
    89  
    90  func AddressFromString(address string) string {
    91  	return sdk.AccAddress(crypto.AddressHash([]byte(address))).String()
    92  }
    93  
    94  func AddressFromTla(addr []string) string {
    95  	if len(addr) != 3 {
    96  		panic("failed to convert from TLA+ address: wrong number of address components")
    97  	}
    98  	s := ""
    99  	if len(addr[0]) == 0 && len(addr[1]) == 0 {
   100  		// simple address: id
   101  		s = addr[2]
   102  	} else if len(addr[2]) == 0 {
   103  		// escrow address: ics20-1\x00port/channel
   104  		s = fmt.Sprintf("%s\x00%s/%s", types.Version, addr[0], addr[1])
   105  	} else {
   106  		panic("failed to convert from TLA+ address: neither simple nor escrow address")
   107  	}
   108  	return s
   109  }
   110  
   111  func DenomFromTla(denom []string) string {
   112  	var i int
   113  	for i = 0; i+1 < len(denom) && len(denom[i]) == 0 && len(denom[i+1]) == 0; i += 2 {
   114  		// skip empty prefixes
   115  	}
   116  	return strings.Join(denom[i:], "/")
   117  }
   118  
   119  func BalanceFromTla(balance TlaBalance) Balance {
   120  	return Balance{
   121  		Id:      AddressFromTla(balance.Address),
   122  		Address: AddressFromString(AddressFromTla(balance.Address)),
   123  		Denom:   DenomFromTla(balance.Denom),
   124  		Amount:  sdk.NewInt(balance.Amount),
   125  	}
   126  }
   127  
   128  func BalancesFromTla(tla []TlaBalance) []Balance {
   129  	balances := make([]Balance, 0)
   130  	for _, b := range tla {
   131  		balances = append(balances, BalanceFromTla(b))
   132  	}
   133  	return balances
   134  }
   135  
   136  func FungibleTokenPacketFromTla(packet TlaFungibleTokenPacket) FungibleTokenPacket {
   137  	return FungibleTokenPacket{
   138  		SourceChannel: packet.SourceChannel,
   139  		SourcePort:    packet.SourcePort,
   140  		DestChannel:   packet.DestChannel,
   141  		DestPort:      packet.DestPort,
   142  		Data: types.NewFungibleTokenPacketData(
   143  			DenomFromTla(packet.Data.Denom),
   144  			packet.Data.Amount,
   145  			AddressFromString(packet.Data.Sender),
   146  			AddressFromString(packet.Data.Receiver)),
   147  	}
   148  }
   149  
   150  func OnRecvPacketTestCaseFromTla(tc TlaOnRecvPacketTestCase) OnRecvPacketTestCase {
   151  	return OnRecvPacketTestCase{
   152  		description: "auto-generated",
   153  		bankBefore:  BalancesFromTla(tc.BankBefore),
   154  		packet:      FungibleTokenPacketFromTla(tc.Packet),
   155  		handler:     tc.Handler,
   156  		bankAfter:   BalancesFromTla(tc.BankAfter), // TODO different semantics
   157  		pass:        !tc.Error,
   158  	}
   159  }
   160  
   161  var addressMap = make(map[string]string)
   162  
   163  type Bank struct {
   164  	balances map[OwnedCoin]sdk.Int
   165  }
   166  
   167  // Make an empty bank
   168  func MakeBank() Bank {
   169  	return Bank{balances: make(map[OwnedCoin]sdk.Int)}
   170  }
   171  
   172  // Subtract other bank from this bank
   173  func (bank *Bank) Sub(other *Bank) Bank {
   174  	diff := MakeBank()
   175  	for coin, amount := range bank.balances {
   176  		otherAmount, exists := other.balances[coin]
   177  		if exists {
   178  			diff.balances[coin] = amount.Sub(otherAmount)
   179  		} else {
   180  			diff.balances[coin] = amount
   181  		}
   182  	}
   183  	for coin, amount := range other.balances {
   184  		if _, exists := bank.balances[coin]; !exists {
   185  			diff.balances[coin] = amount.Neg()
   186  		}
   187  	}
   188  	return diff
   189  }
   190  
   191  // Set specific bank balance
   192  func (bank *Bank) SetBalance(address string, denom string, amount sdk.Int) {
   193  	bank.balances[OwnedCoin{address, denom}] = amount
   194  }
   195  
   196  // Set several balances at once
   197  func (bank *Bank) SetBalances(balances []Balance) {
   198  	for _, balance := range balances {
   199  		bank.balances[OwnedCoin{balance.Address, balance.Denom}] = balance.Amount
   200  		addressMap[balance.Address] = balance.Id
   201  	}
   202  }
   203  
   204  func NullCoin() OwnedCoin {
   205  	return OwnedCoin{
   206  		Address: AddressFromString(""),
   207  		Denom:   "",
   208  	}
   209  }
   210  
   211  // Set several balances at once
   212  func BankFromBalances(balances []Balance) Bank {
   213  	bank := MakeBank()
   214  	for _, balance := range balances {
   215  		coin := OwnedCoin{balance.Address, balance.Denom}
   216  		if coin != NullCoin() { // ignore null coin
   217  			bank.balances[coin] = balance.Amount
   218  			addressMap[balance.Address] = balance.Id
   219  		}
   220  	}
   221  	return bank
   222  }
   223  
   224  // String representation of all bank balances
   225  func (bank *Bank) String() string {
   226  	str := ""
   227  	for coin, amount := range bank.balances {
   228  		str += coin.Address
   229  		if addressMap[coin.Address] != "" {
   230  			str += "(" + addressMap[coin.Address] + ")"
   231  		}
   232  		str += " : " + coin.Denom + " = " + amount.String() + "\n"
   233  	}
   234  	return str
   235  }
   236  
   237  // String representation of non-zero bank balances
   238  func (bank *Bank) NonZeroString() string {
   239  	str := ""
   240  	for coin, amount := range bank.balances {
   241  		if !amount.IsZero() {
   242  			str += coin.Address + " : " + coin.Denom + " = " + amount.String() + "\n"
   243  		}
   244  	}
   245  	return str
   246  }
   247  
   248  // Construct a bank out of the chain bank
   249  func BankOfChain(chain ibctesting.TestChainI) Bank {
   250  	bank := MakeBank()
   251  	// todo how to Iterate all balance
   252  	//	chain.GetSimApp().BankKeeper.IterateAllBalances(chain.GetContext(), func(address sdk.AccAddress, coin sdk.Coin) (stop bool) {
   253  	//		fullDenom := coin.Denom
   254  	//		if strings.HasPrefix(coin.Denom, "ibc/") {
   255  	//			fullDenom, _ = chain.GetSimApp().TransferKeeper.DenomPathFromHash(chain.GetContext(), coin.Denom)
   256  	//		}
   257  	//		bank.SetBalance(address.String(), fullDenom, coin.Amount)
   258  	//		return false
   259  	//	})
   260  	return bank
   261  }
   262  
   263  // Check that the state of the bank is the bankBefore + expectedBankChange
   264  func (suite *KeeperTestSuite) CheckBankBalances(chain ibctesting.TestChainI, bankBefore *Bank, expectedBankChange *Bank) error {
   265  	bankAfter := BankOfChain(chain)
   266  	bankChange := bankAfter.Sub(bankBefore)
   267  	diff := bankChange.Sub(expectedBankChange)
   268  	NonZeroString := diff.NonZeroString()
   269  	if len(NonZeroString) != 0 {
   270  		return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "Unexpected changes in the bank: \n"+NonZeroString)
   271  	}
   272  	return nil
   273  }