예제 #1
0
파일: vm.go 프로젝트: jaekwon/GuppyCamp
// CONTRACT appState is aware of caller and callee, so we can just mutate them.
// value: To be transferred from caller to callee. Refunded upon error.
// gas:   Available gas. No refunds for gas.
// code: May be nil, since the CALL opcode may be used to send value from contracts to accounts
func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, gas *uint64) (output []byte, err error) {

	exception := new(string)
	defer func() {
		if vm.evc != nil {
			vm.evc.FireEvent(types.EventStringAccReceive(callee.Address.Postfix(20)), types.EventMsgCall{
				&types.CallData{caller.Address.Postfix(20), callee.Address.Postfix(20), input, value, *gas},
				vm.origin.Postfix(20),
				vm.txid,
				output,
				*exception,
			})
		}
	}()

	if err = transfer(caller, callee, value); err != nil {
		*exception = err.Error()
		return
	}

	if len(code) > 0 {
		vm.callDepth += 1
		output, err = vm.call(caller, callee, code, input, value, gas)
		vm.callDepth -= 1
		if err != nil {
			*exception = err.Error()
			err := transfer(callee, caller, value)
			if err != nil {
				panic("Could not return value to caller")
			}
		}
	}

	return
}
예제 #2
0
// subscribes to an AccReceive, runs the vm, returns the exception
func runVMWaitEvents(t *testing.T, ourVm *VM, caller, callee *Account, subscribeAddr, contractCode []byte, gas uint64) string {
	// we need to catch the event from the CALL to check for exceptions
	evsw := new(events.EventSwitch)
	evsw.Start()
	ch := make(chan interface{})
	fmt.Printf("subscribe to %x\n", subscribeAddr)
	evsw.AddListenerForEvent("test", types.EventStringAccReceive(subscribeAddr), func(msg interface{}) {
		ch <- msg
	})
	evc := events.NewEventCache(evsw)
	ourVm.SetFireable(evc)
	go func() {
		start := time.Now()
		output, err := ourVm.Call(caller, callee, contractCode, []byte{}, 0, &gas)
		fmt.Printf("Output: %v Error: %v\n", output, err)
		fmt.Println("Call took:", time.Since(start))
		if err != nil {
			ch <- err.Error()
		}
		evc.Flush()
	}()
	msg := <-ch
	switch ev := msg.(type) {
	case types.EventMsgCallTx:
		return ev.Exception
	case types.EventMsgCall:
		return ev.Exception
	case string:
		return ev
	}
	return ""
}
예제 #3
0
// create two contracts, one of which calls the other
func TestWSCallCall(t *testing.T) {
	con := newWSCon(t)
	amt := uint64(10000)
	code, _, returnVal := simpleContract()
	txid := new([]byte)

	// deploy the two contracts
	_, receipt := broadcastTx(t, "JSONRPC", userByteAddr, nil, code, userBytePriv, amt, 1000, 1000)
	contractAddr1 := receipt.ContractAddr
	code, _, _ = simpleCallContract(contractAddr1)
	_, receipt = broadcastTx(t, "JSONRPC", userByteAddr, nil, code, userBytePriv, amt, 1000, 1000)
	contractAddr2 := receipt.ContractAddr

	// susbscribe to the new contracts
	amt = uint64(10001)
	eid1 := types.EventStringAccReceive(contractAddr1)
	subscribe(t, con, eid1)
	defer func() {
		unsubscribe(t, con, eid1)
		con.Close()
	}()
	// call contract2, which should call contract1, and wait for ev1
	data := []byte{0x1} // just needs to be non empty for this to be a CallTx

	// let the contract get created first
	waitForEvent(t, con, eid1, true, func() {
	}, func(eid string, b []byte) error {
		return nil
	})
	// call it
	waitForEvent(t, con, eid1, true, func() {
		tx, _ := broadcastTx(t, "JSONRPC", userByteAddr, contractAddr2, data, userBytePriv, amt, 1000, 1000)
		*txid = account.HashSignBytes(tx)
	}, unmarshalValidateCallCall(userByteAddr, returnVal, txid))
}