长安链的Gas使用

2021-06-19  本文已影响0人  明神特烦恼

作者:明神特烦恼
公众号:明神特烦恼

Gas在以太坊中表示执行智能合约所收取的费用。在长安链设计过程中,也借鉴Gas的概念,本文介绍长安链使用Gas的作用。

   长安链的地址中并没有币的概念,也没有账户体系,而且在联盟链环境中,不需要收取执行智能合约的费用。我们通过代码来分析长安链的使用方式(gasm):

1)在模拟执行生成读写机的流程,传入现有Gas参数为0(倒数第二个参数)

    contractResultPayload, txStatusCode := ts.VmManager.RunContract(contractId, method, byteCode, parameters, txSimContext, 0, tx.Header.TxType)

    result.Code = txStatusCode
    result.ContractResult = contractResultPayload

    if txStatusCode == commonpb.TxStatusCode_SUCCESS {
        return result, nil
    } else {
        return result, errors.New(contractResultPayload.Message)
    }

2)执行wasm字节码需要花费Gas,根据指令的Gas值进行叠加。

func (vm *VirtualMachine) execNativeFunction() {
    for ; int(vm.ActiveContext.PC) < len(vm.ActiveContext.Function.Body); vm.ActiveContext.PC++ {

        switch op := vm.ActiveContext.Function.Body[vm.ActiveContext.PC]; OptCode(op) {
        case OptCodeReturn:
            return
        default:
            virtualMachineInstructions[op](vm)
            vm.AddGas(OptCode(op))
        }
    }
}

3)如果超过Gaslimit值,则会panic。

func (vm *VirtualMachine) AddGas(op OptCode) {
    switch op {
    case OptCodeMemoryGrow:
    case OptCodeCall:
    case OptCodeCallIndirect:
    default:
        vm.Gas += tableGas[op]
    }
    if vm.Gas > vm.GasLimit {
        panic("gas over limit")
    }
}

4)在虚拟机调用地方会捕获异常,设置该交易执行失败

    defer func() {
        if err := recover(); err != nil {
            r.Log.Errorf("failed to invoke gasm, tx id:%s, error:%s", tx.Header.TxId, err)
            contractResult.Code = commonPb.ContractResultCode_FAIL
            if e, ok := err.(error); ok {
                contractResult.Message = e.Error()
            } else if e, ok := err.(string); ok {
                contractResult.Message = e
            }
            debug.PrintStack()
        }
    }()

长安链Gas的作用:
1)Gaslimit 设置比较大,一般够普通合约调用,可以防止死循环
2)区块链的特点要求多副本一致,区块一致,也要求读写集一致。有了Gas可以更近一步保证执行流程一致,如果各节点执行智能合约过程不一致但结果一致,通过Gas计算可以检测到。

注意:
在实际查看区块验证代码时,在交易Result验证过程中把gas值排除后进行比较。思考 是否是考虑不同节点可以选择不同的合约语言或者不同的解析方式来执行合约,这样gas值就会不同。

func CalcResultBytes(result *commonPb.Result) ([]byte, error) {
    if result == nil {
        return nil, errors.New("calculate result bytes error, result == nil")
    }
    tmpGas := result.ContractResult.GasUsed
    result.ContractResult.GasUsed = 0
    resultBytes, err := proto.Marshal(result)
    result.ContractResult.GasUsed = tmpGas
    if err != nil {
        return nil, err
    }
    return resultBytes, nil
}

欢迎大家补充指正。关注作者,共同学习区块链技术。

上一篇下一篇

猜你喜欢

热点阅读