Skip to content

Commit

Permalink
Leak events from Confidential requests (#211)
Browse files Browse the repository at this point in the history
* First try

* Clean all soliditiy code

* Remove code

* Clean a bit more

* Disable gas estimation?

* Update magic number

* Update magic number

* Address feedback
  • Loading branch information
ferranbt authored Mar 1, 2024
1 parent 546f5a5 commit 317f9eb
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 1 deletion.
20 changes: 20 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
suave "github.com/ethereum/go-ethereum/suave/core"
"github.com/tyler-smith/go-bip39"
)

Expand Down Expand Up @@ -1973,6 +1974,8 @@ func (m *mevmStateLogger) CaptureTxStart(gasLimit uint64) {}

func (m *mevmStateLogger) CaptureTxEnd(restGas uint64) {}

var magicBytes = hexutil.MustDecode("0x543543")

// TODO: should be its own api
func runMEVM(ctx context.Context, b Backend, state *state.StateDB, header *types.Header, tx *types.Transaction, msg *core.Message, isCall bool) (*types.Transaction, *core.ExecutionResult, func() error, error) {
var cancel context.CancelFunc
Expand Down Expand Up @@ -2044,6 +2047,23 @@ func runMEVM(ctx context.Context, b Backend, state *state.StateDB, header *types
computeResult = result.ReturnData // Or should it be nil maybe in this case?
}

logs := state.GetLogs(common.Hash{}, 0, common.Hash{})

if len(logs) != 0 {
result := suave.ExecResult{
Logs: logs,
}

logsEncoded, err := result.EncodeABI()
if err != nil {
return nil, nil, nil, err
}

computeResult = append(computeResult, magicBytes...)
computeResult = append(computeResult, logsEncoded...)
}

// encode logs to the ABI form
suaveResultTxData := &types.SuaveTransaction{ConfidentialComputeRequest: confidentialRequest.ConfidentialComputeRecord, ConfidentialComputeResult: computeResult}

signed, err := wallet.SignTx(account, types.NewTx(suaveResultTxData), tx.ChainId())
Expand Down
100 changes: 100 additions & 0 deletions suave/core/exec_result.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package suave

import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
ethgoAbi "github.com/umbracle/ethgo/abi"
)

type ExecResult struct {
Logs []*types.Log
}

// Equal compares two ExecResult structs and returns true if they are equal.
// We need a special equal function because `types.Log` is a struct with metadata information
// that is not included (not necessary) during `EncodeABI`.
func (e *ExecResult) Equal(other *ExecResult) bool {
if len(e.Logs) != len(other.Logs) {
return false
}

for i, log := range e.Logs {
if log.Address != other.Logs[i].Address {
return false
}

if len(log.Topics) != len(other.Logs[i].Topics) {
return false
}
for j, topic := range log.Topics {
if topic != other.Logs[i].Topics[j] {
return false
}
}

if len(log.Data) != len(other.Logs[i].Data) {
return false
}
for j, data := range log.Data {
if data != other.Logs[i].Data[j] {
return false
}
}
}
return true
}

var abiExecResult = ethgoAbi.MustNewType(`
tuple(tuple(address addr, bytes32[] topics, bytes data)[] logs)
`)

type execResultMarshal struct {
Logs []logMarshal
}

type logMarshal struct {
Addr common.Address
Topics []common.Hash
Data []byte
}

// EncodeABI encodes the ExecResult struct to an ABI byte array.
func (e *ExecResult) EncodeABI() ([]byte, error) {
// Convert logs to ABI
abiLogs := make([]logMarshal, len(e.Logs))
for i, log := range e.Logs {
abiLogs[i] = logMarshal{
Addr: log.Address,
Topics: log.Topics,
Data: log.Data,
}
}

execRes := execResultMarshal{
Logs: abiLogs,
}
data, err := abiExecResult.Encode(execRes)
if err != nil {
return nil, err
}
return data, nil
}

// DecodeABI decodes an ABI byte array to an ExecResult struct.
func (e *ExecResult) DecodeABI(data []byte) error {
execRes := execResultMarshal{}
if err := abiExecResult.DecodeStruct(data, &execRes); err != nil {
return err
}

e.Logs = make([]*types.Log, len(execRes.Logs))
for i, log := range execRes.Logs {
e.Logs[i] = &types.Log{
Address: log.Addr,
Topics: log.Topics,
Data: log.Data,
}
}

return nil
}
36 changes: 36 additions & 0 deletions suave/core/exec_result_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package suave

import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

func TestExecResult_ABIEncoding(t *testing.T) {
cases := []*ExecResult{
{
Logs: []*types.Log{
{
Address: common.Address{0x1},
Topics: []common.Hash{{0x2}, {0x3}},
Data: []byte{0x4},
},
},
},
}

for _, c := range cases {
data, err := c.EncodeABI()
if err != nil {
t.Errorf("Error encoding ABI: %v", err)
}
decoded := new(ExecResult)
if err := decoded.DecodeABI(data); err != nil {
t.Errorf("Error decoding ABI: %v", err)
}
if !c.Equal(decoded) {
t.Errorf("Decoded result is not equal to original: %v != %v", c, decoded)
}
}
}
50 changes: 49 additions & 1 deletion suave/e2e/workflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import (

func init() {
// use the gas estimation
sdk.SetDefaultGasLimit(0)
// sdk.SetDefaultGasLimit(0)
}

func TestIsConfidential(t *testing.T) {
Expand Down Expand Up @@ -1050,6 +1050,54 @@ func TestRelayBlockSubmissionContract(t *testing.T) {
require.True(t, ok)
}

func TestE2E_EmitLogs_Basic(t *testing.T) {
fr := newFramework(t, WithKettleAddress())
defer fr.Close()

clt := fr.NewSDKClient()

// We reuse the same address for both the source and target contract
contractAddr := common.Address{0x3}
sourceContract := sdk.GetContract(contractAddr, exampleCallSourceContract.Abi, clt)

res, err := sourceContract.SendTransaction("emitLog", []interface{}{}, nil)
require.NoError(t, err)

fr.suethSrv.ProgressChain()

receipt, err := res.Wait()
require.NoError(t, err)
require.Equal(t, receipt.Status, uint64(1))

require.Len(t, receipt.Logs, 1)
require.Equal(t, receipt.Logs[0].Address, contractAddr)

data, err := exampleCallSourceContract.Abi.Events["OffchainLogs"].Inputs.Unpack(receipt.Logs[0].Data)
require.NoError(t, err)

execResultBytes := data[0].([]byte)

var execResult suave.ExecResult
require.NoError(t, execResult.DecodeABI(execResultBytes))

require.Len(t, execResult.Logs, 5)

t.Log("Testcases for execution result encoding")
t.Log(hex.EncodeToString(execResultBytes))

// check topics size for each log
require.Len(t, execResult.Logs[0].Topics, 0)
require.Len(t, execResult.Logs[1].Topics, 1)
require.Len(t, execResult.Logs[2].Topics, 2)
require.Len(t, execResult.Logs[3].Topics, 3)
require.Len(t, execResult.Logs[4].Topics, 4)

// logs 2.. have data
for _, log := range execResult.Logs[2:] {
require.NotEmpty(t, log.Data)
}
}

func TestE2E_EstimateGas(t *testing.T) {
t.Parallel()

Expand Down
38 changes: 38 additions & 0 deletions suave/sol/standard_peekers/example.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,44 @@ contract ExampleEthCallSource {

return abi.encodeWithSelector(this.emptyCallback.selector);
}

event OffchainLogs(bytes data);

function emitLogCallback(uint256 num) public {
// From the msg.input, the 'confidential context' sequence
// starts at index 37 (4 signbature bytes + 32 bytes for uint256 + 3 bytes for the magic sequence)
uint256 magicSequenceIndex = 4 + 32 + 3;

bytes memory inputData = msg.data;
uint256 dataLength = inputData.length - magicSequenceIndex;

// Initialize memory for the data to decode
bytes memory dataToDecode = new bytes(dataLength);

// Copy the data to decode into the memory array
for (uint256 i = 0; i < dataLength; i++) {
dataToDecode[i] = inputData[magicSequenceIndex + i];
}

emit OffchainLogs(dataToDecode);
}

// Event with no indexed parameters
event EventAnonymous() anonymous;
event EventTopic1();
event EventTopic2(uint256 indexed num1, uint256 numNoIndex);
event EventTopic3(uint256 indexed num1, uint256 indexed num2, uint256 numNoIndex);
event EventTopic4(uint256 indexed num1, uint256 indexed num2, uint256 indexed num3, uint256 numNoIndex);

function emitLog() public payable returns (bytes memory) {
emit EventAnonymous();
emit EventTopic1();
emit EventTopic2(1, 1);
emit EventTopic3(1, 2, 2);
emit EventTopic4(1, 2, 3, 3);

return abi.encodeWithSelector(this.emitLogCallback.selector, 10);
}
}

contract ExampleEthCallTarget {
Expand Down

0 comments on commit 317f9eb

Please sign in to comment.