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