multiversx / mx-chain-go

⚡ The official implementation of the MultiversX blockchain protocol, written in golang.
https://multiversx.com
GNU General Public License v3.0
927 stars 202 forks source link

[Question]: The principle of cross contract call in one shard #5344

Closed lyh169 closed 1 year ago

lyh169 commented 1 year ago

Please write the desired question

  1. Hi, when i read the multiversx doc https://docs.multiversx.com/technology/the-wasm-vm/ about wasm-vm。 I find the official doc about the vm is "In case a contract calls another, and they are both in the same shard, the execution is effectively synchronous, and both contracts are executed without even leaving the VM.";; image

I read the code about the mx-chain-vm-go and find that func populateWasmerImports(imports *wasmerImports) error { this import the hook function(or host function) into the wasmer, this mean whether or not when A cross contract call B in one shard, the call will not leave wasmer and just through the hook function about v1_5_executeOnDestContext https://github.com/multiversx/mx-chain-vm-go/blob/master/wasmer/wasmerImportsCgo.go#L1949 call the B contract

  1. I have view the code of the mx-chain-vm-go, and find in func of callSCMethod(), after the "err = runtime.CallFunction(function)" the code will exec ", err = host.processAsyncInfo(runtime.GetAsyncContextInfo())" in the default(the same shard); if the contract A call contract B in the same shard is that mean the ", err = host.processAsyncInfo(runtime.GetAsyncContextInfo())" is exec call the B contract?

    The analyse 1 and 2 have a conflict, which is the correct principle of cross contract call in one shard about tehe multiversx?

    
    func (host *vmHost) callSCMethod() error {
    runtime := host.Runtime()
    
    log.Trace("call SC method")
    
    // TODO host.verifyAllowedFunctionCall() performs some checks, but then the
    // function itself is changed by host.getFunctionByCallType(). Order must be
    // reversed, and `getFunctionByCallType()` must be decomposed into smaller functions.
    
    err := host.verifyAllowedFunctionCall()
    if err != nil {
        log.Trace("call SC method failed", "error", err, "src", "verifyAllowedFunctionCall")
        return err
    }
    
    callType := runtime.GetVMInput().CallType
    function, err := host.getFunctionByCallType(callType)
    if err != nil {
        if callType == vm.AsynchronousCallBack && errors.Is(err, vmhost.ErrNilCallbackFunction) {
            err = host.processCallbackStack()
            if err != nil {
                log.Trace("call SC method failed", "error", err, "src", "processCallbackStack")
            }
    
            return err
        }
        log.Trace("call SC method failed", "error", err, "src", "getFunctionByCallType")
        return err
    }
    
    err = runtime.CallFunction(function)
    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 err
    }
    
    switch callType {
    case vm.AsynchronousCall:
        _, paiErr := host.processAsyncInfo(runtime.GetAsyncContextInfo())
        if paiErr != nil {
            log.Trace("call SC method failed", "error", paiErr)
            return paiErr
        }
        err = host.sendCallbackToCurrentCaller()
    case vm.AsynchronousCallBack:
        err = host.processCallbackStack()
    default:
        _, err = host.processAsyncInfo(runtime.GetAsyncContextInfo())
    }
    
    if err != nil {
        log.Trace("call SC method failed", "error", err, "src", "async post-process")
    }
    
    return err
    }

func (host vmHost) processAsyncInfo(asyncInfo vmhost.AsyncContextInfo) (*vmhost.AsyncContextInfo, error) { if len(asyncInfo.AsyncContextMap) == 0 { return asyncInfo, nil }

err := host.setupAsyncCallsGas(asyncInfo)
if err != nil {
    return nil, err
}

for _, asyncContext := range asyncInfo.AsyncContextMap {
    for _, asyncCall := range asyncContext.AsyncCalls {
        if !host.canExecuteSynchronously(asyncCall.Destination, asyncCall.Data) {
            continue
        }

        procErr := host.processAsyncCall(asyncCall)
        if procErr != nil {
            return nil, procErr
        }
    }
}

.... }



### Code of Conduct

- [X] I agree to follow this project's Code of Conduct
sasurobert commented 1 year ago

Hey.

The flow is different.

  1. User calls SC and the call enters into doRunSmartContractCall
  2. It gets into the callSCMethod and the WASM code is getting executed on runtime.CallFunction call.
  3. The SC A calls with async VM Api SC B -ManagedAsyncCall from managedei.go which ends the call for SCA and puts a breakpoint in the SCA execution.
  4. This will trigger "handleAsyncCallBreakpoint" function on asyncCall.go and in this function we call executeSyncDestinationCall and executeSyncCallbackCall
  5. Finish execution.