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 }