github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/engine/call.go (about)

     1  package engine
     2  
     3  import (
     4  	"math/big"
     5  
     6  	"github.com/hyperledger/burrow/execution/errors"
     7  	"github.com/hyperledger/burrow/execution/exec"
     8  	"github.com/hyperledger/burrow/permission"
     9  )
    10  
    11  var big64 = big.NewInt(64)
    12  
    13  // Call provides a standard wrapper for implementing Callable.Call with appropriate error handling and event firing.
    14  func Call(state State, params CallParams, execute func(State, CallParams) ([]byte, error)) ([]byte, error) {
    15  	maybe := new(errors.Maybe)
    16  	if params.CallType == exec.CallTypeCall || params.CallType == exec.CallTypeCode {
    17  		// NOTE: Delegate and Static CallTypes do not transfer the value to the callee.
    18  		maybe.PushError(Transfer(state.CallFrame, params.Caller, params.Callee, &params.Value))
    19  	}
    20  
    21  	output := maybe.Bytes(execute(state, params))
    22  	// fire the post call event (including exception if applicable) and make sure we return the accumulated call error
    23  	maybe.PushError(FireCallEvent(state.CallFrame, maybe.Error(), state.EventSink, output, params))
    24  	return output, maybe.Error()
    25  }
    26  
    27  func FireCallEvent(callFrame *CallFrame, callErr error, eventSink exec.EventSink, output []byte,
    28  	params CallParams) error {
    29  	// fire the post call event (including exception if applicable)
    30  	return eventSink.Call(&exec.CallEvent{
    31  		CallType: params.CallType,
    32  		CallData: &exec.CallData{
    33  			Caller: params.Caller,
    34  			Callee: params.Callee,
    35  			Data:   params.Input,
    36  			Value:  params.Value.Bytes(),
    37  			Gas:    params.Gas.Bytes(),
    38  		},
    39  		Origin:     params.Origin,
    40  		StackDepth: callFrame.CallStackDepth(),
    41  		Return:     output,
    42  	}, errors.AsException(callErr))
    43  }
    44  
    45  func CallFromSite(st State, dispatcher Dispatcher, site CallParams, target CallParams) ([]byte, error) {
    46  	err := EnsurePermission(st.CallFrame, site.Callee, permission.Call)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	// Get the arguments from the memory
    51  	// EVM contract
    52  	err = UseGasNegative(site.Gas, GasGetAccount)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	// since CALL is used also for sending funds,
    57  	// acc may not exist yet. This is an errors.CodedError for
    58  	// CALLCODE, but not for CALL, though I don't think
    59  	// ethereum actually cares
    60  	acc, err := st.CallFrame.GetAccount(target.Callee)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	if acc == nil {
    65  		if target.CallType != exec.CallTypeCall {
    66  			return nil, errors.Codes.UnknownAddress
    67  		}
    68  		// We're sending funds to a new account so we must create it first
    69  		err := st.CallFrame.CreateAccount(site.Callee, target.Callee)
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  		acc, err = st.CallFrame.GetAccount(target.Callee)
    74  		if err != nil {
    75  			return nil, err
    76  		}
    77  	}
    78  
    79  	// Establish a stack frame and perform the call
    80  	childCallFrame, err := st.CallFrame.NewFrame()
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	childState := State{
    85  		CallFrame:  childCallFrame,
    86  		Blockchain: st.Blockchain,
    87  		EventSink:  st.EventSink,
    88  	}
    89  	// Ensure that gasLimit is reasonable
    90  	if site.Gas.Cmp(target.Gas) < 0 {
    91  		// EIP150 - the 63/64 rule - rather than errors.CodedError we pass this specified fraction of the total available gas
    92  		gas := new(big.Int)
    93  		target.Gas.Sub(site.Gas, gas.Div(site.Gas, big64))
    94  	}
    95  	// NOTE: we will return any used gas later.
    96  	site.Gas.Sub(site.Gas, target.Gas)
    97  
    98  	// Setup callee params for call type
    99  	target.Origin = site.Origin
   100  
   101  	// Set up the caller/callee context
   102  	switch target.CallType {
   103  	case exec.CallTypeCall:
   104  		// Calls contract at target from this contract normally
   105  		// Value: transferred
   106  		// Caller: this contract
   107  		// Storage: target
   108  		// Code: from target
   109  		target.Caller = site.Callee
   110  
   111  	case exec.CallTypeStatic:
   112  		// Calls contract at target from this contract with no state mutation
   113  		// Value: not transferred
   114  		// Caller: this contract
   115  		// Storage: target (read-only)
   116  		// Code: from target
   117  		target.Caller = site.Callee
   118  
   119  		childState.CallFrame.ReadOnly()
   120  		childState.EventSink = exec.NewLogFreeEventSink(childState.EventSink)
   121  
   122  	case exec.CallTypeCode:
   123  		// Calling this contract from itself as if it had the code at target
   124  		// Value: transferred
   125  		// Caller: this contract
   126  		// Storage: this contract
   127  		// Code: from target
   128  
   129  		target.Caller = site.Callee
   130  		target.Callee = site.Callee
   131  
   132  	case exec.CallTypeDelegate:
   133  		// Calling this contract from the original caller as if it had the code at target
   134  		// Value: not transferred
   135  		// Caller: original caller
   136  		// Storage: this contract
   137  		// Code: from target
   138  
   139  		target.Caller = site.Caller
   140  		target.Callee = site.Callee
   141  
   142  	default:
   143  		// Switch should be exhaustive so we should reach this
   144  		panic("invalid call type")
   145  	}
   146  
   147  	dispatch := dispatcher.Dispatch(acc)
   148  	if dispatch == nil {
   149  		return nil, errors.Errorf(errors.Codes.NotCallable, "cannot call: %v", acc.Address)
   150  	}
   151  	returnData, err := dispatch.Call(childState, target)
   152  
   153  	if err == nil {
   154  		// Sync error is a hard stop
   155  		err = childState.CallFrame.Sync()
   156  	}
   157  
   158  	// Handle remaining gas.
   159  	//site.Gas.Add(site.Gas, target.Gas)
   160  	return returnData, err
   161  }