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 98eecd6c6..337e05812 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 5d2d213e5..8fc0acbb3 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 60ef83c3b..9b01d37e4 100644 --- a/config/gasSchedule.go +++ b/config/gasSchedule.go @@ -301,6 +301,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 c32d718a3..79ebfd2ee 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 a5e57419c..d426fcb2a 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 d606a3170..4b5c1e9f7 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..f66f86c1a 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, @@ -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..349c8fb20 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 } @@ -84,122 +76,6 @@ func createDataFromAsyncParams( return callData.ToBytes(), nil } -/* - 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, - data []byte, - asyncArguments *vmcommon.AsyncArguments, - parseArgumentsFunc func(data string) ([][]byte, error)) ([]byte, error) { - - return appendAsyncParamsToCallData( - CreateCallbackAsyncParams(hasher, asyncArguments), - data, - false, - parseArgumentsFunc) -} - -/* - 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, - asyncData []byte, - parseArgumentsFunc func(data string) ([][]byte, error)) ([]byte, error) { - - var asyncParams [][]byte - if asyncData != nil { - asyncParams, _ = parseArgumentsFunc(string(asyncData)) - // string start with a @ so first parsed argument will be empty always - asyncParams = asyncParams[1:] - } else { - return callData, nil - } - - return appendAsyncParamsToCallData( - asyncParams, - callData, - true, - parseArgumentsFunc) -} - -func appendAsyncParamsToCallData( - asyncParams [][]byte, - data []byte, - hasFunction bool, - parseArgumentsFunc func(data string) ([][]byte, error)) ([]byte, error) { - - if data == nil { - return nil, nil - } - - args, err := parseArgumentsFunc(string(data)) - if err != nil { - return nil, err - } - - var functionName string - if hasFunction { - functionName = string(args[0]) - } - - // check if there is only one argument and that is 0 - if len(args) != 0 { - args = args[1:] - } - - callData := txDataBuilder.NewBuilder() - - if functionName != "" { - callData.Func(functionName) - } - - if len(args) != 0 { - for _, arg := range args { - callData.Bytes(arg) - } - } else { - if !hasFunction { - callData.Bytes([]byte{}) - } - } - - for _, asyncParam := range asyncParams { - callData.Bytes(asyncParam) - } - - return callData.ToBytes(), nil -} - -/* - 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 { - return nil - } - newAsyncParams := make([][]byte, 4) - newAsyncParams[0] = GenerateNewCallID(hasher, asyncParams.CallID, []byte{0}) - newAsyncParams[1] = asyncParams.CallID - newAsyncParams[2] = asyncParams.CallerCallID - newAsyncParams[3] = []byte{0} - return newAsyncParams -} - // GenerateNewCallID will generate a new call ID as byte slice func GenerateNewCallID(hasher crypto.Hasher, parentCallID []byte, suffix []byte) []byte { newCallID := append(parentCallID, suffix...) 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 8d4891cb3..06e12f716 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" @@ -11,4 +50,7 @@ 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" ) diff --git a/vmhost/hostCore/execution.go b/vmhost/hostCore/execution.go index 0cf57db5a..b8cac6718 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) @@ -1142,6 +1140,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) } @@ -1151,12 +1157,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) } @@ -1221,6 +1233,10 @@ func (host *vmHost) callSCMethodAsynchronousCallBack() error { return nil } + if asyncCall.HasPendingCallback { + return nil + } + async.SetCallbackParentCall(asyncCall) if asyncCall.HasCallback() { @@ -1233,12 +1249,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 } @@ -1260,47 +1270,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 9b180c862..f1a4f2bbd 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 implements HostContext interface. @@ -95,7 +96,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 fa74ff916..55b94843c 100644 --- a/vmhost/interface.go +++ b/vmhost/interface.go @@ -395,7 +395,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 72547da21..4cf333dad 100644 --- a/vmhost/vmhooks/baseOps.go +++ b/vmhost/vmhooks/baseOps.go @@ -1421,6 +1421,132 @@ 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 { + runtime := host.Runtime() + + calledSCAddress, err := context.MemLoad(destOffset, vmhost.AddressLen) + if WithFaultAndHost(host, err, runtime.BaseOpsErrorShouldFailExecution()) { + return 1 + } + + value, err := context.MemLoad(valueOffset, vmhost.BalanceLen) + if WithFaultAndHost(host, err, runtime.BaseOpsErrorShouldFailExecution()) { + return 1 + } + + data, err := context.MemLoad(dataOffset, dataLength) + if WithFaultAndHost(host, err, runtime.BaseOpsErrorShouldFailExecution()) { + return 1 + } + + successFunc, err := context.MemLoad(successOffset, successLength) + if WithFaultAndHost(host, err, runtime.BaseOpsErrorShouldFailExecution()) { + return 1 + } + + errorFunc, err := context.MemLoad(errorOffset, errorLength) + if WithFaultAndHost(host, err, runtime.BaseOpsErrorShouldFailExecution()) { + 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 + metering.UseAndTraceGas(gasToUse) + + 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 + metering.UseAndTraceGas(gasToUse) + } + + err := async.RegisterAsyncCall("", asyncCall) + if WithFaultAndHost(host, err, runtime.BaseOpsErrorShouldFailExecution()) { + return 1 + } + + return 0 +} + // SetAsyncContextCallback VMHooks implementation. // @autogenerate(VMHooks) func (context *VMHooksImpl) SetAsyncContextCallback( diff --git a/wasmer2/libvmexeccapi.h b/wasmer2/libvmexeccapi.h index 1152bc3e5..ff4aea320 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/wasmer2ImportsCgo.go b/wasmer2/wasmer2ImportsCgo.go index f85383cba..a4e54eb89 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); @@ -309,6 +310,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), @@ -683,6 +685,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 ad662058b..2fea98e4c 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,