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 }