diff --git a/bevm/call_byzcoin.go b/bevm/call_byzcoin.go index 57ed4212e9..3266c3bdeb 100644 --- a/bevm/call_byzcoin.go +++ b/bevm/call_byzcoin.go @@ -192,120 +192,148 @@ func getArgs(eventArg interface{}) (byzcoin.Arguments, error) { return args, nil } -func getInstrForEvent(name string, eventData []interface{}) ( +func getInstrForSpawnEvent(eventData []interface{}) ( *byzcoin.Instruction, error) { - var instr *byzcoin.Instruction + if len(eventData) != 3 { + return nil, xerrors.Errorf("invalid data for 'spawn' event: "+ + "got %d values, expected 3", len(eventData)) + } - switch name { - case byzcoinSpawnEvent: - if len(eventData) != 3 { - return nil, xerrors.Errorf("invalid data for 'spawn' event: "+ - "got %d values, expected 3", len(eventData)) - } + instanceID, err := get32Byte(eventData[0]) + if err != nil { + return nil, xerrors.Errorf("invalid instanceID for 'spawn' event: "+ + "%v", err) + } - instanceID, err := get32Byte(eventData[0]) - if err != nil { - return nil, xerrors.Errorf("invalid instanceID for 'spawn' event: "+ - "%v", err) - } + contractID, err := getString(eventData[1]) + if err != nil { + return nil, xerrors.Errorf("invalid contractID for 'spawn' event: "+ + "%v", err) + } - contractID, err := getString(eventData[1]) - if err != nil { - return nil, xerrors.Errorf("invalid contractID for 'spawn' event: "+ - "%v", err) - } + args, err := getArgs(eventData[2]) + if err != nil { + return nil, xerrors.Errorf("invalid args for 'spawn' event: "+ + "%v", err) + } - args, err := getArgs(eventData[2]) - if err != nil { - return nil, xerrors.Errorf("invalid args for 'spawn' event: "+ - "%v", err) - } + if !evmSpawnableContracts[contractID] { + return nil, xerrors.Errorf("contract '%s' has not been "+ + "whitelisted to be spawned by an EVM contract", + contractID) + } - if !evmSpawnableContracts[contractID] { - return nil, xerrors.Errorf("contract '%s' has not been "+ - "whitelisted to be spawned by an EVM contract", - contractID) - } + return &byzcoin.Instruction{ + InstanceID: instanceID, + Spawn: &byzcoin.Spawn{ + ContractID: contractID, + Args: args, + }, + }, nil +} - instr = &byzcoin.Instruction{ - InstanceID: instanceID, - Spawn: &byzcoin.Spawn{ - ContractID: contractID, - Args: args, - }, - } +func getInstrForInvokeEvent(eventData []interface{}) ( + *byzcoin.Instruction, error) { + if len(eventData) != 4 { + return nil, xerrors.Errorf("invalid data for 'invoke' event: "+ + "got %d values, expected 4", len(eventData)) + } - case byzcoinInvokeEvent: - if len(eventData) != 4 { - return nil, xerrors.Errorf("invalid data for 'invoke' event: "+ - "got %d values, expected 4", len(eventData)) - } + instanceID, err := get32Byte(eventData[0]) + if err != nil { + return nil, xerrors.Errorf("invalid instanceID for 'invoke' event: "+ + "%v", err) + } - instanceID, err := get32Byte(eventData[0]) - if err != nil { - return nil, xerrors.Errorf("invalid instanceID for 'invoke' event: "+ - "%v", err) - } + contractID, err := getString(eventData[1]) + if err != nil { + return nil, xerrors.Errorf("invalid contractID for 'invoke' event: "+ + "%v", err) + } - contractID, err := getString(eventData[1]) - if err != nil { - return nil, xerrors.Errorf("invalid contractID for 'invoke' event: "+ - "%v", err) - } + command, err := getString(eventData[2]) + if err != nil { + return nil, xerrors.Errorf("invalid command for 'invoke' event: "+ + "%v", err) + } - command, err := getString(eventData[2]) - if err != nil { - return nil, xerrors.Errorf("invalid command for 'invoke' event: "+ - "%v", err) - } + args, err := getArgs(eventData[3]) + if err != nil { + return nil, xerrors.Errorf("invalid args for 'invoke' event: "+ + "%v", err) + } - args, err := getArgs(eventData[3]) - if err != nil { - return nil, xerrors.Errorf("invalid args for 'invoke' event: "+ - "%v", err) - } + return &byzcoin.Instruction{ + InstanceID: instanceID, + Invoke: &byzcoin.Invoke{ + ContractID: contractID, + Command: command, + Args: args, + }, + }, nil +} - instr = &byzcoin.Instruction{ - InstanceID: instanceID, - Invoke: &byzcoin.Invoke{ - ContractID: contractID, - Command: command, - Args: args, - }, - } +func getInstrForDeleteEvent(eventData []interface{}) ( + *byzcoin.Instruction, error) { + if len(eventData) != 3 { + return nil, xerrors.Errorf("invalid data for 'delete' event: "+ + "got %d values, expected 3", len(eventData)) + } - case byzcoinDeleteEvent: - if len(eventData) != 3 { - return nil, xerrors.Errorf("invalid data for 'delete' event: "+ - "got %d values, expected 3", len(eventData)) - } + instanceID, err := get32Byte(eventData[0]) + if err != nil { + return nil, xerrors.Errorf("invalid instanceID for 'delete' event: "+ + "%v", err) + } - instanceID, err := get32Byte(eventData[0]) + contractID, err := getString(eventData[1]) + if err != nil { + return nil, xerrors.Errorf("invalid contractID for 'delete' event: "+ + "%v", err) + } + + args, err := getArgs(eventData[2]) + if err != nil { + return nil, xerrors.Errorf("invalid args for 'delete' event: "+ + "%v", err) + } + + return &byzcoin.Instruction{ + InstanceID: instanceID, + Delete: &byzcoin.Delete{ + ContractID: contractID, + Args: args, + }, + }, nil +} + +func getInstrForEvent(name string, eventData []interface{}) ( + *byzcoin.Instruction, error) { + var instr *byzcoin.Instruction + var err error + + switch name { + case byzcoinSpawnEvent: + instr, err = getInstrForSpawnEvent(eventData) if err != nil { - return nil, xerrors.Errorf("invalid instanceID for 'delete' event: "+ + return nil, xerrors.Errorf("failed to handle 'spawn' event: "+ "%v", err) } - contractID, err := getString(eventData[1]) + case byzcoinInvokeEvent: + instr, err = getInstrForInvokeEvent(eventData) if err != nil { - return nil, xerrors.Errorf("invalid contractID for 'delete' event: "+ + return nil, xerrors.Errorf("failed to handle 'invoke' event: "+ "%v", err) } - args, err := getArgs(eventData[2]) + case byzcoinDeleteEvent: + instr, err = getInstrForDeleteEvent(eventData) if err != nil { - return nil, xerrors.Errorf("invalid args for 'delete' event: "+ + return nil, xerrors.Errorf("failed to handle 'delete' event: "+ "%v", err) } - instr = &byzcoin.Instruction{ - InstanceID: instanceID, - Delete: &byzcoin.Delete{ - ContractID: contractID, - Args: args, - }, - } - default: return nil, xerrors.Errorf("internal error: event '%s' not handled", name) diff --git a/bevm/contract.go b/bevm/contract.go index 1c130e912a..3caeb93761 100644 --- a/bevm/contract.go +++ b/bevm/contract.go @@ -413,10 +413,48 @@ func (c *contractBEvm) Delete(rst byzcoin.ReadOnlyStateTrie, return } +// An EVM call can generate several Byzcoin instructions, all originating from +// different contracts (inter-contract calls). +// The following map helps to keep track of the signer counters while +// processing all the events of an EVM call. +type signerCounters struct { + rst byzcoin.ReadOnlyStateTrie + ctr map[common.Address]uint64 +} + +func newSignerCounters(rst byzcoin.ReadOnlyStateTrie) *signerCounters { + return &signerCounters{ + rst: rst, + ctr: map[common.Address]uint64{}, + } +} + +func (c *signerCounters) getNext(address common.Address, + identity darc.Identity) (uint64, error) { + var err error + + // Retrieve counter from map + counter, ok := c.ctr[address] + if !ok { + // Counter not yet available -- retrieve from Byzcoin + counter, err = c.rst.GetSignerCounter(identity) + if err != nil { + return 0, xerrors.Errorf("failed to get counter "+ + "for identity '%s': %v", identity, err) + } + } + + // Update counter + counter++ + c.ctr[address] = counter + + return counter, nil +} + // Handle the log entries produced by an EVM execution. // Currently, only the special entries allowing to interact with Byzcoin // contracts are handled, the other ones are ignored. -func handleLogs(inst byzcoin.Instruction, rst byzcoin.ReadOnlyStateTrie, +func handleLogs(bevmInstr byzcoin.Instruction, rst byzcoin.ReadOnlyStateTrie, logEntries []*types.Log) ( []byzcoin.StateChange, error) { var err error @@ -426,14 +464,9 @@ func handleLogs(inst byzcoin.Instruction, rst byzcoin.ReadOnlyStateTrie, "events: %v", err) } + counters := newSignerCounters(rst) var stateChanges []byzcoin.StateChange - // An EVM call can generate several Byzcoin instructions, all originating - // from different contracts (inter-contract calls). - // The following map helps to keep track of the signer counters while - // processing all the events of an EVM call. - counters := map[common.Address]uint64{} - for _, logEntry := range logEntries { // See https://solidity.readthedocs.io/en/v0.5.3/abi-spec.html#events eventID := logEntry.Topics[0] @@ -451,61 +484,65 @@ func handleLogs(inst byzcoin.Instruction, rst byzcoin.ReadOnlyStateTrie, continue } - // Build the instruction from the event - instr, err := getInstrForEvent(eventName, eventData) + sc, err := handleLogEntry(bevmInstr.InstanceID[:], rst, eventName, + eventData, logEntry.Address, counters) if err != nil { - return nil, xerrors.Errorf("failed to build instruction "+ + return nil, xerrors.Errorf("failed to handle log entry "+ "for EVM event: %v", err) } - signer := darc.NewSignerEvmContract(inst.InstanceID[:], - logEntry.Address) - identity := signer.Identity() - - // Retrieve counter - counter, ok := counters[logEntry.Address] - if !ok { - // Counter not yet available -- retrieve from Byzcoin - counter, err = rst.GetSignerCounter(identity) - if err != nil { - return nil, xerrors.Errorf("failed to get counter "+ - "for identity '%s': %v", identity, err) - } - } + stateChanges = append(stateChanges, *sc) + } - // Update counter - counter++ - counters[logEntry.Address] = counter + return stateChanges, nil +} - // Fill in missing information and sign - instr.SignerIdentities = []darc.Identity{identity} - instr.SignerCounter = []uint64{counter} +// Handle a single log entry. +func handleLogEntry(bevmInstanceID []byte, rst byzcoin.ReadOnlyStateTrie, + eventName string, eventData []interface{}, address common.Address, + counters *signerCounters) (*byzcoin.StateChange, error) { + // Build the instruction from the event + instr, err := getInstrForEvent(eventName, eventData) + if err != nil { + return nil, xerrors.Errorf("failed to build instruction "+ + "for EVM event: %v", err) + } - instr.SetVersion(rst.GetVersion()) + signer := darc.NewSignerEvmContract(bevmInstanceID, address) + identity := signer.Identity() - err = instr.SignWith([]byte{}, signer) - if err != nil { - return nil, xerrors.Errorf("failed to sign instruction "+ - "from EVM: %v", err) - } + // Get next counter + counter, err := counters.getNext(address, identity) + if err != nil { + return nil, xerrors.Errorf("failed to get counter: %v", err) + } - // Encode the instruction and store it in the state change's value - encodedInstr, err := protobuf.Encode(instr) - if err != nil { - return nil, xerrors.Errorf("failed to encode instruction "+ - "from EVM: %v", err) - } + // Fill in missing information and sign + instr.SignerIdentities = []darc.Identity{identity} + instr.SignerCounter = []uint64{counter} - sc := byzcoin.NewStateChange( - byzcoin.GenerateInstruction, - byzcoin.NewInstanceID(nil), - "", - encodedInstr, - nil, - ) + instr.SetVersion(rst.GetVersion()) - stateChanges = append(stateChanges, sc) + err = instr.SignWith([]byte{}, signer) + if err != nil { + return nil, xerrors.Errorf("failed to sign instruction "+ + "from EVM: %v", err) } - return stateChanges, nil + // Encode the instruction and store it in the state change's value + encodedInstr, err := protobuf.Encode(instr) + if err != nil { + return nil, xerrors.Errorf("failed to encode instruction "+ + "from EVM: %v", err) + } + + sc := byzcoin.NewStateChange( + byzcoin.GenerateInstruction, + byzcoin.NewInstanceID(nil), + "", + encodedInstr, + nil, + ) + + return &sc, nil }