github.com/klaytn/klaytn@v1.10.2/tests/fee_payer_contract_test.go (about) 1 // Copyright 2019 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The klaytn library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package tests 18 19 import ( 20 "math/big" 21 "strings" 22 "testing" 23 "time" 24 25 "github.com/klaytn/klaytn/accounts/abi" 26 "github.com/klaytn/klaytn/blockchain/types" 27 "github.com/klaytn/klaytn/common" 28 "github.com/klaytn/klaytn/common/profile" 29 "github.com/stretchr/testify/assert" 30 ) 31 32 // TestFeePayerContract tests a direct call of precompiled contract 0xa (feepayer). 33 // - It tests a contract `FeePayer` in fee_payer_test.sol. 34 // - It directly calls the precompiled contract 0xa. 35 func TestFeePayerContract(t *testing.T) { 36 contractFunctions := []string{"GetFeePayerDirect", "GetFeePayer"} 37 38 for _, c := range contractFunctions { 39 t.Run(c, func(t *testing.T) { 40 testFeePayerContract(t, c) 41 }) 42 } 43 } 44 45 // TestFeePayerContract tests an indirect call of precompiled contract 0xa (feepayer). 46 // - It tests a contract `FeePayerIndirect` in fee_payer_indirect_test.sol. 47 // - It calls a deployed contract calling the precompiled contract 0xa. 48 // TODO-Klaytn-FeePayer: need more test cases for other calls such as delegatecall, etc. 49 func TestFeePayerContractIndirect(t *testing.T) { 50 contractFunctions := []string{"TestCall"} 51 52 for _, c := range contractFunctions { 53 t.Run(c, func(t *testing.T) { 54 testFeePayerContractIndirect(t, c) 55 }) 56 } 57 } 58 59 // testFeePayerContract tests the fee-payer precompiled contract. 60 // 1. Deploy the FeePayer contract. 61 // 2. Make an input data for the contract call. The function name is given as a parameter. 62 // 3. Call the given function `fn`. 63 // 4. Check the returned value with the specified fee payer. 64 func testFeePayerContract(t *testing.T, fn string) { 65 prof := profile.NewProfiler() 66 67 // Initialize blockchain 68 start := time.Now() 69 bcdata, err := NewBCData(2000, 4) 70 if err != nil { 71 t.Fatal(err) 72 } 73 prof.Profile("main_init_blockchain", time.Now().Sub(start)) 74 defer bcdata.Shutdown() 75 76 // Initialize address-balance map for verification 77 start = time.Now() 78 accountMap := NewAccountMap() 79 if err := accountMap.Initialize(bcdata); err != nil { 80 t.Fatal(err) 81 } 82 prof.Profile("main_init_accountMap", time.Now().Sub(start)) 83 84 // 1. Deploy the contract `FeePayer`. 85 start = time.Now() 86 filepath := "../contracts/feepayer/fee_payer_test.sol" 87 contracts, err := deployContract(filepath, bcdata, accountMap, prof) 88 if err != nil { 89 t.Fatal(err) 90 } 91 prof.Profile("main_deployContract", time.Now().Sub(start)) 92 93 signer := types.MakeSigner(bcdata.bc.Config(), bcdata.bc.CurrentHeader().Number) 94 for _, c := range contracts { 95 abii, err := abi.JSON(strings.NewReader(c.abi)) 96 97 // 2. Make an input data for the contract call. The function name is given as a parameter. 98 data, err := abii.Pack(fn) 99 if err != nil { 100 t.Fatal(err) 101 } 102 103 n := accountMap.GetNonce(*bcdata.addrs[0]) 104 105 tx, err := types.NewTransactionWithMap(types.TxTypeFeeDelegatedSmartContractExecution, map[types.TxValueKeyType]interface{}{ 106 types.TxValueKeyNonce: n, 107 types.TxValueKeyFeePayer: *bcdata.addrs[1], 108 types.TxValueKeyGasPrice: big.NewInt(0), 109 types.TxValueKeyGasLimit: uint64(5000000), 110 types.TxValueKeyFrom: *bcdata.addrs[0], 111 types.TxValueKeyAmount: big.NewInt(0), 112 types.TxValueKeyTo: c.address, 113 types.TxValueKeyData: data, 114 }) 115 assert.Equal(t, nil, err) 116 117 err = tx.Sign(signer, bcdata.privKeys[0]) 118 assert.Equal(t, nil, err) 119 120 err = tx.SignFeePayer(signer, bcdata.privKeys[1]) 121 assert.Equal(t, nil, err) 122 123 // 3. Call the given function `fn`. 124 ret, err := callContract(bcdata, tx) 125 assert.Equal(t, nil, err) 126 127 // 4. Check the returned value with the specified fee payer. 128 var feePayer common.Address 129 if err := abii.Unpack(&feePayer, fn, ret); err != nil { 130 t.Fatal(err) 131 } 132 assert.Equal(t, *bcdata.addrs[1], feePayer) 133 } 134 } 135 136 // testFeePayerContract tests an indirect call of the fee-payer precompiled contract. 137 // 1. Deploy the FeePayer contract. 138 // 2. Deploy the FeePayerIndirect contract. 139 // 3. Make an input data for the contract call. The function name is given as a parameter. 140 // 4. Call the given function `fn`. 141 // 5. Check the returned value with the specified fee payer. 142 func testFeePayerContractIndirect(t *testing.T, fn string) { 143 prof := profile.NewProfiler() 144 145 callee_path := "../contracts/feepayer/fee_payer_test.sol" 146 caller_path := "../contracts/feepayer/fee_payer_indirect_test.sol" 147 148 // Initialize blockchain 149 start := time.Now() 150 bcdata, err := NewBCData(2000, 4) 151 if err != nil { 152 t.Fatal(err) 153 } 154 prof.Profile("main_init_blockchain", time.Now().Sub(start)) 155 defer bcdata.Shutdown() 156 157 // Initialize address-balance map for verification 158 start = time.Now() 159 accountMap := NewAccountMap() 160 if err := accountMap.Initialize(bcdata); err != nil { 161 t.Fatal(err) 162 } 163 prof.Profile("main_init_accountMap", time.Now().Sub(start)) 164 165 // 1. Deploy the FeePayer contract. 166 callee_contracts, err := deployContract(callee_path, bcdata, accountMap, prof) 167 assert.Equal(t, nil, err) 168 var callee deployedContract 169 for _, v := range callee_contracts { 170 callee = *v 171 break 172 } 173 174 // 2. Deploy the FeePayerIndirect contract. 175 start = time.Now() 176 caller_contracts, err := deployContract(caller_path, bcdata, accountMap, prof) 177 assert.Equal(t, nil, err) 178 179 prof.Profile("main_deployContract", time.Now().Sub(start)) 180 181 signer := types.MakeSigner(bcdata.bc.Config(), bcdata.bc.CurrentHeader().Number) 182 { 183 var c deployedContract 184 for k, v := range caller_contracts { 185 if strings.Contains(k, "FeePayerIndirect") { 186 c = *v 187 break 188 } 189 } 190 abii, err := abi.JSON(strings.NewReader(c.abi)) 191 192 // 3. Make an input data for the contract call. The function name is given as a parameter. 193 data, err := abii.Pack(fn, callee.address) 194 if err != nil { 195 t.Fatal(err) 196 } 197 198 n := accountMap.GetNonce(*bcdata.addrs[0]) 199 200 tx, err := types.NewTransactionWithMap(types.TxTypeFeeDelegatedSmartContractExecution, map[types.TxValueKeyType]interface{}{ 201 types.TxValueKeyNonce: n, 202 types.TxValueKeyFeePayer: *bcdata.addrs[1], 203 types.TxValueKeyGasPrice: big.NewInt(0), 204 types.TxValueKeyGasLimit: uint64(5000000), 205 types.TxValueKeyFrom: *bcdata.addrs[0], 206 types.TxValueKeyAmount: big.NewInt(0), 207 types.TxValueKeyTo: c.address, 208 types.TxValueKeyData: data, 209 }) 210 assert.Equal(t, nil, err) 211 212 err = tx.Sign(signer, bcdata.privKeys[0]) 213 assert.Equal(t, nil, err) 214 215 err = tx.SignFeePayer(signer, bcdata.privKeys[1]) 216 assert.Equal(t, nil, err) 217 218 // 4. Call the given function `fn`. 219 ret, err := callContract(bcdata, tx) 220 assert.Equal(t, nil, err) 221 222 // 5. Check the returned value with the specified fee payer. 223 var feePayer common.Address 224 if err := abii.Unpack(&feePayer, fn, ret); err != nil { 225 t.Fatal(err) 226 } 227 assert.Equal(t, *bcdata.addrs[1], feePayer) 228 } 229 }