diff --git a/src/Lachain.Core/Blockchain/Hardfork/HardforkHeights.cs b/src/Lachain.Core/Blockchain/Hardfork/HardforkHeights.cs index 63485d773..188038285 100644 --- a/src/Lachain.Core/Blockchain/Hardfork/HardforkHeights.cs +++ b/src/Lachain.Core/Blockchain/Hardfork/HardforkHeights.cs @@ -104,6 +104,8 @@ public static bool IsHardfork_15Active(ulong height) return height >= Hardfork_15; } + // adjust gas consumption + // allow contract balance transfer in ExternalHandler.cs public static bool IsHardfork_16Active(ulong height) { return height >= Hardfork_16; diff --git a/src/Lachain.Core/Blockchain/Operations/BlockManager.cs b/src/Lachain.Core/Blockchain/Operations/BlockManager.cs index 456742437..690b5d407 100644 --- a/src/Lachain.Core/Blockchain/Operations/BlockManager.cs +++ b/src/Lachain.Core/Blockchain/Operations/BlockManager.cs @@ -511,6 +511,7 @@ out Money totalFee throw new InvalidBlockException(OperatingError.BlockGasOverflow); Logger.LogWarning( $"Unable to take transaction {txHash.ToHex()} with gas {receipt.GasUsed}, block gas limit overflowed {gasUsed}/{GasMetering.DefaultBlockGasLimit}"); + gasUsed -= receipt.GasUsed; continue; } else Logger.LogInformation($"Block gas limit after execution ok for tx : {txHash.ToHex()}"); diff --git a/src/Lachain.Core/Blockchain/VM/ExternalHandler.cs b/src/Lachain.Core/Blockchain/VM/ExternalHandler.cs index b0a53dbac..028e28dc4 100644 --- a/src/Lachain.Core/Blockchain/VM/ExternalHandler.cs +++ b/src/Lachain.Core/Blockchain/VM/ExternalHandler.cs @@ -35,6 +35,7 @@ private static UInt160 GetHardfork_5CurrentAddressOrDelegate(IExecutionFrame fra return frame.CurrentAddress; } + // use GasMetering.InvokeContractGasCost before calling DoInternalCall private static InvocationResult DoInternalCall( UInt160 caller, UInt160 address, @@ -49,18 +50,26 @@ private static InvocationResult DoInternalCall( return ContractInvoker.Invoke(address, context, input, gasLimit); } - private static ulong GetDeployHeight(ulong currentHeight, UInt160 contract, UInt160 caller, ulong gasLimit, InvocationMessage message) + private static ulong GetDeployHeight(IExecutionFrame frame, ulong currentHeight, UInt160 contract, UInt160 caller, ulong gasLimit, InvocationMessage message) { if (HardforkHeights.IsHardfork_7Active(currentHeight)) - return GetDeployHeightV2(currentHeight, contract, caller, gasLimit, message); - return GetDeployHeightV1(currentHeight, contract, caller, gasLimit, message); + return GetDeployHeightV2(frame, currentHeight, contract, caller, gasLimit, message); + return GetDeployHeightV1(frame, currentHeight, contract, caller, gasLimit, message); } - private static ulong GetDeployHeightV1(ulong currentHeight, UInt160 contract, UInt160 caller, ulong gasLimit, InvocationMessage message) + private static ulong GetDeployHeightV1(IExecutionFrame frame, ulong currentHeight, UInt160 contract, UInt160 caller, ulong gasLimit, InvocationMessage message) { var input = ContractEncoder.Encode(DeployInterface.MethodGetDeployHeight, contract); - var height = DoInternalCall(caller, ContractRegisterer.DeployContract, - input, gasLimit, message).ReturnValue; + UseGas(frame, GasMetering.InvokeContractGasCost * (ulong) VirtualMachine.ExecutionFrames.Count, null); + if (HardforkHeights.IsHardfork_16Active(currentHeight)) + { + gasLimit = frame.GasLimit - frame.GasUsed; + } + var callResult = DoInternalCall( + caller, ContractRegisterer.DeployContract, input, gasLimit, message + ); + UseGas(frame, callResult.GasUsed, null); + var height = callResult.ReturnValue; Logger.LogInformation($"GetDeployHeight result :[{(height != null ? height.ToHex() : "null")}]"); if (HardforkHeights.IsHardfork_6Active(currentHeight)) { @@ -71,13 +80,21 @@ private static ulong GetDeployHeightV1(ulong currentHeight, UInt160 contract, UI return BitConverter.ToUInt64(height, 0); } - private static ulong GetDeployHeightV2(ulong currentHeight, UInt160 contract, UInt160 caller, ulong gasLimit, InvocationMessage message) + private static ulong GetDeployHeightV2(IExecutionFrame frame, ulong currentHeight, UInt160 contract, UInt160 caller, ulong gasLimit, InvocationMessage message) { try { var input = ContractEncoder.Encode(DeployInterface.MethodGetDeployHeight, contract); - var height = DoInternalCall(caller, ContractRegisterer.DeployContract, - input, gasLimit, message).ReturnValue; + UseGas(frame, GasMetering.InvokeContractGasCost * (ulong) VirtualMachine.ExecutionFrames.Count, null); + if (HardforkHeights.IsHardfork_16Active(currentHeight)) + { + gasLimit = frame.GasLimit - frame.GasUsed; + } + var callResult = DoInternalCall( + caller, ContractRegisterer.DeployContract, input, gasLimit, message + ); + UseGas(frame, callResult.GasUsed, null); + var height = callResult.ReturnValue; Logger.LogInformation($"GetDeployHeight result :[{(height != null ? height.ToHex() : "null")}]"); if (HardforkHeights.IsHardfork_6Active(currentHeight)) { @@ -95,10 +112,16 @@ private static ulong GetDeployHeightV2(ulong currentHeight, UInt160 contract, UI } } - private static void SetDeployHeight(UInt160 contract, ulong height, ulong gasLimit, InvocationMessage message) + private static void SetDeployHeight(IExecutionFrame frame, UInt160 contract, ulong height, ulong gasLimit, InvocationMessage message) { var input = ContractEncoder.Encode(DeployInterface.MethodSetDeployHeight, contract, height.ToBytes()); - DoInternalCall(contract, ContractRegisterer.DeployContract, input, gasLimit, message); + UseGas(frame, GasMetering.InvokeContractGasCost * (ulong) VirtualMachine.ExecutionFrames.Count, null); + if (HardforkHeights.IsHardfork_16Active(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight())) + { + gasLimit = frame.GasLimit - frame.GasUsed; + } + var callResult = DoInternalCall(contract, ContractRegisterer.DeployContract, input, gasLimit, message); + UseGas(frame, callResult.GasUsed, null); } private static byte[]? SafeCopyFromMemory(UnmanagedMemory memory, int offset, int length) @@ -109,7 +132,7 @@ private static void SetDeployHeight(UInt160 contract, ulong height, ulong gasLim return null; if (offset + length > memory.Size) return null; - frame.UseGas(GasMetering.CopyFromMemoryGasPerByte * (ulong) length); + UseGas(frame, GasMetering.NewCopyFromMemoryGasPerByte * (ulong) length, GasMetering.CopyFromMemoryGasPerByte * (ulong) length); var buffer = new byte[length]; try { @@ -130,7 +153,7 @@ private static bool SafeCopyToMemory(UnmanagedMemory memory, byte[] data, int of var frame = VirtualMachine.ExecutionFrames.Peek(); if (offset < 0 || offset + data.Length > memory.Size) return false; - frame.UseGas(GasMetering.CopyToMemoryGasPerByte * (ulong) data.Length); + UseGas(frame, GasMetering.NewCopyToMemoryGasPerByte * (ulong) data.Length, GasMetering.CopyToMemoryGasPerByte * (ulong) data.Length); try { Marshal.Copy(data, 0, IntPtr.Add(memory.Start, offset), data.Length); @@ -175,14 +198,16 @@ private static int InvokeContract( if (!result) throw new InsufficientFundsException(); - Emit(frame.InvocationContext, Lrc20Interface.EventTransfer, transferFrom, address, value); + Emit(frame, Lrc20Interface.EventTransfer, transferFrom, address, value); } + UseGas(frame, GasMetering.LoadStorageGasCost, null); if (snapshot.Contracts.GetContractByHash(address) is null) { frame.LastChildReturnValue = Array.Empty(); return 0; } + UseGas(frame, GasMetering.InvokeContractGasCost * (ulong) VirtualMachine.ExecutionFrames.Count, null); var gasBuffer = SafeCopyFromMemory(frame.Memory, gasOffset, 8); if (gasBuffer is null) throw new InvalidContractException("Bad call to call function"); @@ -212,6 +237,8 @@ private static int InvokeContract( } Logger.LogInformation($"invocationMessage.Sender: {invocationMessage.Sender.ToHex()}"); var callResult = DoInternalCall(GetHardfork_5CurrentAddressOrDelegate(frame), address, inputBuffer, gasLimit, invocationMessage); + UseGas(frame, callResult.GasUsed, null); + if (HardforkHeights.IsHardfork_12Active(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight())) { @@ -227,7 +254,7 @@ private static int InvokeContract( throw new InvalidContractException($"Cannot invoke call: {callResult.Status}, {callResult.ReturnValue}"); } - frame.UseGas(callResult.GasUsed); + UseGas(frame, null, callResult.GasUsed); frame.LastChildReturnValue = callResult.ReturnValue ?? Array.Empty(); return 0; } @@ -327,6 +354,9 @@ int topic3Offset }, topics ); + UseGas(frame, GasMetering.WriteEventPerByteGas * ((ulong)data.Length + 32), null); + var topcisDataLength = topics is null ? 0 : topics.Count * 32; + UseGas(frame, GasMetering.WriteEventPerByteGas * (ulong) topcisDataLength, null); frame.InvocationContext.Snapshot.Events.AddEvent(eventObj); } @@ -376,7 +406,7 @@ public static int Handler_Env_Transfer( var result = TransferBalance(transferFrom, address, value, frame); if (!result) throw new InsufficientFundsException(); - Emit(frame.InvocationContext, Lrc20Interface.EventTransfer, transferFrom, address, value); + Emit(frame, Lrc20Interface.EventTransfer, transferFrom, address, value); } return 0; @@ -392,9 +422,10 @@ public static int Handler_Env_Create(int valueOffset, int dataOffset, int dataLe Value = frame.InvocationContext.Message?.Value ?? UInt256Utils.Zero, Type = InvocationType.Regular, }; - var deployHeight = GetDeployHeight(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), - frame.CurrentAddress, frame.CurrentAddress, frame.GasLimit, - invocationMessage); + var deployHeight = GetDeployHeight( + frame, frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), + frame.CurrentAddress, frame.CurrentAddress, frame.GasLimit, invocationMessage + ); if (Hardfork.HardforkHeights.IsHardfork_2Active(deployHeight)) return Handler_Env_Create_V2(valueOffset, dataOffset, dataLength, resultOffset, frame); return Handler_Env_Create_V1(valueOffset, dataOffset, dataLength, resultOffset, frame); @@ -417,6 +448,7 @@ private static int Handler_Env_Create_V1(int valueOffset, int dataOffset, int da if (value is null) throw new InvalidContractException("Bad call to Create function"); + UseGas(frame, GasMetering.LoadStorageGasCost, null); if (snapshot.Balances.GetBalance(GetHardfork_5CurrentAddressOrDelegate(frame)) < value) { throw new InsufficientFundsException(); @@ -441,8 +473,10 @@ private static int Handler_Env_Create_V1(int valueOffset, int dataOffset, int da Value = frame.InvocationContext.Message?.Value ?? UInt256Utils.Zero, Type = InvocationType.Regular, }; - var deployHeight = GetDeployHeight(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), - frame.CurrentAddress, frame.CurrentAddress, frame.GasLimit, invocationMessage); + var deployHeight = GetDeployHeight( + frame, frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), + frame.CurrentAddress, frame.CurrentAddress, frame.GasLimit, invocationMessage + ); if (!VirtualMachine.VerifyContract(contract.ByteCode, Hardfork.HardforkHeights.IsHardfork_2Active(deployHeight))) @@ -452,8 +486,9 @@ private static int Handler_Env_Create_V1(int valueOffset, int dataOffset, int da try { + UseGas(frame, GasMetering.SaveStorageGasCost, null); snapshot.Contracts.AddContract(context.Sender, contract); - SetDeployHeight(hash, deployHeight, frame.GasLimit, invocationMessage); + SetDeployHeight(frame, hash, deployHeight, frame.GasLimit, invocationMessage); } catch (OutOfGasException e) { @@ -465,7 +500,7 @@ private static int Handler_Env_Create_V1(int valueOffset, int dataOffset, int da frame.UseGas(GasMetering.TransferFundsGasCost); var transferFrom = GetHardfork_5CurrentAddressOrDelegate(frame); TransferBalance(transferFrom, hash, value, frame); - Emit(frame.InvocationContext, Lrc20Interface.EventTransfer, transferFrom, hash, value); + Emit(frame, Lrc20Interface.EventTransfer, transferFrom, hash, value); SafeCopyToMemory(frame.Memory, hash.ToBytes(), resultOffset); @@ -490,6 +525,7 @@ private static int Handler_Env_Create_V2(int valueOffset, int dataOffset, int da if (value is null) throw new InvalidContractException("Bad call to Create function"); + UseGas(frame, GasMetering.LoadStorageGasCost, null); if (snapshot.Balances.GetBalance(GetHardfork_5CurrentAddressOrDelegate(frame)) < value) { throw new InsufficientFundsException(); @@ -517,6 +553,7 @@ private static int Handler_Env_Create_V2(int valueOffset, int dataOffset, int da try { + UseGas(frame, GasMetering.SaveStorageGasCost, null); snapshot.Contracts.AddContract(context.Sender, deploymentContract); } catch (OutOfGasException e) @@ -530,7 +567,14 @@ private static int Handler_Env_Create_V2(int valueOffset, int dataOffset, int da Value = msgValue, Type = InvocationType.Regular, }; - var status = DoInternalCall(GetHardfork_5CurrentAddressOrDelegate(frame), hash, Array.Empty(), frame.GasLimit, invocationMessage); + UseGas(frame, GasMetering.InvokeContractGasCost * (ulong) VirtualMachine.ExecutionFrames.Count, null); + var gasLimit = frame.GasLimit; + if (HardforkHeights.IsHardfork_16Active(snapshot.Blocks.GetTotalBlockHeight())) + { + gasLimit = frame.GasLimit - frame.GasUsed; + } + var status = DoInternalCall(GetHardfork_5CurrentAddressOrDelegate(frame), hash, Array.Empty(), gasLimit, invocationMessage); + UseGas(frame, status.GasUsed, null); if (HardforkHeights.IsHardfork_12Active(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight())) { @@ -554,13 +598,16 @@ private static int Handler_Env_Create_V2(int valueOffset, int dataOffset, int da throw new InvalidContractException("Failed to verify runtime contract"); } - var deployHeight = GetDeployHeight(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), - context.Sender, context.Sender, frame.GasLimit, invocationMessage); + var deployHeight = GetDeployHeight( + frame, frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), + context.Sender, context.Sender, frame.GasLimit, invocationMessage + ); try { + UseGas(frame, GasMetering.SaveStorageGasCost, null); snapshot.Contracts.AddContract(context.Sender, runtimeContract); - SetDeployHeight(hash, deployHeight, frame.GasLimit, invocationMessage); + SetDeployHeight(frame, hash, deployHeight, frame.GasLimit, invocationMessage); } catch (OutOfGasException e) { @@ -572,7 +619,7 @@ private static int Handler_Env_Create_V2(int valueOffset, int dataOffset, int da frame.UseGas(GasMetering.TransferFundsGasCost); var transferFrom = GetHardfork_5CurrentAddressOrDelegate(frame); TransferBalance(transferFrom, hash, value, frame); - Emit(frame.InvocationContext, Lrc20Interface.EventTransfer, transferFrom, hash, value); + Emit(frame, Lrc20Interface.EventTransfer, transferFrom, hash, value); SafeCopyToMemory(frame.Memory, hash.ToBytes(), resultOffset); @@ -590,8 +637,10 @@ public static int Handler_Env_Create2(int valueOffset, int dataOffset, int dataL Value = frame.InvocationContext.Message?.Value ?? UInt256Utils.Zero, Type = InvocationType.Regular, }; - var deployHeight = GetDeployHeight(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), - frame.CurrentAddress, frame.CurrentAddress, frame.GasLimit, invocationMessage); + var deployHeight = GetDeployHeight( + frame, frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), + frame.CurrentAddress, frame.CurrentAddress, frame.GasLimit, invocationMessage + ); if (Hardfork.HardforkHeights.IsHardfork_2Active(deployHeight)) return Handler_Env_Create2_V2(valueOffset, dataOffset, dataLength, saltOffset, resultOffset, frame); @@ -616,6 +665,7 @@ public static int Handler_Env_Create2_V1(int valueOffset, int dataOffset, int da if (value is null) throw new InvalidContractException("Bad call to Create2 function"); + UseGas(frame, GasMetering.LoadStorageGasCost, null); if (snapshot.Balances.GetBalance(GetHardfork_5CurrentAddressOrDelegate(frame)) < value) { throw new InsufficientFundsException(); @@ -638,8 +688,10 @@ public static int Handler_Env_Create2_V1(int valueOffset, int dataOffset, int da Value = frame.InvocationContext.Message?.Value ?? UInt256Utils.Zero, Type = InvocationType.Regular, }; - var deployHeight = GetDeployHeight(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), - frame.CurrentAddress, frame.CurrentAddress, frame.GasLimit, invocationMessage); + var deployHeight = GetDeployHeight( + frame, frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), + frame.CurrentAddress, frame.CurrentAddress, frame.GasLimit, invocationMessage + ); if (!VirtualMachine.VerifyContract(contract.ByteCode, Hardfork.HardforkHeights.IsHardfork_2Active(deployHeight))) @@ -649,8 +701,9 @@ public static int Handler_Env_Create2_V1(int valueOffset, int dataOffset, int da try { + UseGas(frame, GasMetering.SaveStorageGasCost, null); snapshot.Contracts.AddContract(context.Sender, contract); - SetDeployHeight(hash, deployHeight, frame.GasLimit, invocationMessage); + SetDeployHeight(frame, hash, deployHeight, frame.GasLimit, invocationMessage); } catch (OutOfGasException e) { @@ -662,7 +715,7 @@ public static int Handler_Env_Create2_V1(int valueOffset, int dataOffset, int da frame.UseGas(GasMetering.TransferFundsGasCost); var transferFrom = GetHardfork_5CurrentAddressOrDelegate(frame); TransferBalance(transferFrom, hash, value, frame); - Emit(frame.InvocationContext, Lrc20Interface.EventTransfer, transferFrom, hash, value); + Emit(frame, Lrc20Interface.EventTransfer, transferFrom, hash, value); SafeCopyToMemory(frame.Memory, hash.ToBytes(), resultOffset); @@ -688,6 +741,7 @@ public static int Handler_Env_Create2_V2(int valueOffset, int dataOffset, int da if (value is null) throw new InvalidContractException("Bad call to Create2 function"); + UseGas(frame, GasMetering.LoadStorageGasCost, null); if (snapshot.Balances.GetBalance(GetHardfork_5CurrentAddressOrDelegate(frame)) < value) { throw new InsufficientFundsException(); @@ -713,6 +767,7 @@ public static int Handler_Env_Create2_V2(int valueOffset, int dataOffset, int da try { + UseGas(frame, GasMetering.SaveStorageGasCost, null); snapshot.Contracts.AddContract(context.Sender, deploymentContract); } catch (OutOfGasException e) @@ -726,7 +781,14 @@ public static int Handler_Env_Create2_V2(int valueOffset, int dataOffset, int da Value = msgValue, Type = InvocationType.Regular, }; - var status = DoInternalCall(GetHardfork_5CurrentAddressOrDelegate(frame), hash, Array.Empty(), frame.GasLimit, invocationMessage); + UseGas(frame, GasMetering.InvokeContractGasCost * (ulong) VirtualMachine.ExecutionFrames.Count, null); + var gasLimit = frame.GasLimit; + if (HardforkHeights.IsHardfork_16Active(snapshot.Blocks.GetTotalBlockHeight())) + { + gasLimit = frame.GasLimit - frame.GasUsed; + } + var status = DoInternalCall(GetHardfork_5CurrentAddressOrDelegate(frame), hash, Array.Empty(), gasLimit, invocationMessage); + UseGas(frame, status.GasUsed, null); if (HardforkHeights.IsHardfork_12Active(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight())) { @@ -745,8 +807,10 @@ public static int Handler_Env_Create2_V2(int valueOffset, int dataOffset, int da // runtime code var runtimeContract = new Contract(hash, status.ReturnValue); - var deployHeight = GetDeployHeight(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), - frame.CurrentAddress, frame.CurrentAddress, frame.GasLimit, invocationMessage); + var deployHeight = GetDeployHeight( + frame, frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(), + frame.CurrentAddress, frame.CurrentAddress, frame.GasLimit, invocationMessage + ); if (!VirtualMachine.VerifyContract(runtimeContract.ByteCode, true)) { @@ -755,8 +819,9 @@ public static int Handler_Env_Create2_V2(int valueOffset, int dataOffset, int da try { + UseGas(frame, GasMetering.SaveStorageGasCost, null); snapshot.Contracts.AddContract(context.Sender, runtimeContract); - SetDeployHeight(hash, deployHeight, frame.GasLimit, invocationMessage); + SetDeployHeight(frame, hash, deployHeight, frame.GasLimit, invocationMessage); } catch (OutOfGasException e) { @@ -768,7 +833,7 @@ public static int Handler_Env_Create2_V2(int valueOffset, int dataOffset, int da frame.UseGas(GasMetering.TransferFundsGasCost); var transferFrom = GetHardfork_5CurrentAddressOrDelegate(frame); TransferBalance(transferFrom, hash, value, frame); - Emit(frame.InvocationContext, Lrc20Interface.EventTransfer, transferFrom, hash, value); + Emit(frame, Lrc20Interface.EventTransfer, transferFrom, hash, value); SafeCopyToMemory(frame.Memory, hash.ToBytes(), resultOffset); @@ -803,6 +868,7 @@ public static int Handler_Env_GetCodeSize() var frame = VirtualMachine.ExecutionFrames.Peek() as WasmExecutionFrame ?? throw new InvalidOperationException("Cannot call GetCodeSize outside wasm frame"); frame.UseGas(GasMetering.GetCodeSizeGasCost); + UseGas(frame, GasMetering.LoadStorageGasCost, null); var byteCode = frame.InvocationContext.Snapshot.Contracts.GetContractByHash(frame.CurrentAddress)?.ByteCode ?? Array.Empty(); return byteCode.Length; } @@ -813,6 +879,7 @@ public static void Handler_Env_CopyCodeValue(int resultOffset, int dataOffset, i var frame = VirtualMachine.ExecutionFrames.Peek() as WasmExecutionFrame ?? throw new InvalidOperationException("Cannot call CopyCodeValue outside wasm frame"); frame.UseGas(GasMetering.CopyCodeValueGasCost); + UseGas(frame, GasMetering.LoadStorageGasCost, null); var byteCode = frame.InvocationContext.Snapshot.Contracts.GetContractByHash(frame.CurrentAddress)?.ByteCode ?? Array.Empty(); if (dataOffset < 0 || length < 0 || dataOffset + length > byteCode.Length) throw new InvalidContractException("Bad CopyCodeValue call"); @@ -1032,6 +1099,7 @@ public static int Handler_Env_GetStorageStringSize(int keyOffset) var frame = VirtualMachine.ExecutionFrames.Peek() as WasmExecutionFrame ?? throw new InvalidOperationException("Cannot call GETSTORAGESTRINGSIZE outside wasm frame"); frame.UseGas(GasMetering.GetReturnSizeGasCost); + UseGas(frame, GasMetering.LoadStorageGasCost, null); var key = SafeCopyFromMemory(frame.Memory, keyOffset, 32); if (key is null) @@ -1058,6 +1126,7 @@ public static void Handler_Env_SetReturn(int offset, int length) Logger.LogInformation($"Handler_Env_SetReturn({offset}, {length})"); var frame = VirtualMachine.ExecutionFrames.Peek() as WasmExecutionFrame ?? throw new InvalidOperationException("Cannot call SETRETURN outside wasm frame"); + UseGas(frame, GasMetering.SetReturnGasCost, null); var ret = SafeCopyFromMemory(frame.Memory, offset, length); Logger.LogInformation($"ret: {ret.ToHex()}"); if (ret is null) @@ -1082,6 +1151,7 @@ public static void Handler_Env_GetGasLeft(int dataOffset) Logger.LogInformation($"Handler_Env_GetGasLeft({dataOffset})"); var frame = VirtualMachine.ExecutionFrames.Peek() as WasmExecutionFrame ?? throw new InvalidOperationException("Cannot call GETGASLEFT outside wasm frame"); + UseGas(frame, GasMetering.GetGasLeftGasCost, null); var data = (frame.GasLimit - frame.GasUsed).ToBytes().ToArray(); var ret = SafeCopyToMemory(frame.Memory, data, dataOffset); if (!ret) @@ -1115,6 +1185,7 @@ public static void Handler_Env_GetBlockNumber(int dataOffset) Logger.LogInformation($"Handler_Env_GetBlockNumber({dataOffset})"); var frame = VirtualMachine.ExecutionFrames.Peek() as WasmExecutionFrame ?? throw new InvalidOperationException("Cannot call GETBLOCKNUMBER outside wasm frame"); + UseGas(frame, GasMetering.LoadStorageGasCost, null); var data = frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight().ToBytes().ToArray(); var ret = SafeCopyToMemory(frame.Memory, data, dataOffset); if (!ret) @@ -1180,7 +1251,7 @@ public static void Handler_Env_CryptoRecoverV1(int hashOffset, int v, int rOffse { bool useNewChainId = HardforkHeights.IsHardfork_9Active(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight() + 1); - frame.UseGas(GasMetering.RecoverGasCost); + UseGas(frame, GasMetering.NewRecoverGasCost, GasMetering.RecoverGasCost); var hash = SafeCopyFromMemory(frame.Memory, hashOffset, 32) ?? throw new InvalidOperationException(); var sig = new byte[SignatureUtils.Length(useNewChainId)]; @@ -1211,7 +1282,7 @@ public static void Handler_Env_CryptoRecoverV2(int hashOffset, int v, int rOffse { bool useNewChainId = HardforkHeights.IsHardfork_9Active(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight() + 1); - frame.UseGas(GasMetering.RecoverGasCost); + UseGas(frame, GasMetering.NewRecoverGasCost, GasMetering.RecoverGasCost); var hash = SafeCopyFromMemory(frame.Memory, hashOffset, 32) ?? throw new InvalidOperationException(); var sigLength = SignatureUtils.Length(useNewChainId); @@ -1260,7 +1331,7 @@ public static void Handler_Env_CryptoVerify(int messageOffset, int messageLength ?? throw new InvalidOperationException("Cannot call ECVERIFY outside wasm frame"); bool useNewChainId = HardforkHeights.IsHardfork_9Active(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight() + 1); - frame.UseGas(GasMetering.VerifyGasCost); + UseGas(frame, GasMetering.NewVerifyGasCost, GasMetering.VerifyGasCost); var message = SafeCopyFromMemory(frame.Memory, messageOffset, messageLength) ?? throw new InvalidOperationException(); var sig = SafeCopyFromMemory(frame.Memory, signatureOffset, SignatureUtils.Length(useNewChainId)) ?? @@ -1323,7 +1394,12 @@ public static void Handler_Env_GetAddress(int resultOffset) var frame = VirtualMachine.ExecutionFrames.Peek() as WasmExecutionFrame ?? throw new InvalidOperationException("Cannot call GetAddress outside wasm frame"); var result = (frame.InvocationContext.Message?.Delegate ?? frame.CurrentAddress).ToBytes(); - SafeCopyToMemory(frame.Memory, result, resultOffset); + var ret = SafeCopyToMemory(frame.Memory, result, resultOffset); + if (HardforkHeights.IsHardfork_16Active(frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight())) + { + if (!ret) + throw new InvalidContractException("Bad call to (get_address)"); + } } public static void Handler_Env_GetMsgValue(int dataOffset) @@ -1343,7 +1419,6 @@ public static void Handler_Env_GetBlockGasLimit(int dataOffset) var frame = VirtualMachine.ExecutionFrames.Peek() as WasmExecutionFrame ?? throw new InvalidOperationException("Cannot call GetBlockGasLimit outside wasm frame"); const ulong defaultBlockGasLimit = GasMetering.DefaultBlockGasLimit; - // Load `default block gasLimit` at given memory offset var ret = SafeCopyToMemory(frame.Memory, defaultBlockGasLimit.ToBytes().ToArray(), dataOffset); if (!ret) @@ -1356,7 +1431,6 @@ public static void Handler_Env_GetBlockCoinbase(int dataOffset) var frame = VirtualMachine.ExecutionFrames.Peek() as WasmExecutionFrame ?? throw new InvalidOperationException("Cannot call GetBlockCoinbase outside wasm frame"); UInt160 coinbase = UInt160Utils.Zero; - // Load `zero address` at given memory offset var ret = SafeCopyToMemory(frame.Memory, coinbase.ToBytes().ToArray(), dataOffset); if (!ret) @@ -1369,7 +1443,6 @@ public static void Handler_Env_GetBlockDifficulty(int dataOffset) var frame = VirtualMachine.ExecutionFrames.Peek() as WasmExecutionFrame ?? throw new InvalidOperationException("Cannot call GetBlockDifficulty outside wasm frame"); var difficulty = 0; - // Load `zero difficulty` at given memory offset var ret = SafeCopyToMemory(frame.Memory, difficulty.ToBytes().ToArray(), dataOffset); if (!ret) @@ -1390,6 +1463,7 @@ public static void Handler_Env_GetExternalBalance(int addressOffset, int resultO var address = addressBuffer.Take(20).ToArray().ToUInt160(); // Get balance at the given addres + UseGas(frame, GasMetering.LoadStorageGasCost, null); var balance = snapshot.Balances.GetBalance(address); // Load balance at the given resultOffset @@ -1404,7 +1478,6 @@ public static void Handler_Env_GetExtcodesize(int addressOffset, int resultOffse Logger.LogInformation($"Handler_Env_GetExtcodesize({addressOffset}, {resultOffset})"); var frame = VirtualMachine.ExecutionFrames.Peek() as WasmExecutionFrame ?? throw new InvalidOperationException("Cannot call GetExtcodesize outside wasm frame"); - // Get the address from the given memory offset var snapshot = frame.InvocationContext.Snapshot; var addressBuffer = SafeCopyFromMemory(frame.Memory, addressOffset, 20); @@ -1413,6 +1486,7 @@ public static void Handler_Env_GetExtcodesize(int addressOffset, int resultOffse var address = addressBuffer.Take(20).ToArray().ToUInt160(); // Get contract at the given address + UseGas(frame, GasMetering.LoadStorageGasCost, null); var contract = snapshot.Contracts.GetContractByHash(address); // Load contract size at the given resultOffset @@ -1429,6 +1503,7 @@ public static void Handler_Env_GetBlockTimestamp(int dataOffset) ?? throw new InvalidOperationException("Cannot call GetBlockTimestamp outside wasm frame"); // Get the TotalBlockHeight at the given Snapshot + UseGas(frame, GasMetering.LoadStorageGasCost, null); var snapshot = frame.InvocationContext.Snapshot; var blockHeight = snapshot.Blocks.GetTotalBlockHeight(); @@ -1453,6 +1528,7 @@ public static void Handler_Env_GetBlockHash(int numberOffset, int dataOffset) var blockNumber = BitConverter.ToUInt64(blockNumberBuffer, 0); // Get block at the given height + UseGas(frame, GasMetering.LoadStorageGasCost, null); var block = snapshot.Blocks.GetBlockByHeight(blockNumber); // Get block's hash @@ -1534,6 +1610,21 @@ private static bool TransferBalance( ); } } + + private static void UseGas(IExecutionFrame frame, ulong? newGasPrice, ulong? oldGasPrice) + { + var height = frame.InvocationContext.Snapshot.Blocks.GetTotalBlockHeight(); + if (HardforkHeights.IsHardfork_16Active(height)) + { + if (newGasPrice.HasValue) + frame.UseGas(newGasPrice.Value); + } + else + { + if (oldGasPrice.HasValue) + frame.UseGas(oldGasPrice.Value); + } + } private static FunctionImport CreateImport(string methodName) { @@ -1619,10 +1710,11 @@ private static string PrettyParam(dynamic param) }; } - private static void Emit(InvocationContext context, string eventSignature, params dynamic[] values) + private static void Emit(IExecutionFrame frame, string eventSignature, params dynamic[] values) { var eventData = ContractEncoder.Encode(null, values); EventObject eventObj; + var context = frame.InvocationContext; if (Lrc20Interface.EventTransfer == eventSignature && HardforkHeights.IsHardfork_6Active(context.Snapshot.Blocks.GetTotalBlockHeight())) { @@ -1634,6 +1726,9 @@ private static void Emit(InvocationContext context, string eventSignature, param Buffer.BlockCopy(eventData, 64, value, 0, 32); topics.Add(from.ToUInt256()); topics.Add(to.ToUInt256()); + UseGas(frame, GasMetering.WriteEventPerByteGas * ((ulong)value.Length + 32), null); + var topcisDataLength = topics.Count * 32; + UseGas(frame, GasMetering.WriteEventPerByteGas * (ulong) topcisDataLength, null); eventObj = new EventObject( new Event diff --git a/src/Lachain.Core/Blockchain/VM/GasMetering.cs b/src/Lachain.Core/Blockchain/VM/GasMetering.cs index e90d6cfa4..159b90540 100644 --- a/src/Lachain.Core/Blockchain/VM/GasMetering.cs +++ b/src/Lachain.Core/Blockchain/VM/GasMetering.cs @@ -7,13 +7,18 @@ public class GasMetering public const ulong InputDataGasPerByte = 10; public const ulong CopyFromMemoryGasPerByte = 10; + public const ulong NewCopyFromMemoryGasPerByte = 100; public const ulong CopyToMemoryGasPerByte = 10; + public const ulong NewCopyToMemoryGasPerByte = 100; public const ulong GetCallValueGasCost = 100; public const ulong GetCallSizeGasCost = 10; public const ulong GetReturnValueGasCost = 100; public const ulong GetReturnSizeGasCost = 10; + public const ulong SetReturnGasCost = 100; + public const ulong GetGasLeftGasCost = 100; public const ulong CopyCodeValueGasCost = 100; public const ulong GetCodeSizeGasCost = 10; + public const ulong InvokeContractGasCost = 200_000; public const ulong TransferFundsGasCost = 3_000_000; public const ulong LoadStorageGasCost = 500_000; public const ulong SaveStorageGasCost = 3_000_000; @@ -25,7 +30,9 @@ public class GasMetering public const ulong Ripemd160GasCost = 0; public const ulong Ripemd160GasPerByte = 100_000; public const ulong RecoverGasCost = 100_000; + public const ulong NewRecoverGasCost = 1_000_000; public const ulong VerifyGasCost = 60_000; + public const ulong NewVerifyGasCost = 1_000_000; public const ulong WriteEventPerByteGas = SaveStorageGasCost / 32; public const ulong CallDataLoad = 1_000; public const ulong MLoad = 1_000; diff --git a/test/Lachain.CoreTest/RPC/HTTP/Web3/Web3TransactionTests.cs b/test/Lachain.CoreTest/RPC/HTTP/Web3/Web3TransactionTests.cs index f2632c0f1..689cd0d98 100644 --- a/test/Lachain.CoreTest/RPC/HTTP/Web3/Web3TransactionTests.cs +++ b/test/Lachain.CoreTest/RPC/HTTP/Web3/Web3TransactionTests.cs @@ -336,10 +336,41 @@ public void Test_EstimateGas() { _blockManager.TryBuildGenesisBlock(); - var keyPair = _privateWallet!.EcdsaKeyPair; - GenerateBlocks(1, 1); + + // after hardfork: + while (!HardforkHeights.IsHardfork_16Active(_blockManager.GetHeight())) + GenerateBlocks(_blockManager.GetHeight() + 1, _blockManager.GetHeight() + 1); + + DeployAndTestContract("0x2e356e"); + + var receipt = TestUtils.GetRandomTransaction(false); + var opts = new JObject + { + ["from"] = receipt.Transaction.From.ToHex(), + ["to"] = receipt.Transaction.To.ToHex(), + }; + + var result = _apiService!.EstimateGas(opts); + Assert.AreNotEqual(result, "0x"); + } + [Test] + /// Changed from private to public + [Ignore("fix it")] + public void Test_GetNetworkGasPrice() + { + var gasPrice_Expected = "0x1"; + + var gasPrice_Actual = _apiService!.GetNetworkGasPrice(); + + Assert.AreEqual(gasPrice_Expected, gasPrice_Actual); + + } + + private void DeployAndTestContract(string gasEstimate) + { // Deploy contract + var keyPair = _privateWallet!.EcdsaKeyPair; var byteCode = ByteCodeHex.HexToBytes(); Assert.That(VirtualMachine.VerifyContract(byteCode, true), "Unable to validate smart-contract code"); var from = keyPair.PublicKey.GetAddress(); @@ -352,7 +383,7 @@ public void Test_EstimateGas() var tx = _transactionBuilder.DeployTransaction(from, byteCode); var signedTx = _transactionSigner.Sign(tx, keyPair, HardforkHeights.IsHardfork_9Active(2)); Assert.That(_transactionPool.Add(signedTx) == OperatingError.Ok, "Can't add deploy tx to pool"); - GenerateBlocks(2, 2); + GenerateBlocks(_blockManager.GetHeight() + 1, _blockManager.GetHeight() + 1); // check contract is deployed var contract = _stateManager.LastApprovedSnapshot.Contracts.GetContractByHash(contractHash); @@ -369,30 +400,8 @@ public void Test_EstimateGas() }; var result = _apiService!.EstimateGas(opts); - Assert.AreEqual(result, "0x2e1d22"); - - var receipt = TestUtils.GetRandomTransaction(false); - opts = new JObject - { - ["from"] = receipt.Transaction.From.ToHex(), - ["to"] = receipt.Transaction.To.ToHex(), - }; - - result = _apiService!.EstimateGas(opts); - Assert.AreNotEqual(result, "0x"); - } - - [Test] - /// Changed from private to public - [Ignore("fix it")] - public void Test_GetNetworkGasPrice() - { - var gasPrice_Expected = "0x1"; - - var gasPrice_Actual = _apiService!.GetNetworkGasPrice(); - - Assert.AreEqual(gasPrice_Expected, gasPrice_Actual); - + System.Console.WriteLine(HardforkHeights.IsHardfork_16Active(_blockManager.GetHeight())); + Assert.AreEqual(result, gasEstimate); } // Below methods Execute a Transaction diff --git a/test/Lachain.CoreTest/Resources/config2.json b/test/Lachain.CoreTest/Resources/config2.json index e9ad640eb..6344d869f 100644 --- a/test/Lachain.CoreTest/Resources/config2.json +++ b/test/Lachain.CoreTest/Resources/config2.json @@ -58,7 +58,8 @@ "hardfork_12": 0, "hardfork_13": 0, "hardfork_14": 0, - "hardfork_15": 0 + "hardfork_15": 0, + "hardfork_16": 10 }, "storage": { "provider": "RocksDB", diff --git a/test/Lachain.StorageTest/StorageIntergrationTest.cs b/test/Lachain.StorageTest/StorageIntergrationTest.cs index bda218398..f44e8428b 100644 --- a/test/Lachain.StorageTest/StorageIntergrationTest.cs +++ b/test/Lachain.StorageTest/StorageIntergrationTest.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Reflection; using Lachain.Core.CLI; @@ -5,7 +6,9 @@ using Lachain.Core.DI; using Lachain.Core.DI.Modules; using Lachain.Core.DI.SimpleInjector; +using Lachain.Crypto; using Lachain.Storage.State; +using Lachain.Utility.Serialization; using Lachain.Utility.Utils; using Lachain.UtilityTest; using NUnit.Framework;