diff --git a/contract/contract_module.c b/contract/contract_module.c index da9b259be..3fd1ac152 100644 --- a/contract/contract_module.c +++ b/contract/contract_module.c @@ -46,7 +46,7 @@ static int set_value(lua_State *L, const char *str) { luaL_error(L, "not enough memory"); } lua_pushstring(L, str); - free (str); + free(str); break; } default: @@ -280,6 +280,14 @@ static int moduleBalance(lua_State *L) { return 1; } +static int moduleGasLeft(lua_State *L) { + char str[64]; + // do not charge gas for this call for easier calculations + snprintf(str, sizeof(str), "%llu", lua_gasget(L)); + lua_pushstring(L, str); + return 1; +} + static int modulePcall(lua_State *L) { int argc = lua_gettop(L) - 1; int service = getLuaExecContext(L); @@ -492,7 +500,20 @@ static const luaL_Reg deploy_call_meta[] = { {NULL, NULL} }; -static const luaL_Reg contract_lib[] = { +static const luaL_Reg contract_lib_v3[] = { + {"balance", moduleBalance}, + {"send", moduleSend}, + {"pcall", modulePcall}, + {"event", moduleEvent}, + {"stake", moduleStake}, + {"unstake", moduleUnstake}, + {"vote", moduleVote}, + {"voteDao", moduleVoteDao}, + {NULL, NULL} +}; + +static const luaL_Reg contract_lib_v4[] = { + {"gasLeft", moduleGasLeft}, {"balance", moduleBalance}, {"send", moduleSend}, {"pcall", modulePcall}, @@ -506,7 +527,11 @@ static const luaL_Reg contract_lib[] = { int luaopen_contract(lua_State *L) { - luaL_register(L, contract_str, contract_lib); + if (vm_is_hardfork(L, 4)) { + luaL_register(L, contract_str, contract_lib_v4); + } else { + luaL_register(L, contract_str, contract_lib_v3); + } lua_createtable(L, 0, 3); luaL_register(L, NULL, call_methods); diff --git a/contract/vm_dummy/test_files/contract_gasleft.lua b/contract/vm_dummy/test_files/contract_gasleft.lua new file mode 100644 index 000000000..654752f81 --- /dev/null +++ b/contract/vm_dummy/test_files/contract_gasleft.lua @@ -0,0 +1,42 @@ + +function test1() + local gas_before = bignum.number(contract.gasLeft()) + local a = 0 + for i = 1,10 do + a = a + i + end + local gas_after = bignum.number(contract.gasLeft()) + local used_gas = gas_before - gas_after + return gas_before, gas_after, used_gas +end + +function test2(...) + local gas_before = bignum.number(contract.gasLeft()) + contract.call(...) + local gas_after = bignum.number(contract.gasLeft()) + local used_gas = gas_before - gas_after + return gas_before, gas_after, used_gas +end + +function test3(address) + local g1 = contract.gasLeft() + local g2, g3 = contract.call(address, "test") + local g4 = contract.gasLeft() + return g1, g2, g3, g4 +end + +function callback() + return contract.gasLeft() +end + +abi.register(test1, test2, test3, callback) + +--- this is used in another contract + +function test() + local g1 = contract.gasLeft() + local g2 = contract.call(system.getSender(), "callback") + return g1, g2 +end + +abi.register(test) diff --git a/contract/vm_dummy/test_files/loop.lua b/contract/vm_dummy/test_files/loop.lua new file mode 100644 index 000000000..5748fd770 --- /dev/null +++ b/contract/vm_dummy/test_files/loop.lua @@ -0,0 +1,10 @@ + +function loop(n) + local a = 0 + for i = 1,n do + a = a + i + end + return a +end + +abi.register(loop) diff --git a/contract/vm_dummy/vm_dummy_pub_test.go b/contract/vm_dummy/vm_dummy_pub_test.go index 544d60c0b..cc210f4c4 100644 --- a/contract/vm_dummy/vm_dummy_pub_test.go +++ b/contract/vm_dummy/vm_dummy_pub_test.go @@ -794,3 +794,45 @@ func TestTypeBigTable(t *testing.T) { require.NoErrorf(t, err, "failed to call tx") } } + +func TestContractGasLeft(t *testing.T) { + code1 := readLuaCode(t, "contract_gasleft.lua") + code2 := readLuaCode(t, "loop.lua") + + for version := int32(4); version <= max_version; version++ { + bc, err := LoadDummyChain(SetHardForkVersion(version), SetPubNet()) + require.NoErrorf(t, err, "failed to create dummy chain") + defer bc.Release() + + err = bc.ConnectBlock( + NewLuaTxAccount("user1", 1, types.Aergo), + NewLuaTxDeploy("user1", "A", 0, code1), + NewLuaTxDeploy("user1", "B", 0, code1), + NewLuaTxDeploy("user1", "loop", 0, code2), + ) + require.NoErrorf(t, err, "failed to connect new block") + + tx := NewLuaTxCall("user1", "A", 0, `{"Name":"test1", "Args":[]}`) + err = bc.ConnectBlock(tx) + require.NoErrorf(t, err, "failed to connect new block") + receipt := bc.GetReceipt(tx.Hash()) + expected := `[{"_bignum":"18446744073709548605"},{"_bignum":"18446744073709548172"},{"_bignum":"433"}]` + require.Equalf(t, expected, receipt.GetRet(), "contract call ret error") + + tx = NewLuaTxCall("user1", "A", 0, fmt.Sprintf(`{"Name":"test2", "Args":["%s","loop",10]}`, nameToAddress("loop"))) + err = bc.ConnectBlock(tx) + require.NoErrorf(t, err, "failed to connect new block") + receipt = bc.GetReceipt(tx.Hash()) + expected = `[{"_bignum":"18446744073709547285"},{"_bignum":"18446744073709543337"},{"_bignum":"3948"}]` + require.Equalf(t, expected, receipt.GetRet(), "contract call ret error") + + // make an external call and a callback, like this: A -> B -> A + tx = NewLuaTxCall("user1", "A", 0, fmt.Sprintf(`{"Name":"test3", "Args":["%s"]}`, nameToAddress("B"))) + err = bc.ConnectBlock(tx) + require.NoErrorf(t, err, "failed to connect new block") + receipt = bc.GetReceipt(tx.Hash()) + expected = `["18446744073709548612","18446744073709543257","18446744073709536656","18446744073709535921"]` + require.Equalf(t, expected, receipt.GetRet(), "contract call ret error") + + } +}