diff --git a/Makefile b/Makefile index 8e1e367fe..c43880365 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,9 @@ clean: build: go build ./... +gen-async: + protoc -I=./vmhost -I=${GOPATH}/src -I=${GOPATH}/src/github.com/multiversx/protobuf/protobuf -I=${GOPATH}/src/github.com/gogo/protobuf --gogoslick_out=./vmhost ./vmhost/asyncCall.proto + vmserver: ifndef VMSERVER_PATH $(error VMSERVER_PATH is undefined) diff --git a/config/config.toml b/config/config.toml index b873a641a..e169f5a8e 100644 --- a/config/config.toml +++ b/config/config.toml @@ -45,6 +45,7 @@ AsyncCallStep = 10 AsyncCallbackGasLock = 10 CreateAsyncCall = 10 + CreateAsyncV3Call = 10 SetAsyncCallback = 10 SetAsyncGroupCallback = 10 SetAsyncContextCallback = 10 diff --git a/config/gasCost.go b/config/gasCost.go index 8cf0b3638..040c6c67f 100644 --- a/config/gasCost.go +++ b/config/gasCost.go @@ -70,6 +70,7 @@ type BaseOpsAPICost struct { AsyncCallStep uint64 AsyncCallbackGasLock uint64 CreateAsyncCall uint64 + CreateAsyncV3Call uint64 SetAsyncCallback uint64 SetAsyncGroupCallback uint64 SetAsyncContextCallback uint64 diff --git a/config/gasSchedule.go b/config/gasSchedule.go index 55866fd55..72ded0a1a 100644 --- a/config/gasSchedule.go +++ b/config/gasSchedule.go @@ -308,6 +308,7 @@ func FillGasMapBaseOpsAPICosts(value, asyncCallbackGasLock uint64) map[string]ui gasMap["AsyncCallStep"] = value gasMap["AsyncCallbackGasLock"] = asyncCallbackGasLock gasMap["CreateAsyncCall"] = value + gasMap["CreateAsyncV3Call"] = value gasMap["SetAsyncCallback"] = value gasMap["SetAsyncGroupCallback"] = value gasMap["SetAsyncContextCallback"] = value diff --git a/executor/vmHooks.go b/executor/vmHooks.go index c3acedbcc..128ae3d40 100644 --- a/executor/vmHooks.go +++ b/executor/vmHooks.go @@ -40,6 +40,7 @@ type MainVMHooks interface { TransferESDTNFTExecute(destOffset MemPtr, tokenIDOffset MemPtr, tokenIDLen MemLength, valueOffset MemPtr, nonce int64, gasLimit int64, functionOffset MemPtr, functionLength MemLength, numArguments int32, argumentsLengthOffset MemPtr, dataOffset MemPtr) int32 MultiTransferESDTNFTExecute(destOffset MemPtr, numTokenTransfers int32, tokenTransfersArgsLengthOffset MemPtr, tokenTransferDataOffset MemPtr, gasLimit int64, functionOffset MemPtr, functionLength MemLength, numArguments int32, argumentsLengthOffset MemPtr, dataOffset MemPtr) int32 CreateAsyncCall(destOffset MemPtr, valueOffset MemPtr, dataOffset MemPtr, dataLength MemLength, successOffset MemPtr, successLength MemLength, errorOffset MemPtr, errorLength MemLength, gas int64, extraGasForCallback int64) int32 + CreateAsyncV3Call(destOffset MemPtr, valueOffset MemPtr, dataOffset MemPtr, dataLength MemLength, successOffset MemPtr, successLength MemLength, errorOffset MemPtr, errorLength MemLength, gas int64, extraGasForCallback int64) int32 SetAsyncContextCallback(callback MemPtr, callbackLength MemLength, data MemPtr, dataLength MemLength, gas int64) int32 UpgradeContract(destOffset MemPtr, gasLimit int64, valueOffset MemPtr, codeOffset MemPtr, codeMetadataOffset MemPtr, length MemLength, numArguments int32, argumentsLengthOffset MemPtr, dataOffset MemPtr) UpgradeFromSourceContract(destOffset MemPtr, gasLimit int64, valueOffset MemPtr, sourceContractAddressOffset MemPtr, codeMetadataOffset MemPtr, numArguments int32, argumentsLengthOffset MemPtr, dataOffset MemPtr) diff --git a/executor/wrapper/wrapperVMHooks.go b/executor/wrapper/wrapperVMHooks.go index a9fc6de34..91e167d38 100644 --- a/executor/wrapper/wrapperVMHooks.go +++ b/executor/wrapper/wrapperVMHooks.go @@ -203,6 +203,15 @@ func (w *WrapperVMHooks) CreateAsyncCall(destOffset executor.MemPtr, valueOffset return result } +// CreateAsyncV3Call VM hook wrapper +func (w *WrapperVMHooks) CreateAsyncV3Call(destOffset executor.MemPtr, valueOffset executor.MemPtr, dataOffset executor.MemPtr, dataLength executor.MemLength, successOffset executor.MemPtr, successLength executor.MemLength, errorOffset executor.MemPtr, errorLength executor.MemLength, gas int64, extraGasForCallback int64) int32 { + callInfo := fmt.Sprintf("CreateAsyncV3Call(%d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", destOffset, valueOffset, dataOffset, dataLength, successOffset, successLength, errorOffset, errorLength, gas, extraGasForCallback) + w.logger.LogVMHookCallBefore(callInfo) + result := w.wrappedVMHooks.CreateAsyncV3Call(destOffset, valueOffset, dataOffset, dataLength, successOffset, successLength, errorOffset, errorLength, gas, extraGasForCallback) + w.logger.LogVMHookCallAfter(callInfo) + return result +} + // SetAsyncContextCallback VM hook wrapper func (w *WrapperVMHooks) SetAsyncContextCallback(callback executor.MemPtr, callbackLength executor.MemLength, data executor.MemPtr, dataLength executor.MemLength, gas int64) int32 { callInfo := fmt.Sprintf("SetAsyncContextCallback(%d, %d, %d, %d, %d)", callback, callbackLength, data, dataLength, gas) diff --git a/mock/context/executorMockFunc.go b/mock/context/executorMockFunc.go index c79ea34e2..d47fc7fb4 100644 --- a/mock/context/executorMockFunc.go +++ b/mock/context/executorMockFunc.go @@ -30,6 +30,7 @@ var functionNames = map[string]struct{}{ "transferESDTNFTExecute": empty, "multiTransferESDTNFTExecute": empty, "createAsyncCall": empty, + "createAsyncV3Call": empty, "setAsyncContextCallback": empty, "upgradeContract": empty, "upgradeFromSourceContract": empty, diff --git a/scenario/gasSchedules/gasSchedules.go b/scenario/gasSchedules/gasSchedules.go index 807248821..178de9023 100644 --- a/scenario/gasSchedules/gasSchedules.go +++ b/scenario/gasSchedules/gasSchedules.go @@ -1,6 +1,5 @@ package gasschedules -// TODO: go:embed can be used after we upgrade to go 1.16 // import _ "embed" // //go:embed gasScheduleV1.toml diff --git a/test/contracts/erc20/erc20.c b/test/contracts/erc20/erc20.c index 5e3aab34c..0f49c4de6 100644 --- a/test/contracts/erc20/erc20.c +++ b/test/contracts/erc20/erc20.c @@ -44,7 +44,6 @@ void computeAllowanceKey(byte *destination, byte *from, byte* to) { // Note: in smart contract addresses, the first 10 bytes are all 0 // therefore we read from byte 10 onwards to provide more significant bytes // and to minimize the chance for collisions - // TODO: switching to a hash instead of a concatenation of addresses might make it safer for (int i = 0; i < 15; i++) { destination[1+i] = from[10+i]; } diff --git a/vmhost/asyncCall.go b/vmhost/asyncCall.go index 0f0151466..3cc9b4448 100644 --- a/vmhost/asyncCall.go +++ b/vmhost/asyncCall.go @@ -23,21 +23,28 @@ type AsyncCall struct { CallbackClosure []byte IsBuiltinFunctionCall bool + IsAsyncV3 bool + + HasPendingCallback bool + PendingCallbackGasLocked uint64 } // Clone creates a deep clone of the AsyncCall func (ac *AsyncCall) Clone() *AsyncCall { clone := &AsyncCall{ - CallID: ac.CallID, - Status: ac.Status, - ExecutionMode: ac.ExecutionMode, - Destination: make([]byte, len(ac.Destination)), - Data: make([]byte, len(ac.Data)), - GasLimit: ac.GasLimit, - GasLocked: ac.GasLocked, - ValueBytes: make([]byte, len(ac.ValueBytes)), - SuccessCallback: ac.SuccessCallback, - ErrorCallback: ac.ErrorCallback, + CallID: ac.CallID, + Status: ac.Status, + ExecutionMode: ac.ExecutionMode, + Destination: make([]byte, len(ac.Destination)), + Data: make([]byte, len(ac.Data)), + GasLimit: ac.GasLimit, + GasLocked: ac.GasLocked, + ValueBytes: make([]byte, len(ac.ValueBytes)), + SuccessCallback: ac.SuccessCallback, + ErrorCallback: ac.ErrorCallback, + IsAsyncV3: ac.IsAsyncV3, + HasPendingCallback: ac.HasPendingCallback, + PendingCallbackGasLocked: ac.PendingCallbackGasLocked, } copy(clone.Destination, ac.Destination) @@ -105,6 +112,12 @@ func (ac *AsyncCall) HasDefinedAnyCallback() bool { return len(ac.SuccessCallback) > 0 || len(ac.ErrorCallback) > 0 } +// MarkSkippedCallback this async call has skipped calling its callback +func (ac *AsyncCall) MarkSkippedCallback(pendingGasLock uint64) { + ac.HasPendingCallback = true + ac.PendingCallbackGasLocked = pendingGasLock +} + // UpdateStatus sets the status of the async call depending on the provided ReturnCode func (ac *AsyncCall) UpdateStatus(returnCode vmcommon.ReturnCode) { ac.Status = AsyncCallResolved @@ -135,17 +148,20 @@ func (ac *AsyncCall) IsInterfaceNil() bool { func (ac *AsyncCall) toSerializable() *SerializableAsyncCall { return &SerializableAsyncCall{ - CallID: ac.CallID, - Status: SerializableAsyncCallStatus(ac.Status), - ExecutionMode: SerializableAsyncCallExecutionMode(ac.ExecutionMode), - Destination: ac.Destination, - Data: ac.Data, - GasLimit: ac.GasLimit, - GasLocked: ac.GasLocked, - ValueBytes: ac.ValueBytes, - SuccessCallback: ac.SuccessCallback, - ErrorCallback: ac.ErrorCallback, - CallbackClosure: ac.CallbackClosure, + CallID: ac.CallID, + Status: SerializableAsyncCallStatus(ac.Status), + ExecutionMode: SerializableAsyncCallExecutionMode(ac.ExecutionMode), + Destination: ac.Destination, + Data: ac.Data, + GasLimit: ac.GasLimit, + GasLocked: ac.GasLocked, + ValueBytes: ac.ValueBytes, + SuccessCallback: ac.SuccessCallback, + ErrorCallback: ac.ErrorCallback, + CallbackClosure: ac.CallbackClosure, + IsAsyncV3: ac.IsAsyncV3, + HasPendingCallback: ac.HasPendingCallback, + PendingCallbackGasLocked: ac.PendingCallbackGasLocked, } } @@ -159,16 +175,19 @@ func fromSerializableAsyncCalls(serializableAsyncCalls []*SerializableAsyncCall) func (serAsyncCall *SerializableAsyncCall) fromSerializable() *AsyncCall { return &AsyncCall{ - CallID: serAsyncCall.CallID, - Status: AsyncCallStatus(serAsyncCall.Status), - ExecutionMode: AsyncCallExecutionMode(serAsyncCall.ExecutionMode), - Destination: serAsyncCall.Destination, - Data: serAsyncCall.Data, - GasLimit: serAsyncCall.GasLimit, - GasLocked: serAsyncCall.GasLocked, - ValueBytes: serAsyncCall.ValueBytes, - SuccessCallback: serAsyncCall.SuccessCallback, - ErrorCallback: serAsyncCall.ErrorCallback, - CallbackClosure: serAsyncCall.CallbackClosure, + CallID: serAsyncCall.CallID, + Status: AsyncCallStatus(serAsyncCall.Status), + ExecutionMode: AsyncCallExecutionMode(serAsyncCall.ExecutionMode), + Destination: serAsyncCall.Destination, + Data: serAsyncCall.Data, + GasLimit: serAsyncCall.GasLimit, + GasLocked: serAsyncCall.GasLocked, + ValueBytes: serAsyncCall.ValueBytes, + SuccessCallback: serAsyncCall.SuccessCallback, + ErrorCallback: serAsyncCall.ErrorCallback, + CallbackClosure: serAsyncCall.CallbackClosure, + IsAsyncV3: serAsyncCall.IsAsyncV3, + HasPendingCallback: serAsyncCall.HasPendingCallback, + PendingCallbackGasLocked: serAsyncCall.PendingCallbackGasLocked, } } diff --git a/vmhost/asyncCall.pb.go b/vmhost/asyncCall.pb.go index 63b6dd399..8aa8d0afc 100644 --- a/vmhost/asyncCall.pb.go +++ b/vmhost/asyncCall.pb.go @@ -79,17 +79,20 @@ func (SerializableAsyncCallExecutionMode) EnumDescriptor() ([]byte, []int) { } type SerializableAsyncCall struct { - CallID []byte `protobuf:"bytes,1,opt,name=CallID,proto3" json:"CallID,omitempty"` - Status SerializableAsyncCallStatus `protobuf:"varint,2,opt,name=Status,proto3,enum=vmhost.SerializableAsyncCallStatus" json:"Status,omitempty"` - ExecutionMode SerializableAsyncCallExecutionMode `protobuf:"varint,3,opt,name=ExecutionMode,proto3,enum=vmhost.SerializableAsyncCallExecutionMode" json:"ExecutionMode,omitempty"` - Destination []byte `protobuf:"bytes,5,opt,name=Destination,proto3" json:"Destination,omitempty"` - Data []byte `protobuf:"bytes,6,opt,name=Data,proto3" json:"Data,omitempty"` - GasLimit uint64 `protobuf:"varint,7,opt,name=GasLimit,proto3" json:"GasLimit,omitempty"` - GasLocked uint64 `protobuf:"varint,8,opt,name=GasLocked,proto3" json:"GasLocked,omitempty"` - ValueBytes []byte `protobuf:"bytes,9,opt,name=ValueBytes,proto3" json:"ValueBytes,omitempty"` - SuccessCallback string `protobuf:"bytes,10,opt,name=SuccessCallback,proto3" json:"SuccessCallback,omitempty"` - ErrorCallback string `protobuf:"bytes,11,opt,name=ErrorCallback,proto3" json:"ErrorCallback,omitempty"` - CallbackClosure []byte `protobuf:"bytes,12,opt,name=CallbackClosure,proto3" json:"CallbackClosure,omitempty"` + CallID []byte `protobuf:"bytes,1,opt,name=CallID,proto3" json:"CallID,omitempty"` + Status SerializableAsyncCallStatus `protobuf:"varint,2,opt,name=Status,proto3,enum=vmhost.SerializableAsyncCallStatus" json:"Status,omitempty"` + ExecutionMode SerializableAsyncCallExecutionMode `protobuf:"varint,3,opt,name=ExecutionMode,proto3,enum=vmhost.SerializableAsyncCallExecutionMode" json:"ExecutionMode,omitempty"` + Destination []byte `protobuf:"bytes,5,opt,name=Destination,proto3" json:"Destination,omitempty"` + Data []byte `protobuf:"bytes,6,opt,name=Data,proto3" json:"Data,omitempty"` + GasLimit uint64 `protobuf:"varint,7,opt,name=GasLimit,proto3" json:"GasLimit,omitempty"` + GasLocked uint64 `protobuf:"varint,8,opt,name=GasLocked,proto3" json:"GasLocked,omitempty"` + ValueBytes []byte `protobuf:"bytes,9,opt,name=ValueBytes,proto3" json:"ValueBytes,omitempty"` + SuccessCallback string `protobuf:"bytes,10,opt,name=SuccessCallback,proto3" json:"SuccessCallback,omitempty"` + ErrorCallback string `protobuf:"bytes,11,opt,name=ErrorCallback,proto3" json:"ErrorCallback,omitempty"` + CallbackClosure []byte `protobuf:"bytes,12,opt,name=CallbackClosure,proto3" json:"CallbackClosure,omitempty"` + IsAsyncV3 bool `protobuf:"varint,13,opt,name=IsAsyncV3,proto3" json:"IsAsyncV3,omitempty"` + HasPendingCallback bool `protobuf:"varint,14,opt,name=HasPendingCallback,proto3" json:"HasPendingCallback,omitempty"` + PendingCallbackGasLocked uint64 `protobuf:"varint,15,opt,name=PendingCallbackGasLocked,proto3" json:"PendingCallbackGasLocked,omitempty"` } func (m *SerializableAsyncCall) Reset() { *m = SerializableAsyncCall{} } @@ -197,6 +200,27 @@ func (m *SerializableAsyncCall) GetCallbackClosure() []byte { return nil } +func (m *SerializableAsyncCall) GetIsAsyncV3() bool { + if m != nil { + return m.IsAsyncV3 + } + return false +} + +func (m *SerializableAsyncCall) GetHasPendingCallback() bool { + if m != nil { + return m.HasPendingCallback + } + return false +} + +func (m *SerializableAsyncCall) GetPendingCallbackGasLocked() uint64 { + if m != nil { + return m.PendingCallbackGasLocked + } + return 0 +} + type SerializableAsyncCallGroup struct { Callback string `protobuf:"bytes,1,opt,name=Callback,proto3" json:"Callback,omitempty"` GasLocked uint64 `protobuf:"varint,2,opt,name=GasLocked,proto3" json:"GasLocked,omitempty"` @@ -278,43 +302,45 @@ func init() { func init() { proto.RegisterFile("asyncCall.proto", fileDescriptor_a0e9b586d6e1f667) } var fileDescriptor_a0e9b586d6e1f667 = []byte{ - // 564 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0xcd, 0x6e, 0x13, 0x31, - 0x10, 0x5e, 0x27, 0x6d, 0x68, 0xa7, 0x2d, 0x8d, 0x2c, 0x81, 0x4c, 0x69, 0xad, 0x25, 0x20, 0xb4, - 0x8a, 0x44, 0x2a, 0x95, 0x23, 0x42, 0x82, 0xfe, 0x50, 0x55, 0x02, 0xa9, 0xda, 0x08, 0x0e, 0xdc, - 0x9c, 0x5d, 0x37, 0x35, 0xd9, 0xd8, 0xd5, 0xda, 0x5b, 0x08, 0x27, 0x2e, 0xdc, 0x79, 0x0c, 0x5e, - 0x81, 0x37, 0xe8, 0x31, 0xc7, 0x70, 0x23, 0x9b, 0x0b, 0xc7, 0x3e, 0x02, 0x5a, 0x27, 0x59, 0x92, - 0x34, 0x4a, 0x4f, 0x3b, 0xf3, 0xcd, 0x37, 0xdf, 0x37, 0x9e, 0xb5, 0x0c, 0x9b, 0x4c, 0x77, 0x64, - 0x70, 0xc0, 0xa2, 0xa8, 0x76, 0x11, 0x2b, 0xa3, 0x70, 0xe9, 0xb2, 0x7d, 0xae, 0xb4, 0xd9, 0x7a, - 0xd6, 0x14, 0xe6, 0x3c, 0x69, 0xd4, 0x02, 0xd5, 0xde, 0x6d, 0xaa, 0xa6, 0xda, 0xb5, 0xe5, 0x46, - 0x72, 0x66, 0x33, 0x9b, 0xd8, 0x68, 0xd8, 0x56, 0xb9, 0x2a, 0xc2, 0xbd, 0x3a, 0x8f, 0x05, 0x8b, - 0xc4, 0x57, 0xd6, 0x88, 0xf8, 0xeb, 0xb1, 0x2c, 0xbe, 0x0f, 0xa5, 0xec, 0x7b, 0x72, 0x48, 0x90, - 0x8b, 0xbc, 0x75, 0x7f, 0x94, 0xe1, 0x17, 0x50, 0xaa, 0x1b, 0x66, 0x12, 0x4d, 0x0a, 0x2e, 0xf2, - 0xee, 0xee, 0x3d, 0xae, 0x0d, 0x9d, 0x6b, 0x73, 0x65, 0x86, 0x54, 0x7f, 0xd4, 0x82, 0x4f, 0x61, - 0xe3, 0xe8, 0x0b, 0x0f, 0x12, 0x23, 0x94, 0x7c, 0xa7, 0x42, 0x4e, 0x8a, 0x56, 0xa3, 0xba, 0x50, - 0x63, 0xaa, 0xc3, 0x9f, 0x16, 0xc0, 0x2e, 0xac, 0x1d, 0x72, 0x6d, 0x84, 0x64, 0x19, 0x44, 0x96, - 0xed, 0xac, 0x93, 0x10, 0xc6, 0xb0, 0x74, 0xc8, 0x0c, 0x23, 0x25, 0x5b, 0xb2, 0x31, 0xde, 0x82, - 0x95, 0x63, 0xa6, 0xdf, 0x8a, 0xb6, 0x30, 0xe4, 0x8e, 0x8b, 0xbc, 0x25, 0x3f, 0xcf, 0xf1, 0x36, - 0xac, 0x66, 0xb1, 0x0a, 0x5a, 0x3c, 0x24, 0x2b, 0xb6, 0xf8, 0x1f, 0xc0, 0x14, 0xe0, 0x03, 0x8b, - 0x12, 0xbe, 0xdf, 0x31, 0x5c, 0x93, 0x55, 0xab, 0x39, 0x81, 0x60, 0x0f, 0x36, 0xeb, 0x49, 0x10, - 0x70, 0xad, 0xb3, 0xd1, 0x1b, 0x2c, 0x68, 0x11, 0x70, 0x91, 0xb7, 0xea, 0xcf, 0xc2, 0xf8, 0x09, - 0x6c, 0x1c, 0xc5, 0xb1, 0x8a, 0x73, 0xde, 0x9a, 0xe5, 0x4d, 0x83, 0x99, 0xde, 0x38, 0x3e, 0x88, - 0x94, 0x4e, 0x62, 0x4e, 0xd6, 0xad, 0xe9, 0x2c, 0x5c, 0xf9, 0x8d, 0x60, 0x6b, 0xee, 0xfe, 0x8e, - 0x63, 0x95, 0x5c, 0x64, 0x47, 0xce, 0x9d, 0x90, 0x75, 0xca, 0xf3, 0xe9, 0x23, 0x17, 0x66, 0x8f, - 0x5c, 0x81, 0xf5, 0x31, 0xd3, 0x2e, 0xb2, 0x68, 0xfd, 0xa7, 0xb0, 0x6c, 0x2d, 0x27, 0x21, 0x97, - 0x46, 0x9c, 0x09, 0x1e, 0x93, 0x25, 0xab, 0x3f, 0x81, 0xe0, 0x97, 0x00, 0xf9, 0x3c, 0x9a, 0x2c, - 0xbb, 0x45, 0x6f, 0x6d, 0x6f, 0x67, 0xe1, 0x5f, 0xf7, 0x27, 0x1a, 0xaa, 0xdf, 0x11, 0x3c, 0x5c, - 0x70, 0xbf, 0xb0, 0x0b, 0xdb, 0x73, 0xcb, 0xa7, 0x5c, 0x86, 0x42, 0x36, 0xcb, 0x0e, 0x7e, 0x04, - 0x3b, 0xf3, 0x6d, 0xb8, 0x56, 0xd1, 0x25, 0x0f, 0xcb, 0x68, 0x01, 0xe5, 0x13, 0x0f, 0x0c, 0x0f, - 0xcb, 0x85, 0xea, 0x2f, 0x04, 0x95, 0xdb, 0xef, 0x28, 0xde, 0x81, 0x07, 0x93, 0xac, 0x7a, 0x47, - 0x06, 0x39, 0xa1, 0xec, 0xe0, 0x2a, 0x3c, 0xbd, 0x21, 0xb2, 0x9f, 0x88, 0xc8, 0x08, 0xf9, 0x26, - 0x91, 0xc1, 0x89, 0x34, 0x31, 0xab, 0x9f, 0xb3, 0x38, 0x1b, 0xea, 0x16, 0xee, 0x41, 0xac, 0xb4, - 0x1e, 0x72, 0x0b, 0x78, 0x1b, 0xc8, 0x0d, 0xee, 0x7b, 0xd9, 0x92, 0xea, 0xb3, 0x2c, 0x17, 0xf7, - 0x5f, 0x75, 0xfb, 0xd4, 0xe9, 0xf5, 0xa9, 0x73, 0xdd, 0xa7, 0xe8, 0x5b, 0x4a, 0xd1, 0xcf, 0x94, - 0xa2, 0xab, 0x94, 0xa2, 0x6e, 0x4a, 0x51, 0x2f, 0xa5, 0xe8, 0x4f, 0x4a, 0xd1, 0xdf, 0x94, 0x3a, - 0xd7, 0x29, 0x45, 0x3f, 0x06, 0xd4, 0xe9, 0x0e, 0xa8, 0xd3, 0x1b, 0x50, 0xe7, 0xe3, 0xe8, 0x6d, - 0x69, 0x94, 0xec, 0x9b, 0xf1, 0xfc, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xde, 0x7d, 0x58, 0x3d, - 0x7d, 0x04, 0x00, 0x00, + // 608 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0xcd, 0x6e, 0x13, 0x3d, + 0x14, 0x1d, 0x27, 0x69, 0xbe, 0xf6, 0xf6, 0x2f, 0xb2, 0xf4, 0x21, 0x53, 0x5a, 0x6b, 0x08, 0x08, + 0x8d, 0x22, 0x91, 0x4a, 0xed, 0x0e, 0x84, 0x04, 0xfd, 0xa1, 0x44, 0x02, 0xa9, 0x9a, 0x88, 0x2e, + 0xd8, 0x39, 0x33, 0x6e, 0x6a, 0x3a, 0xb5, 0xab, 0xb1, 0xa7, 0x50, 0x56, 0x6c, 0xd8, 0xf3, 0x18, + 0xbc, 0x02, 0x6f, 0xc0, 0xb2, 0x62, 0x55, 0x76, 0x74, 0xb2, 0x61, 0xd9, 0x47, 0x40, 0xe3, 0x24, + 0x93, 0x9f, 0x86, 0x74, 0x95, 0x7b, 0xcf, 0x3d, 0xf7, 0xdc, 0x9b, 0x63, 0x8f, 0x61, 0x99, 0xe9, + 0x73, 0x19, 0x6c, 0xb3, 0x28, 0xaa, 0x9f, 0xc6, 0xca, 0x28, 0x5c, 0x3e, 0x3b, 0x39, 0x52, 0xda, + 0xac, 0x3c, 0x6e, 0x0b, 0x73, 0x94, 0xb4, 0xea, 0x81, 0x3a, 0x59, 0x6f, 0xab, 0xb6, 0x5a, 0xb7, + 0xe5, 0x56, 0x72, 0x68, 0x33, 0x9b, 0xd8, 0xa8, 0xdb, 0x56, 0xfd, 0x59, 0x82, 0xff, 0x9b, 0x3c, + 0x16, 0x2c, 0x12, 0x9f, 0x58, 0x2b, 0xe2, 0x2f, 0xfa, 0xb2, 0xf8, 0x0e, 0x94, 0xb3, 0xdf, 0xc6, + 0x0e, 0x41, 0x2e, 0xf2, 0x16, 0xfc, 0x5e, 0x86, 0x9f, 0x42, 0xb9, 0x69, 0x98, 0x49, 0x34, 0x29, + 0xb8, 0xc8, 0x5b, 0xda, 0x78, 0x50, 0xef, 0x4e, 0xae, 0x4f, 0x94, 0xe9, 0x52, 0xfd, 0x5e, 0x0b, + 0xde, 0x87, 0xc5, 0xdd, 0x8f, 0x3c, 0x48, 0x8c, 0x50, 0xf2, 0x8d, 0x0a, 0x39, 0x29, 0x5a, 0x8d, + 0xda, 0x54, 0x8d, 0x91, 0x0e, 0x7f, 0x54, 0x00, 0xbb, 0x30, 0xbf, 0xc3, 0xb5, 0x11, 0x92, 0x65, + 0x10, 0x99, 0xb1, 0xbb, 0x0e, 0x43, 0x18, 0x43, 0x69, 0x87, 0x19, 0x46, 0xca, 0xb6, 0x64, 0x63, + 0xbc, 0x02, 0xb3, 0x7b, 0x4c, 0xbf, 0x16, 0x27, 0xc2, 0x90, 0xff, 0x5c, 0xe4, 0x95, 0xfc, 0x3c, + 0xc7, 0xab, 0x30, 0x97, 0xc5, 0x2a, 0x38, 0xe6, 0x21, 0x99, 0xb5, 0xc5, 0x01, 0x80, 0x29, 0xc0, + 0x01, 0x8b, 0x12, 0xbe, 0x75, 0x6e, 0xb8, 0x26, 0x73, 0x56, 0x73, 0x08, 0xc1, 0x1e, 0x2c, 0x37, + 0x93, 0x20, 0xe0, 0x5a, 0x67, 0xab, 0xb7, 0x58, 0x70, 0x4c, 0xc0, 0x45, 0xde, 0x9c, 0x3f, 0x0e, + 0xe3, 0x87, 0xb0, 0xb8, 0x1b, 0xc7, 0x2a, 0xce, 0x79, 0xf3, 0x96, 0x37, 0x0a, 0x66, 0x7a, 0xfd, + 0x78, 0x3b, 0x52, 0x3a, 0x89, 0x39, 0x59, 0xb0, 0x43, 0xc7, 0xe1, 0x6c, 0xef, 0x86, 0xb6, 0xa6, + 0x1d, 0x6c, 0x92, 0x45, 0x17, 0x79, 0xb3, 0xfe, 0x00, 0xc0, 0x75, 0xc0, 0xaf, 0x98, 0xde, 0xe7, + 0x32, 0x14, 0xb2, 0x9d, 0x8f, 0x5c, 0xb2, 0xb4, 0x09, 0x15, 0xfc, 0x04, 0xc8, 0x18, 0x34, 0x30, + 0x65, 0xd9, 0x9a, 0xf2, 0xcf, 0x7a, 0xf5, 0x17, 0x82, 0x95, 0x89, 0x27, 0xb9, 0x17, 0xab, 0xe4, + 0x34, 0x33, 0x3f, 0x5f, 0x00, 0xd9, 0xff, 0x9c, 0xe7, 0xa3, 0xe6, 0x17, 0xc6, 0xcd, 0xaf, 0xc2, + 0x42, 0x9f, 0x69, 0x8f, 0xb4, 0x68, 0x9d, 0x18, 0xc1, 0xb2, 0x03, 0x6a, 0x84, 0x5c, 0x1a, 0x71, + 0x28, 0x78, 0x4c, 0x4a, 0x56, 0x7f, 0x08, 0xc1, 0xcf, 0x00, 0xf2, 0x7d, 0x34, 0x99, 0x71, 0x8b, + 0xde, 0xfc, 0xc6, 0xda, 0xd4, 0xfb, 0xe7, 0x0f, 0x35, 0xd4, 0xbe, 0x20, 0xb8, 0x37, 0xe5, 0xa6, + 0x63, 0x17, 0x56, 0x27, 0x96, 0x7b, 0x66, 0x55, 0x1c, 0x7c, 0x1f, 0xd6, 0x26, 0x8f, 0xe1, 0x5a, + 0x45, 0x67, 0x3c, 0xac, 0xa0, 0x29, 0x94, 0xf7, 0x3c, 0x30, 0x3c, 0xac, 0x14, 0x6a, 0xdf, 0x11, + 0x54, 0x6f, 0xff, 0x5a, 0xf0, 0x1a, 0xdc, 0x1d, 0x66, 0x35, 0xcf, 0x65, 0x90, 0x13, 0x2a, 0x0e, + 0xae, 0xc1, 0xa3, 0x1b, 0x22, 0x5b, 0x89, 0x88, 0x8c, 0x90, 0x2f, 0x13, 0x19, 0x34, 0xa4, 0x89, + 0x59, 0xf3, 0x88, 0xc5, 0xd9, 0x52, 0xb7, 0x70, 0xb7, 0x63, 0xa5, 0x75, 0x97, 0x5b, 0xc0, 0xab, + 0x40, 0x6e, 0x70, 0xdf, 0xca, 0x63, 0xa9, 0x3e, 0xc8, 0x4a, 0x71, 0xeb, 0xf9, 0xc5, 0x15, 0x75, + 0x2e, 0xaf, 0xa8, 0x73, 0x7d, 0x45, 0xd1, 0xe7, 0x94, 0xa2, 0x6f, 0x29, 0x45, 0x3f, 0x52, 0x8a, + 0x2e, 0x52, 0x8a, 0x2e, 0x53, 0x8a, 0x7e, 0xa7, 0x14, 0xfd, 0x49, 0xa9, 0x73, 0x9d, 0x52, 0xf4, + 0xb5, 0x43, 0x9d, 0x8b, 0x0e, 0x75, 0x2e, 0x3b, 0xd4, 0x79, 0xd7, 0x7b, 0xe5, 0x5a, 0x65, 0xfb, + 0x7a, 0x6d, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x15, 0xd8, 0x65, 0xd8, 0x07, 0x05, 0x00, 0x00, } func (x SerializableAsyncCallStatus) String() string { @@ -383,6 +409,15 @@ func (this *SerializableAsyncCall) Equal(that interface{}) bool { if !bytes.Equal(this.CallbackClosure, that1.CallbackClosure) { return false } + if this.IsAsyncV3 != that1.IsAsyncV3 { + return false + } + if this.HasPendingCallback != that1.HasPendingCallback { + return false + } + if this.PendingCallbackGasLocked != that1.PendingCallbackGasLocked { + return false + } return true } func (this *SerializableAsyncCallGroup) Equal(that interface{}) bool { @@ -430,7 +465,7 @@ func (this *SerializableAsyncCall) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 15) + s := make([]string, 0, 18) s = append(s, "&vmhost.SerializableAsyncCall{") s = append(s, "CallID: "+fmt.Sprintf("%#v", this.CallID)+",\n") s = append(s, "Status: "+fmt.Sprintf("%#v", this.Status)+",\n") @@ -443,6 +478,9 @@ func (this *SerializableAsyncCall) GoString() string { s = append(s, "SuccessCallback: "+fmt.Sprintf("%#v", this.SuccessCallback)+",\n") s = append(s, "ErrorCallback: "+fmt.Sprintf("%#v", this.ErrorCallback)+",\n") s = append(s, "CallbackClosure: "+fmt.Sprintf("%#v", this.CallbackClosure)+",\n") + s = append(s, "IsAsyncV3: "+fmt.Sprintf("%#v", this.IsAsyncV3)+",\n") + s = append(s, "HasPendingCallback: "+fmt.Sprintf("%#v", this.HasPendingCallback)+",\n") + s = append(s, "PendingCallbackGasLocked: "+fmt.Sprintf("%#v", this.PendingCallbackGasLocked)+",\n") s = append(s, "}") return strings.Join(s, "") } @@ -490,6 +528,31 @@ func (m *SerializableAsyncCall) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.PendingCallbackGasLocked != 0 { + i = encodeVarintAsyncCall(dAtA, i, uint64(m.PendingCallbackGasLocked)) + i-- + dAtA[i] = 0x78 + } + if m.HasPendingCallback { + i-- + if m.HasPendingCallback { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x70 + } + if m.IsAsyncV3 { + i-- + if m.IsAsyncV3 { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x68 + } if len(m.CallbackClosure) > 0 { i -= len(m.CallbackClosure) copy(dAtA[i:], m.CallbackClosure) @@ -682,6 +745,15 @@ func (m *SerializableAsyncCall) Size() (n int) { if l > 0 { n += 1 + l + sovAsyncCall(uint64(l)) } + if m.IsAsyncV3 { + n += 2 + } + if m.HasPendingCallback { + n += 2 + } + if m.PendingCallbackGasLocked != 0 { + n += 1 + sovAsyncCall(uint64(m.PendingCallbackGasLocked)) + } return n } @@ -737,6 +809,9 @@ func (this *SerializableAsyncCall) String() string { `SuccessCallback:` + fmt.Sprintf("%v", this.SuccessCallback) + `,`, `ErrorCallback:` + fmt.Sprintf("%v", this.ErrorCallback) + `,`, `CallbackClosure:` + fmt.Sprintf("%v", this.CallbackClosure) + `,`, + `IsAsyncV3:` + fmt.Sprintf("%v", this.IsAsyncV3) + `,`, + `HasPendingCallback:` + fmt.Sprintf("%v", this.HasPendingCallback) + `,`, + `PendingCallbackGasLocked:` + fmt.Sprintf("%v", this.PendingCallbackGasLocked) + `,`, `}`, }, "") return s @@ -1107,16 +1182,72 @@ func (m *SerializableAsyncCall) Unmarshal(dAtA []byte) error { m.CallbackClosure = []byte{} } iNdEx = postIndex + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsAsyncV3", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAsyncCall + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsAsyncV3 = bool(v != 0) + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HasPendingCallback", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAsyncCall + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.HasPendingCallback = bool(v != 0) + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PendingCallbackGasLocked", wireType) + } + m.PendingCallbackGasLocked = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAsyncCall + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PendingCallbackGasLocked |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipAsyncCall(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAsyncCall - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAsyncCall } if (iNdEx + skippy) > l { @@ -1317,10 +1448,7 @@ func (m *SerializableAsyncCallGroup) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAsyncCall - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAsyncCall } if (iNdEx + skippy) > l { diff --git a/vmhost/asyncCall.proto b/vmhost/asyncCall.proto index f898bec26..4fd6601ff 100644 --- a/vmhost/asyncCall.proto +++ b/vmhost/asyncCall.proto @@ -32,6 +32,9 @@ message SerializableAsyncCall { string SuccessCallback = 10; string ErrorCallback = 11; bytes CallbackClosure = 12; + bool IsAsyncV3 = 13; + bool HasPendingCallback = 14; + uint64 PendingCallbackGasLocked = 15; } message SerializableAsyncCallGroup { diff --git a/vmhost/contexts/async.go b/vmhost/contexts/async.go index b90e87a0c..25187375f 100644 --- a/vmhost/contexts/async.go +++ b/vmhost/contexts/async.go @@ -183,7 +183,7 @@ func (context *asyncContext) PushState() { callbackData: context.callbackData, gasAccumulated: context.gasAccumulated, returnData: context.returnData, - asyncCallGroups: context.asyncCallGroups, // TODO matei-p use cloneCallGroups()? + asyncCallGroups: context.cloneCallGroups(), callType: context.callType, callbackAsyncInitiatorCallID: context.callbackAsyncInitiatorCallID, @@ -624,7 +624,7 @@ func (context *asyncContext) addAsyncCall(groupID string, call *vmhost.AsyncCall call.CallID = context.generateNewCallID() } - if context.isMultiLevelAsync(call) { + if context.isMultiLevelAsync(call) && !call.IsAsyncV3 { return vmhost.ErrAsyncNoMultiLevel } @@ -864,7 +864,7 @@ func (context *asyncContext) callCallback(callID []byte, vmOutput *vmcommon.VMOu } context.host.Metering().DisableRestoreGas() - isComplete, callbackVMOutput := loadedContext.ExecuteSyncCallbackAndFinishOutput(asyncCall, vmOutput, nil, gasAccumulated, err) + isComplete, callbackVMOutput := loadedContext.ExecuteLocalCallbackAndFinishOutput(asyncCall, vmOutput, nil, gasAccumulated, err) context.host.Metering().EnableRestoreGas() return isComplete, callbackVMOutput, nil } diff --git a/vmhost/contexts/asyncComposability.go b/vmhost/contexts/asyncComposability.go index 495aa30ff..6ec53922f 100644 --- a/vmhost/contexts/asyncComposability.go +++ b/vmhost/contexts/asyncComposability.go @@ -63,31 +63,34 @@ func (context *asyncContext) complete() error { return nil } + gasToAccumulate := context.gasAccumulated + notifyChildComplete := true currentCallID := context.GetCallID() - if context.callType == vm.AsynchronousCall { + switch context.callType { + case vm.AsynchronousCall: vmOutput := context.childResults - isCallbackComplete, _, err := context.callCallback(currentCallID, vmOutput, nil) + notifyChildComplete, _, err = context.callCallback(currentCallID, vmOutput, nil) if err != nil { return err } - if isCallbackComplete { - return context.NotifyChildIsComplete(currentCallID, 0) - } - } else if context.callType == vm.AsynchronousCallBack { + gasToAccumulate = 0 + case vm.AsynchronousCallBack: err = context.LoadParentContext() if err != nil { return err } - currentCallID := context.GetCallerCallID() - return context.NotifyChildIsComplete(currentCallID, context.gasAccumulated) - } else if context.callType == vm.DirectCall { + currentCallID = context.GetCallerCallID() + case vm.DirectCall: err = context.LoadParentContext() if err != nil { return err } + currentCallID = nil + } - return context.NotifyChildIsComplete(nil, context.gasAccumulated) + if notifyChildComplete { + return context.NotifyChildIsComplete(currentCallID, gasToAccumulate) } return nil diff --git a/vmhost/contexts/asyncLocal.go b/vmhost/contexts/asyncLocal.go index 9b59bf6fe..92929422b 100644 --- a/vmhost/contexts/asyncLocal.go +++ b/vmhost/contexts/asyncLocal.go @@ -16,12 +16,24 @@ type lastTransferInfo struct { func (context *asyncContext) executeAsyncLocalCalls() error { localCalls := make([]*vmhost.AsyncCall, 0) + hasAnyRemoteCallbacks := false for _, group := range context.asyncCallGroups { for _, call := range group.AsyncCalls { if call.IsLocal() { localCalls = append(localCalls, call) } + + if call.IsRemote() && call.HasCallback() { + hasAnyRemoteCallbacks = true + } + } + } + + if hasAnyRemoteCallbacks { + currentCall := context.GetAsyncCallByCallID(context.GetCallID()).GetAsyncCall() + if currentCall != nil && currentCall.IsAsyncV3 { + currentCall.MarkSkippedCallback(currentCall.GetGasLocked()) } } @@ -35,7 +47,6 @@ func (context *asyncContext) executeAsyncLocalCalls() error { return nil } -// TODO split this method into smaller ones func (context *asyncContext) executeAsyncLocalCall(asyncCall *vmhost.AsyncCall) error { destinationCallInput, err := context.createContractCallInput(asyncCall) if err != nil { @@ -79,44 +90,51 @@ func (context *asyncContext) executeAsyncLocalCall(asyncCall *vmhost.AsyncCall) asyncCall.UpdateStatus(vmOutput.ReturnCode) if isComplete { - if asyncCall.HasCallback() { - // Restore gas locked while still on the caller instance; otherwise, the - // locked gas will appear to have been used twice by the caller instance. - isCallbackComplete, callbackVMOutput := context.ExecuteSyncCallbackAndFinishOutput(asyncCall, vmOutput, destinationCallInput, 0, err) - if callbackVMOutput == nil { - return vmhost.ErrAsyncNoOutputFromCallback - } + return context.executeAsyncCallbackAndComplete(asyncCall, vmOutput, destinationCallInput, err) + } - context.host.CompleteLogEntriesWithCallType(callbackVMOutput, vmhost.AsyncCallbackString) + return nil +} - if isCallbackComplete { - callbackGasRemaining := callbackVMOutput.GasRemaining - callbackVMOutput.GasRemaining = 0 - return context.completeChild(asyncCall.CallID, callbackGasRemaining) - } - } else { - return context.completeChild(asyncCall.CallID, 0) +func (context *asyncContext) executeAsyncCallbackAndComplete( + asyncCall *vmhost.AsyncCall, + destinationVMOutput *vmcommon.VMOutput, + destinationCallInput *vmcommon.ContractCallInput, + destinationErr error, +) error { + callbackGasRemaining := uint64(0) + if asyncCall.HasCallback() { + // Restore gas locked while still on the caller instance; otherwise, the + // locked gas will appear to have been used twice by the caller instance. + isCallbackComplete, callbackVMOutput := context.ExecuteLocalCallbackAndFinishOutput(asyncCall, destinationVMOutput, destinationCallInput, 0, destinationErr) + if callbackVMOutput == nil { + return vmhost.ErrAsyncNoOutputFromCallback + } + + context.host.CompleteLogEntriesWithCallType(callbackVMOutput, vmhost.AsyncCallbackString) + + if isCallbackComplete { + callbackGasRemaining = callbackVMOutput.GasRemaining + callbackVMOutput.GasRemaining = 0 } } - return nil + return context.completeChild(asyncCall.CallID, callbackGasRemaining) } -// ExecuteSyncCallbackAndFinishOutput executes the callback and finishes the output -// TODO rename to executeLocalCallbackAndFinishOutput -func (context *asyncContext) ExecuteSyncCallbackAndFinishOutput( +// ExecuteLocalCallbackAndFinishOutput executes the callback and finishes the output +func (context *asyncContext) ExecuteLocalCallbackAndFinishOutput( asyncCall *vmhost.AsyncCall, vmOutput *vmcommon.VMOutput, _ *vmcommon.ContractCallInput, gasAccumulated uint64, err error) (bool, *vmcommon.VMOutput) { - callbackVMOutput, isComplete, _ := context.executeSyncCallback(asyncCall, vmOutput, gasAccumulated, err) + callbackVMOutput, isComplete, _ := context.executeLocalCallback(asyncCall, vmOutput, gasAccumulated, err) context.finishAsyncLocalCallbackExecution() return isComplete, callbackVMOutput } -// TODO rename to executeLocalCallback -func (context *asyncContext) executeSyncCallback( +func (context *asyncContext) executeLocalCallback( asyncCall *vmhost.AsyncCall, destinationVMOutput *vmcommon.VMOutput, gasAccumulated uint64, @@ -124,11 +142,11 @@ func (context *asyncContext) executeSyncCallback( ) (*vmcommon.VMOutput, bool, error) { callbackInput, err := context.createCallbackInput(asyncCall, destinationVMOutput, gasAccumulated, destinationErr) if err != nil { - logAsync.Trace("executeSyncCallback", "error", err) + logAsync.Trace("executeLocalCallback", "error", err) return nil, true, err } - logAsync.Trace("executeSyncCallback", + logAsync.Trace("executeLocalCallback", "caller", callbackInput.CallerAddr, "dest", callbackInput.RecipientAddr, "func", callbackInput.Function, @@ -183,7 +201,7 @@ func (context *asyncContext) executeSyncHalfOfBuiltinFunction(asyncCall *vmhost. if vmOutput.ReturnCode != vmcommon.Ok { asyncCall.Reject() if asyncCall.HasCallback() { - _, _, _ = context.executeSyncCallback(asyncCall, vmOutput, 0, err) + _, _, _ = context.executeLocalCallback(asyncCall, vmOutput, 0, err) context.finishAsyncLocalCallbackExecution() } } @@ -240,7 +258,6 @@ func (context *asyncContext) createContractCallInput(asyncCall *vmhost.AsyncCall return contractCallInput, nil } -// TODO function too large; refactor needed func (context *asyncContext) createCallbackInput( asyncCall *vmhost.AsyncCall, vmOutput *vmcommon.VMOutput, @@ -255,14 +272,12 @@ func (context *asyncContext) createCallbackInput( } arguments := context.getArgumentsForCallback(vmOutput, destinationErr) - returnWithError := false if destinationErr != nil || vmOutput.ReturnCode != vmcommon.Ok { returnWithError = true } callbackFunction := asyncCall.GetCallbackName() - dataLength := computeDataLengthFromArguments(callbackFunction, arguments) gasLimit, err := context.computeGasLimitForCallback(asyncCall, vmOutput, dataLength) if err != nil { @@ -270,9 +285,8 @@ func (context *asyncContext) createCallbackInput( } originalCaller := runtime.GetOriginalCallerAddress() - caller := context.address - lastTransferInfo := context.extractLastTransferWithoutData(caller, vmOutput) + lastTransferData := context.extractLastTransferWithoutData(caller, vmOutput) // Return to the sender SC, calling its specified callback method. contractCallInput := &vmcommon.ContractCallInput{ @@ -280,7 +294,7 @@ func (context *asyncContext) createCallbackInput( OriginalCallerAddr: originalCaller, CallerAddr: actualCallbackInitiator, Arguments: arguments, - CallValue: lastTransferInfo.callValue, + CallValue: lastTransferData.callValue, CallType: vm.AsynchronousCallBack, GasPrice: runtime.GetVMInput().GasPrice, GasProvided: gasLimit, @@ -289,7 +303,7 @@ func (context *asyncContext) createCallbackInput( OriginalTxHash: runtime.GetOriginalTxHash(), PrevTxHash: runtime.GetPrevTxHash(), ReturnCallAfterError: returnWithError, - ESDTTransfers: lastTransferInfo.lastESDTTransfers, + ESDTTransfers: lastTransferData.lastESDTTransfers, }, RecipientAddr: caller, Function: callbackFunction, diff --git a/vmhost/contexts/asyncParams.go b/vmhost/contexts/asyncParams.go index 15ec142e1..f60d75960 100644 --- a/vmhost/contexts/asyncParams.go +++ b/vmhost/contexts/asyncParams.go @@ -8,31 +8,23 @@ import ( vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/multiversx/mx-chain-vm-common-go/txDataBuilder" "github.com/multiversx/mx-chain-vm-go/crypto" - "github.com/multiversx/mx-chain-vm-go/vmhost" ) -/* - Called to process OutputTransfers created by a - direct call (on dest) builtin function call by the VM -*/ +// AddAsyncArgumentsToOutputTransfers +// Called to process OutputTransfers created by a +// direct call (on dest) builtin function call by the VM func AddAsyncArgumentsToOutputTransfers( - output vmhost.OutputContext, - address []byte, asyncParams *vmcommon.AsyncArguments, callType vm.CallType, - vmOutput *vmcommon.VMOutput) error { + vmOutput *vmcommon.VMOutput, +) error { if asyncParams == nil { return nil } + for _, outAcc := range vmOutput.OutputAccounts { - // if !bytes.Equal(address, outAcc.Address) { - // continue - // } for t, outTransfer := range outAcc.OutputTransfers { - // if !bytes.Equal(address, outTransfer.SenderAddress) { - // continue - // } if outTransfer.CallType != callType { continue } @@ -85,13 +77,13 @@ func createDataFromAsyncParams( } /* - Called when a SCR for a callback is created outside the VM - (by createAsyncCallBackSCRFromVMOutput()) - This is the case - A) after an async call executed following a builtin function call, - B) other cases where processing the output trasnfers of a VMOutput did - not produce a SCR of type AsynchronousCallBack - TODO(check): function not used? + Called when a SCR for a callback is created outside the VM + (by createAsyncCallBackSCRFromVMOutput()) + This is the case + A) after an async call executed following a builtin function call, + B) other cases where processing the output trasnfers of a VMOutput did + not produce a SCR of type AsynchronousCallBack + TODO(check): function not used? */ func AppendAsyncArgumentsToCallbackCallData( hasher crypto.Hasher, @@ -107,10 +99,10 @@ func AppendAsyncArgumentsToCallbackCallData( } /* - Called when a SCR is created from VMOutput in order to recompose - async data and call data into a transfer data ready for the SCR - (by preprocessOutTransferToSCR()) - TODO(check): function not used? + Called when a SCR is created from VMOutput in order to recompose + async data and call data into a transfer data ready for the SCR + (by preprocessOutTransferToSCR()) + TODO(check): function not used? */ func AppendTransferAsyncDataToCallData( callData []byte, @@ -182,11 +174,11 @@ func appendAsyncParamsToCallData( } /* - Used by when a callback SCR is created - 1) after a failure of an async call - Async data is extracted (by extractAsyncCallParamsFromTxData()) and then - reappended to the new SCR's callback data (by reapendAsyncParamsToTxData()) - 2) from the last transfer (see useLastTransferAsAsyncCallBackWhenNeeded()) +Used by when a callback SCR is created + 1. after a failure of an async call + Async data is extracted (by extractAsyncCallParamsFromTxData()) and then + reappended to the new SCR's callback data (by reapendAsyncParamsToTxData()) + 2. from the last transfer (see useLastTransferAsAsyncCallBackWhenNeeded()) */ func CreateCallbackAsyncParams(hasher crypto.Hasher, asyncParams *vmcommon.AsyncArguments) [][]byte { if asyncParams == nil { diff --git a/vmhost/contexts/async_test.go b/vmhost/contexts/async_test.go index 579693bdc..79f2e26fc 100644 --- a/vmhost/contexts/async_test.go +++ b/vmhost/contexts/async_test.go @@ -2,10 +2,12 @@ package contexts import ( "errors" - "github.com/multiversx/mx-chain-vm-go/wasmer2" "math/big" "testing" + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-vm-go/wasmer2" + "github.com/multiversx/mx-chain-core-go/data/vm" "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-scenario-go/worldmock" @@ -499,6 +501,79 @@ func TestAsyncContext_UpdateCurrentCallStatus(t *testing.T) { require.Equal(t, vmhost.AsyncCallRejected, asyncCall.Status) } +func TestAsyncContext_OutputInCaseOfErrorInCallback(t *testing.T) { + user := []byte("user") + contractA := []byte("contractA") + contractB := []byte("contractB") + + host, _ := initializeVMAndWasmerAsyncContext(t) + host.EnableEpochsHandlerField = &worldmock.EnableEpochsHandlerStub{ + IsFlagEnabledCalled: func(flag core.EnableEpochFlag) bool { + return flag == vmhost.AsyncV3Flag + }, + } + + async := makeAsyncContext(t, host, contractA) + host.Storage().SetAddress(contractA) + host.AsyncContext = async + + vmInput := &vmcommon.ContractCallInput{ + VMInput: vmcommon.VMInput{ + CallerAddr: user, + Arguments: [][]byte{{0}}, + CallType: vm.DirectCall, + }, + RecipientAddr: contractA, + } + host.Runtime().InitStateFromContractCallInput(vmInput) + + err := async.RegisterAsyncCall("", &vmhost.AsyncCall{ + Destination: contractB, + Data: []byte("function"), + }) + require.Nil(t, err) + + err = async.Save() + require.Nil(t, err) + + asyncCallId := async.GetCallID() + asyncStoragePrefix := host.Storage().GetVmProtectedPrefix(vmhost.AsyncDataPrefix) + asyncCallKey := vmhost.CustomStorageKey(string(asyncStoragePrefix), asyncCallId) + + data, _, _, _ := host.Storage().GetStorageUnmetered(asyncCallKey) + require.NotEqual(t, len(data), 0) + + vmInput = &vmcommon.ContractCallInput{ + VMInput: vmcommon.VMInput{ + CallerAddr: contractB, + Arguments: [][]byte{{0}}, + CallType: vm.AsynchronousCallBack, + }, + RecipientAddr: contractA, + } + host.Runtime().InitStateFromContractCallInput(vmInput) + + async.callbackAsyncInitiatorCallID = asyncCallId + async.callType = vmInput.CallType + err = async.LoadParentContext() + require.Nil(t, err) + + vmOutput := host.Output().CreateVMOutputInCaseOfError(vmhost.ErrNotEnoughGas) + outputAccount := vmOutput.OutputAccounts[string(contractA)] + + require.NotNil(t, outputAccount) + + storageUpdates := outputAccount.StorageUpdates + require.Equal(t, len(storageUpdates), 1) + + asyncContextDeletionUpdate := storageUpdates[string(asyncCallKey)] + require.NotNil(t, asyncContextDeletionUpdate) + require.Equal(t, len(asyncContextDeletionUpdate.Data), 0) + + data, _, _, _ = host.Storage().GetStorageUnmetered(asyncCallKey) + require.Equal(t, len(data), 0) +} + func TestAsyncContext_SendAsyncCallCrossShard(t *testing.T) { host, world := initializeVMAndWasmerAsyncContext(t) world.AcctMap.PutAccount(&worldmock.Account{ diff --git a/vmhost/contexts/output.go b/vmhost/contexts/output.go index fb77b9141..fb695f81d 100644 --- a/vmhost/contexts/output.go +++ b/vmhost/contexts/output.go @@ -567,14 +567,47 @@ func (context *outputContext) DeployCode(input vmhost.CodeDeployInput) { context.codeUpdates[string(input.ContractAddress)] = empty } +// createVMOutputInCaseOfErrorOfAsyncCallback appends the deletion of the async context to the output +func (context *outputContext) createVMOutputInCaseOfErrorOfAsyncCallback(returnCode vmcommon.ReturnCode, returnMessage string) *vmcommon.VMOutput { + async := context.host.Async() + metering := context.host.Metering() + + callId := async.GetCallbackAsyncInitiatorCallID() + + context.outputState = &vmcommon.VMOutput{ + GasRemaining: 0, + GasRefund: big.NewInt(0), + ReturnCode: returnCode, + ReturnMessage: returnMessage, + OutputAccounts: make(map[string]*vmcommon.OutputAccount), + } + + err := async.DeleteFromCallID(callId) + if err != nil { + logOutput.Trace("failed to delete Async Context", "callId", callId, "err", err) + } + + metering.UpdateGasStateOnFailure(context.outputState) + + return context.outputState +} + // CreateVMOutputInCaseOfError creates a new vmOutput with the given error set as return message. func (context *outputContext) CreateVMOutputInCaseOfError(err error) *vmcommon.VMOutput { runtime := context.host.Runtime() + metering := context.host.Metering() + + callType := runtime.GetVMInput().CallType + runtime.AddError(err, runtime.FunctionName()) returnCode := context.resolveReturnCodeFromError(err) returnMessage := context.resolveReturnMessageFromError(err) + if context.host.EnableEpochsHandler().IsFlagEnabled(vmhost.AsyncV3Flag) && callType == vm.AsynchronousCallBack { + return context.createVMOutputInCaseOfErrorOfAsyncCallback(returnCode, returnMessage) + } + vmOutput := &vmcommon.VMOutput{ GasRemaining: 0, GasRefund: big.NewInt(0), @@ -582,7 +615,7 @@ func (context *outputContext) CreateVMOutputInCaseOfError(err error) *vmcommon.V ReturnMessage: returnMessage, } - context.host.Metering().UpdateGasStateOnFailure(vmOutput) + metering.UpdateGasStateOnFailure(vmOutput) return vmOutput } diff --git a/vmhost/flags.go b/vmhost/flags.go index 911c22c82..cf6541fd0 100644 --- a/vmhost/flags.go +++ b/vmhost/flags.go @@ -3,6 +3,45 @@ package vmhost import "github.com/multiversx/mx-chain-core-go/core" const ( + // MultiESDTTransferFixOnCallBackFlag defines the flag that activates the multi esdt transfer fix on callback + MultiESDTTransferFixOnCallBackFlag core.EnableEpochFlag = "MultiESDTTransferFixOnCallBackFlag" + + // RemoveNonUpdatedStorageFlag defines the flag that activates the remove non updated storage fix + RemoveNonUpdatedStorageFlag core.EnableEpochFlag = "RemoveNonUpdatedStorageFlag" + + // CreateNFTThroughExecByCallerFlag defines the flag that activates the create nft through exec by caller fix + CreateNFTThroughExecByCallerFlag core.EnableEpochFlag = "CreateNFTThroughExecByCallerFlag" + + // StorageAPICostOptimizationFlag defines the flag that activates the storage api cost optimization + StorageAPICostOptimizationFlag core.EnableEpochFlag = "StorageAPICostOptimizationFlag" + + // CheckExecuteOnReadOnlyFlag defines the flag that activates the check execute on read only + CheckExecuteOnReadOnlyFlag core.EnableEpochFlag = "CheckExecuteOnReadOnlyFlag" + + // FailExecutionOnEveryAPIErrorFlag defines the flag that activates the fail execution on every api error + FailExecutionOnEveryAPIErrorFlag core.EnableEpochFlag = "FailExecutionOnEveryAPIErrorFlag" + + // ManagedCryptoAPIsFlag defines the flag that activates the manage crypto apis + ManagedCryptoAPIsFlag core.EnableEpochFlag = "ManagedCryptoAPIsFlag" + + // DisableExecByCallerFlag defines the flag that activates disable exec by caller + DisableExecByCallerFlag core.EnableEpochFlag = "DisableExecByCallerFlag" + + // RefactorContextFlag defines the flag that activates the refactor context + RefactorContextFlag core.EnableEpochFlag = "RefactorContextFlag" + + // RuntimeMemStoreLimitFlag defines the flag that activates the runtime mem store limit + RuntimeMemStoreLimitFlag core.EnableEpochFlag = "RuntimeMemStoreLimitFlag" + + // RuntimeCodeSizeFixFlag defines the flag that activates the runtime code size fix + RuntimeCodeSizeFixFlag core.EnableEpochFlag = "RuntimeCodeSizeFixFlag" + + // FixOOGReturnCodeFlag defines the flag that activates the fix oog return code + FixOOGReturnCodeFlag core.EnableEpochFlag = "FixOOGReturnCodeFlag" + + // DynamicGasCostForDataTrieStorageLoadFlag defines the flag that activates the dynamic gas cost for data trie storage load + DynamicGasCostForDataTrieStorageLoadFlag core.EnableEpochFlag = "DynamicGasCostForDataTrieStorageLoadFlag" + // CryptoOpcodesV2Flag defines the flag that activates the new crypto APIs for RC1.7 CryptoOpcodesV2Flag core.EnableEpochFlag = "CryptoOpcodesV2Flag" @@ -12,6 +51,9 @@ const ( // UseGasBoundedShouldFailExecutionFlag defines the flag that activates failing of execution if gas bounded check fails UseGasBoundedShouldFailExecutionFlag core.EnableEpochFlag = "UseGasBoundedShouldFailExecutionFlag" + // AsyncV3Flag defines the flag that activates async v3 + AsyncV3Flag core.EnableEpochFlag = "AsyncV3Flag" + // CheckBuiltInCallOnTransferValueAndFailExecutionFlag defines the flag that activates failing of execution if gas bounded check fails CheckBuiltInCallOnTransferValueAndFailExecutionFlag core.EnableEpochFlag = "CheckBuiltInCallOnTransferValueAndFailExecutionFlag" diff --git a/vmhost/hostCore/execution.go b/vmhost/hostCore/execution.go index eb1bf9f21..e50026e03 100644 --- a/vmhost/hostCore/execution.go +++ b/vmhost/hostCore/execution.go @@ -412,8 +412,6 @@ func (host *vmHost) handleBuiltinFunctionCall(input *vmcommon.ContractCallInput) } err = contexts.AddAsyncArgumentsToOutputTransfers( - host.Output(), - input.RecipientAddr, input.AsyncArguments, vm.AsynchronousCall, builtinOutput) @@ -1150,6 +1148,14 @@ func (host *vmHost) checkFinalGasAfterExit() error { return nil } +func (host *vmHost) checkValidFunctionName(name string) error { + if name == "" { + return executor.ErrInvalidFunction + } + + return nil +} + func (host *vmHost) callInitFunction() error { return host.callSCFunction(vmhost.InitFunctionName) } @@ -1159,12 +1165,18 @@ func (host *vmHost) callUpgradeFunction() error { } func (host *vmHost) callSCFunction(functionName string) error { + err := host.checkValidFunctionName(functionName) + if err != nil { + log.Trace("call SC method failed", "error", err, "src", "checkValidFunctionName") + return err + } + runtime := host.Runtime() if !runtime.HasFunction(functionName) { return executor.ErrFuncNotFound } - err := runtime.CallSCFunction(functionName) + err = runtime.CallSCFunction(functionName) if err != nil { err = host.handleBreakpointIfAny(err) } @@ -1229,6 +1241,10 @@ func (host *vmHost) callSCMethodAsynchronousCallBack() error { return nil } + if asyncCall.HasPendingCallback { + return nil + } + async.SetCallbackParentCall(asyncCall) if asyncCall.HasCallback() { @@ -1241,12 +1257,6 @@ func (host *vmHost) callSCMethodAsynchronousCallBack() error { _ = metering.UseGasBounded(metering.GasLeft()) } - // TODO matei-p R2 Returning an error here will cause the VMOutput to be - // empty (due to CreateVMOutputInCaseOfError()). But in release 2 of - // Promises, CreateVMOutputInCaseOfError() should still contain storage - // deletions caused by AsyncContext cleanup, even if callbackErr != nil and - // was returned here. The storage deletions MUST be persisted in the data - // trie once R2 goes live. if !isCallComplete { return callbackErr } @@ -1268,47 +1278,47 @@ func (host *vmHost) callFunctionAndExecuteAsync() (bool, error) { runtime := host.Runtime() async := host.Async() - // TODO refactor this, and apply this condition in other places where a - // function is called - if runtime.FunctionName() != "" { - err := host.verifyAllowedFunctionCall() - if err != nil { - log.Trace("call SC method failed", "error", err, "src", "verifyAllowedFunctionCall") - return false, err - } + err := host.checkValidFunctionName(runtime.FunctionName()) + if err != nil { + log.Trace("call SC method failed", "error", err, "src", "checkValidFunctionName") + return false, err + } - functionName, err := runtime.FunctionNameChecked() - if err != nil { - log.Trace("call SC method failed", "error", err, "src", "FunctionNameChecked") - return false, err - } + err = host.verifyAllowedFunctionCall() + if err != nil { + log.Trace("call SC method failed", "error", err, "src", "verifyAllowedFunctionCall") + return false, err + } - err = runtime.CallSCFunction(functionName) - if err != nil { - err = host.handleBreakpointIfAny(err) - log.Trace("breakpoint detected and handled", "err", err) - } - if err == nil { - err = host.checkFinalGasAfterExit() - } - if err != nil { - log.Trace("call SC method failed", "error", err, "src", "sc function") - return true, err - } + functionName, err := runtime.FunctionNameChecked() + if err != nil { + log.Trace("call SC method failed", "error", err, "src", "FunctionNameChecked") + return false, err + } - err = async.Execute() - if err != nil { - log.Trace("call SC method failed", "error", err, "src", "async execution") - return false, err - } + err = runtime.CallSCFunction(functionName) + if err != nil { + err = host.handleBreakpointIfAny(err) + log.Trace("breakpoint detected and handled", "err", err) + } + if err == nil { + err = host.checkFinalGasAfterExit() + } + if err != nil { + log.Trace("call SC method failed", "error", err, "src", "sc function") + return true, err + } - if !async.IsComplete() || async.HasLegacyGroup() { - async.SetResults(host.Output().GetVMOutput()) - err = async.Save() - return false, err - } - } else { - return false, executor.ErrInvalidFunction + err = async.Execute() + if err != nil { + log.Trace("call SC method failed", "error", err, "src", "async execution") + return false, err + } + + if !async.IsComplete() || async.HasLegacyGroup() { + async.SetResults(host.Output().GetVMOutput()) + err = async.Save() + return false, err } return true, nil diff --git a/vmhost/hostCore/host.go b/vmhost/hostCore/host.go index b2a3fdffc..a718badba 100644 --- a/vmhost/hostCore/host.go +++ b/vmhost/hostCore/host.go @@ -42,6 +42,7 @@ var allFlags = []core.EnableEpochFlag{ vmhost.CryptoOpcodesV2Flag, vmhost.MultiESDTNFTTransferAndExecuteByUserFlag, vmhost.UseGasBoundedShouldFailExecutionFlag, + vmhost.AsyncV3Flag, vmhost.CheckBuiltInCallOnTransferValueAndFailExecutionFlag, vmhost.ValidationOnGobDecodeFlag, vmhost.BarnardOpcodesFlag, @@ -99,7 +100,7 @@ func NewVMHost( if check.IfNil(hostParameters.EnableEpochsHandler) { return nil, vmhost.ErrNilEnableEpochsHandler } - err := core.CheckHandlerCompatibility(hostParameters.EnableEpochsHandler, allFlags) + err := core.CheckHandlerCompatibility(hostParameters.EnableEpochsHandler, []core.EnableEpochFlag{}) if err != nil { return nil, err } diff --git a/vmhost/hostCore/host_test.go b/vmhost/hostCore/host_test.go index 21e9f4804..536c2f546 100644 --- a/vmhost/hostCore/host_test.go +++ b/vmhost/hostCore/host_test.go @@ -4,7 +4,6 @@ import ( "math" "testing" - "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-scenario-go/worldmock" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/multiversx/mx-chain-vm-common-go/builtInFunctions" @@ -73,17 +72,6 @@ func TestNewVMHost(t *testing.T) { require.Nil(t, host) require.ErrorIs(t, err, vmhost.ErrNilEnableEpochsHandler) }) - t.Run("InvalidEnableEpochsHandler", func(t *testing.T) { - hostParameters := makeHostParameters() - hostParameters.EnableEpochsHandler = &worldmock.EnableEpochsHandlerStub{ - IsFlagDefinedCalled: func(flag core.EnableEpochFlag) bool { - return false - }, - } - host, err := NewVMHost(blockchainHook, hostParameters) - require.Nil(t, host) - require.ErrorIs(t, err, core.ErrInvalidEnableEpochsHandler) - }) t.Run("NilHasher", func(t *testing.T) { hostParameters := makeHostParameters() hostParameters.Hasher = nil diff --git a/vmhost/interface.go b/vmhost/interface.go index 1f12afbfa..1d67f2ccf 100644 --- a/vmhost/interface.go +++ b/vmhost/interface.go @@ -400,7 +400,7 @@ type AsyncContext interface { GetAsyncCallByCallID(callID []byte) AsyncCallLocation LoadParentContextFromStackOrStorage() (AsyncContext, error) - ExecuteSyncCallbackAndFinishOutput( + ExecuteLocalCallbackAndFinishOutput( asyncCall *AsyncCall, vmOutput *vmcommon.VMOutput, destinationCallInput *vmcommon.ContractCallInput, diff --git a/vmhost/vmhooks/baseOps.go b/vmhost/vmhooks/baseOps.go index c2e1f2aa8..ec7329fee 100644 --- a/vmhost/vmhooks/baseOps.go +++ b/vmhost/vmhooks/baseOps.go @@ -31,6 +31,7 @@ const ( multiTransferESDTNFTExecuteName = "multiTransferESDTNFTExecute" transferValueExecuteName = "transferValueExecute" createAsyncCallName = "createAsyncCall" + createAsyncV3CallName = "createAsyncV3Call" setAsyncGroupCallbackName = "setAsyncGroupCallback" setAsyncContextCallbackName = "setAsyncContextCallback" getArgumentLengthName = "getArgumentLength" @@ -1452,6 +1453,144 @@ func CreateAsyncCallWithTypedArgs(host vmhost.VMHost, return 0 } +// CreateAsyncV3Call VMHooks implementation. +// @autogenerate(VMHooks) +func (context *VMHooksImpl) CreateAsyncV3Call( + destOffset executor.MemPtr, + valueOffset executor.MemPtr, + dataOffset executor.MemPtr, + dataLength executor.MemLength, + successOffset executor.MemPtr, + successLength executor.MemLength, + errorOffset executor.MemPtr, + errorLength executor.MemLength, + gas int64, + extraGasForCallback int64, +) int32 { + host := context.GetVMHost() + return context.CreateAsyncV3CallWithHost( + host, + destOffset, + valueOffset, + dataOffset, + dataLength, + successOffset, + successLength, + errorOffset, + errorLength, + gas, + extraGasForCallback) +} + +// CreateAsyncV3CallWithHost - createAsyncV3Call with host instead of pointer +func (context *VMHooksImpl) CreateAsyncV3CallWithHost(host vmhost.VMHost, + destOffset executor.MemPtr, + valueOffset executor.MemPtr, + dataOffset executor.MemPtr, + dataLength executor.MemLength, + successOffset executor.MemPtr, + successLength executor.MemLength, + errorOffset executor.MemPtr, + errorLength executor.MemLength, + gas int64, + extraGasForCallback int64, +) int32 { + calledSCAddress, err := context.MemLoad(destOffset, vmhost.AddressLen) + if err != nil { + FailExecution(host, err) + return 1 + } + + value, err := context.MemLoad(valueOffset, vmhost.BalanceLen) + if err != nil { + FailExecution(host, err) + return 1 + } + + data, err := context.MemLoad(dataOffset, dataLength) + if err != nil { + FailExecution(host, err) + return 1 + } + + successFunc, err := context.MemLoad(successOffset, successLength) + if err != nil { + FailExecution(host, err) + return 1 + } + + errorFunc, err := context.MemLoad(errorOffset, errorLength) + if err != nil { + FailExecution(host, err) + return 1 + } + + return CreateAsyncCallV3WithTypedArgs(host, + calledSCAddress, + value, + data, + successFunc, + errorFunc, + gas, + extraGasForCallback, + nil) +} + +// CreateAsyncCallV3WithTypedArgs - createAsyncV3Call with arguments already read from memory +func CreateAsyncCallV3WithTypedArgs(host vmhost.VMHost, + calledSCAddress []byte, + value []byte, + data []byte, + successFunc []byte, + errorFunc []byte, + gas int64, + extraGasForCallback int64, + callbackClosure []byte) int32 { + + metering := host.Metering() + runtime := host.Runtime() + async := host.Async() + + metering.StartGasTracing(createAsyncCallName) + + gasToUse := metering.GasSchedule().BaseOpsAPICost.CreateAsyncCall + err := metering.UseGasBoundedAndAddTracedGas(createAsyncV3CallName, gasToUse) + if err != nil && runtime.UseGasBoundedShouldFailExecution() { + FailExecution(host, err) + return 1 + } + + asyncCall := &vmhost.AsyncCall{ + Status: vmhost.AsyncCallPending, + Destination: calledSCAddress, + Data: data, + ValueBytes: value, + GasLimit: uint64(gas), + SuccessCallback: string(successFunc), + ErrorCallback: string(errorFunc), + GasLocked: uint64(extraGasForCallback), + CallbackClosure: callbackClosure, + IsAsyncV3: true, + } + + if asyncCall.HasDefinedAnyCallback() { + gasToUse := metering.GasSchedule().BaseOpsAPICost.SetAsyncCallback + err := metering.UseGasBoundedAndAddTracedGas(setAsyncContextCallbackName, gasToUse) + if err != nil && runtime.UseGasBoundedShouldFailExecution() { + FailExecution(host, err) + return 1 + } + } + + err = async.RegisterAsyncCall("", asyncCall) + if err != nil { + FailExecution(host, err) + return 1 + } + + return 0 +} + // SetAsyncContextCallback VMHooks implementation. // @autogenerate(VMHooks) func (context *VMHooksImpl) SetAsyncContextCallback( diff --git a/wasmer2/libvmexeccapi.dylib b/wasmer2/libvmexeccapi.dylib index 26f7bfb81..523053179 100755 Binary files a/wasmer2/libvmexeccapi.dylib and b/wasmer2/libvmexeccapi.dylib differ diff --git a/wasmer2/libvmexeccapi.h b/wasmer2/libvmexeccapi.h index 14ab42707..99c49f35a 100644 --- a/wasmer2/libvmexeccapi.h +++ b/wasmer2/libvmexeccapi.h @@ -59,6 +59,7 @@ typedef struct { int32_t (*transfer_esdt_nft_execute_func_ptr)(void *context, int32_t dest_offset, int32_t token_id_offset, int32_t token_id_len, int32_t value_offset, int64_t nonce, int64_t gas_limit, int32_t function_offset, int32_t function_length, int32_t num_arguments, int32_t arguments_length_offset, int32_t data_offset); int32_t (*multi_transfer_esdt_nft_execute_func_ptr)(void *context, int32_t dest_offset, int32_t num_token_transfers, int32_t token_transfers_args_length_offset, int32_t token_transfer_data_offset, int64_t gas_limit, int32_t function_offset, int32_t function_length, int32_t num_arguments, int32_t arguments_length_offset, int32_t data_offset); int32_t (*create_async_call_func_ptr)(void *context, int32_t dest_offset, int32_t value_offset, int32_t data_offset, int32_t data_length, int32_t success_offset, int32_t success_length, int32_t error_offset, int32_t error_length, int64_t gas, int64_t extra_gas_for_callback); + int32_t (*create_async_v3_call_func_ptr)(void *context, int32_t dest_offset, int32_t value_offset, int32_t data_offset, int32_t data_length, int32_t success_offset, int32_t success_length, int32_t error_offset, int32_t error_length, int64_t gas, int64_t extra_gas_for_callback); int32_t (*set_async_context_callback_func_ptr)(void *context, int32_t callback, int32_t callback_length, int32_t data, int32_t data_length, int64_t gas); void (*upgrade_contract_func_ptr)(void *context, int32_t dest_offset, int64_t gas_limit, int32_t value_offset, int32_t code_offset, int32_t code_metadata_offset, int32_t length, int32_t num_arguments, int32_t arguments_length_offset, int32_t data_offset); void (*upgrade_from_source_contract_func_ptr)(void *context, int32_t dest_offset, int64_t gas_limit, int32_t value_offset, int32_t source_contract_address_offset, int32_t code_metadata_offset, int32_t num_arguments, int32_t arguments_length_offset, int32_t data_offset); diff --git a/wasmer2/libvmexeccapi.so b/wasmer2/libvmexeccapi.so index c024872c1..885abe8d3 100755 Binary files a/wasmer2/libvmexeccapi.so and b/wasmer2/libvmexeccapi.so differ diff --git a/wasmer2/libvmexeccapi_arm.dylib b/wasmer2/libvmexeccapi_arm.dylib index 6cf080c64..bed4870ad 100755 Binary files a/wasmer2/libvmexeccapi_arm.dylib and b/wasmer2/libvmexeccapi_arm.dylib differ diff --git a/wasmer2/libvmexeccapi_arm.so b/wasmer2/libvmexeccapi_arm.so index 1a31d9385..91a13e7de 100644 Binary files a/wasmer2/libvmexeccapi_arm.so and b/wasmer2/libvmexeccapi_arm.so differ diff --git a/wasmer2/wasmer2ImportsCgo.go b/wasmer2/wasmer2ImportsCgo.go index 2e7a83134..1867a1a39 100644 --- a/wasmer2/wasmer2ImportsCgo.go +++ b/wasmer2/wasmer2ImportsCgo.go @@ -32,6 +32,7 @@ package wasmer2 // extern int32_t w2_transferESDTNFTExecute(void* context, int32_t destOffset, int32_t tokenIDOffset, int32_t tokenIDLen, int32_t valueOffset, long long nonce, long long gasLimit, int32_t functionOffset, int32_t functionLength, int32_t numArguments, int32_t argumentsLengthOffset, int32_t dataOffset); // extern int32_t w2_multiTransferESDTNFTExecute(void* context, int32_t destOffset, int32_t numTokenTransfers, int32_t tokenTransfersArgsLengthOffset, int32_t tokenTransferDataOffset, long long gasLimit, int32_t functionOffset, int32_t functionLength, int32_t numArguments, int32_t argumentsLengthOffset, int32_t dataOffset); // extern int32_t w2_createAsyncCall(void* context, int32_t destOffset, int32_t valueOffset, int32_t dataOffset, int32_t dataLength, int32_t successOffset, int32_t successLength, int32_t errorOffset, int32_t errorLength, long long gas, long long extraGasForCallback); +// extern int32_t w2_createAsyncV3Call(void* context, int32_t destOffset, int32_t valueOffset, int32_t dataOffset, int32_t dataLength, int32_t successOffset, int32_t successLength, int32_t errorOffset, int32_t errorLength, long long gas, long long extraGasForCallback); // extern int32_t w2_setAsyncContextCallback(void* context, int32_t callback, int32_t callbackLength, int32_t data, int32_t dataLength, long long gas); // extern void w2_upgradeContract(void* context, int32_t destOffset, long long gasLimit, int32_t valueOffset, int32_t codeOffset, int32_t codeMetadataOffset, int32_t length, int32_t numArguments, int32_t argumentsLengthOffset, int32_t dataOffset); // extern void w2_upgradeFromSourceContract(void* context, int32_t destOffset, long long gasLimit, int32_t valueOffset, int32_t sourceContractAddressOffset, int32_t codeMetadataOffset, int32_t numArguments, int32_t argumentsLengthOffset, int32_t dataOffset); @@ -320,6 +321,7 @@ func populateCgoFunctionPointers() *cWasmerVmHookPointers { transfer_esdt_nft_execute_func_ptr: funcPointer(C.w2_transferESDTNFTExecute), multi_transfer_esdt_nft_execute_func_ptr: funcPointer(C.w2_multiTransferESDTNFTExecute), create_async_call_func_ptr: funcPointer(C.w2_createAsyncCall), + create_async_v3_call_func_ptr: funcPointer(C.w2_createAsyncV3Call), set_async_context_callback_func_ptr: funcPointer(C.w2_setAsyncContextCallback), upgrade_contract_func_ptr: funcPointer(C.w2_upgradeContract), upgrade_from_source_contract_func_ptr: funcPointer(C.w2_upgradeFromSourceContract), @@ -705,6 +707,12 @@ func w2_createAsyncCall(context unsafe.Pointer, destOffset int32, valueOffset in return vmHooks.CreateAsyncCall(executor.MemPtr(destOffset), executor.MemPtr(valueOffset), executor.MemPtr(dataOffset), dataLength, executor.MemPtr(successOffset), successLength, executor.MemPtr(errorOffset), errorLength, gas, extraGasForCallback) } +//export w2_createAsyncV3Call +func w2_createAsyncV3Call(context unsafe.Pointer, destOffset int32, valueOffset int32, dataOffset int32, dataLength int32, successOffset int32, successLength int32, errorOffset int32, errorLength int32, gas int64, extraGasForCallback int64) int32 { + vmHooks := getVMHooksFromContextRawPtr(context) + return vmHooks.CreateAsyncV3Call(executor.MemPtr(destOffset), executor.MemPtr(valueOffset), executor.MemPtr(dataOffset), dataLength, executor.MemPtr(successOffset), successLength, executor.MemPtr(errorOffset), errorLength, gas, extraGasForCallback) +} + //export w2_setAsyncContextCallback func w2_setAsyncContextCallback(context unsafe.Pointer, callback int32, callbackLength int32, data int32, dataLength int32, gas int64) int32 { vmHooks := getVMHooksFromContextRawPtr(context) diff --git a/wasmer2/wasmer2Names.go b/wasmer2/wasmer2Names.go index bba5cf140..6408a88bd 100644 --- a/wasmer2/wasmer2Names.go +++ b/wasmer2/wasmer2Names.go @@ -30,6 +30,7 @@ var functionNames = map[string]struct{}{ "transferESDTNFTExecute": empty, "multiTransferESDTNFTExecute": empty, "createAsyncCall": empty, + "createAsyncV3Call": empty, "setAsyncContextCallback": empty, "upgradeContract": empty, "upgradeFromSourceContract": empty,