Skip to content

Commit

Permalink
call 'visibilityRules' method
Browse files Browse the repository at this point in the history
  • Loading branch information
tudor-malene committed Sep 19, 2024
1 parent 3e8c21f commit e0c8f78
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 26 deletions.
24 changes: 24 additions & 0 deletions contracts/src/lib/ContractTransparencyConfig.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache 2

// implement this interface if you want to configure the visibility rules of your smart contract
// the TEN platform will interpret this information
interface ContractTransparencyConfig {
// configuration per event log type
struct EventLogConfig {
bytes eventSignature;
bool isPublic; // everyone can see and query for this event
bool topic1CanView; // If the event is private, and this is true, it means that the address from topic1 is an EOA that can view this event
bool topic2CanView; // same
bool topic3CanView; // same
bool visibleToSender; // if true, the tx signer will see this event. Default false
}

struct VisibilityConfig {
bool isTransparent; // If true - the internal state via getStorageAt will be accessible to everyone. All events will be public. Default false
EventLogConfig[] eventLogConfigs; // mapping from event signature to visibility configs per event
}

// keep the logic independent of the environment
// max gas: 1 Million
function visibilityRules() external pure returns (VisibilityConfig memory);
}
93 changes: 93 additions & 0 deletions go/enclave/evm/ContractTransparency.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package evm

import (
"errors"
"math/big"
"strings"

ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)

// file generated by ethereum tooling to help calling the "visibilityRules" function

// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
_ = abi.ConvertType
)

// ContractTranspMetaData contains all meta data concerning the TransparencyConfig contract.
var ContractTranspMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[],\"name\":\"visibilityRules\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isTransparent\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"eventSignature\",\"type\":\"bytes\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"topic1CanView\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"topic2CanView\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"topic3CanView\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"visibleToSender\",\"type\":\"bool\"}],\"internalType\":\"structContractTransparencyConfig.EventLogConfig[]\",\"name\":\"eventLogConfigs\",\"type\":\"tuple[]\"}],\"internalType\":\"structContractTransparencyConfig.VisibilityConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]",
}

// ContractTransparencyConfigEventLogConfig is an auto generated low-level Go binding around an user-defined struct.
type ContractTransparencyConfigEventLogConfig struct {
EventSignature []byte
IsPublic bool
Topic1CanView bool
Topic2CanView bool
Topic3CanView bool
VisibleToSender bool
}

// ContractTransparencyConfigVisibilityConfig is an auto generated low-level Go binding around an user-defined struct.
type ContractTransparencyConfigVisibilityConfig struct {
IsTransparent bool
EventLogConfigs []ContractTransparencyConfigEventLogConfig
}

// TransparencyConfig is an auto generated Go binding around an Ethereum contract.
type TransparencyConfig struct {
TransparencyConfigCaller // Read-only binding to the contract
}

// TransparencyConfigCaller is an auto generated read-only Go binding around an Ethereum contract.
type TransparencyConfigCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}

// NewTransparencyConfigCaller creates a new read-only instance of TransparencyConfig, bound to a specific deployed contract.
func NewTransparencyConfigCaller(address common.Address, caller bind.ContractCaller) (*TransparencyConfigCaller, error) {
contract, err := bindContractTransp(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &TransparencyConfigCaller{contract: contract}, nil
}

// bindContractTransp binds a generic wrapper to an already deployed contract.
func bindContractTransp(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := ContractTranspMetaData.GetAbi()
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
}

// VisibilityRules is a free data retrieval call binding the contract method 0x30173dd1.
//
// Solidity: function visibilityRules() pure returns((bool,(bytes,bool,bool,bool,bool,bool)[]))
func (_ContractTransp *TransparencyConfigCaller) VisibilityRules(opts *bind.CallOpts) (ContractTransparencyConfigVisibilityConfig, error) {
var out []interface{}
err := _ContractTransp.contract.Call(opts, &out, "visibilityRules")
if err != nil {
return *new(ContractTransparencyConfigVisibilityConfig), err
}

out0 := *abi.ConvertType(out[0], new(ContractTransparencyConfigVisibilityConfig)).(*ContractTransparencyConfigVisibilityConfig)

return out0, err
}
75 changes: 71 additions & 4 deletions go/enclave/evm/evm_facade.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"math/big"
_ "unsafe"

"github.com/ethereum/go-ethereum"

"github.com/ethereum/go-ethereum/core/tracing"
"github.com/holiman/uint256"

Expand Down Expand Up @@ -153,6 +155,7 @@ func executeTransaction(
// calculate a random value per transaction
header.MixDigest = crypto.CalculateTxRnd(before.Bytes(), tCount)

var vmenv *vm.EVM
applyTx := func(
config *params.ChainConfig,
bc gethcore.ChainContext,
Expand Down Expand Up @@ -196,7 +199,7 @@ func executeTransaction(

// Create a new context to be used in the EVM environment
blockContext := gethcore.NewEVMBlockContext(header, bc, author)
vmenv := vm.NewEVM(blockContext, vm.TxContext{BlobHashes: tx.Tx.BlobHashes(), GasPrice: header.BaseFee}, statedb, config, cfg)
vmenv = vm.NewEVM(blockContext, vm.TxContext{BlobHashes: tx.Tx.BlobHashes(), GasPrice: header.BaseFee}, statedb, config, cfg)
var receipt *types.Receipt
receipt, err = gethcore.ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx.Tx, usedGas, vmenv)
if err != nil {
Expand Down Expand Up @@ -241,16 +244,64 @@ func executeTransaction(
return &core.TxExecResult{Receipt: receipt, Err: err}
}

// todo - placeholder for calling the visibility config function on the newly created contracts
// this is step 1 of the transition to configured visibility rules. The auto-detection of the visibility rules
contractsWithVisibility := make(map[gethcommon.Address]*core.ContractVisibilityConfig)
for _, contractAddress := range createdContracts {
contractsWithVisibility[*contractAddress] = &core.ContractVisibilityConfig{AutoConfig: true}
visibCfg, err := readVisibilityConfig(vmenv, contractAddress)
if err != nil {
return &core.TxExecResult{Receipt: receipt, Err: err}
}
contractsWithVisibility[*contractAddress] = visibCfg
}

return &core.TxExecResult{Receipt: receipt, CreatedContracts: contractsWithVisibility}
}

// hardcode at 1 million gas.
const (
maxGasForVisibility = 1_000_000
execRevertedErr = "execution reverted"
)

func readVisibilityConfig(vmenv *vm.EVM, contractAddress *gethcommon.Address) (*core.ContractVisibilityConfig, error) {
cc, err := NewTransparencyConfigCaller(*contractAddress, &localContractCaller{evm: vmenv, maxGasForVisibility: maxGasForVisibility})
if err != nil {
return nil, fmt.Errorf("could not create transparency config caller: %w", err)
}
visibilityRules, err := cc.VisibilityRules(nil)
if err != nil && err.Error() == execRevertedErr {
// there is no visibility defined, so we return auto
return &core.ContractVisibilityConfig{AutoConfig: true}, nil
}

if err != nil {
return nil, fmt.Errorf("could not get visibility rules: %w", err)
}

cfg := &core.ContractVisibilityConfig{
AutoConfig: false,
Transparent: &visibilityRules.IsTransparent,
EventConfigs: make(map[gethcommon.Hash]*core.EventVisibilityConfig),
}

for i := range visibilityRules.EventLogConfigs {
logConfig := visibilityRules.EventLogConfigs[i]

sig := gethcommon.Hash{}
sig.SetBytes(logConfig.EventSignature)

cfg.EventConfigs[sig] = &core.EventVisibilityConfig{
AutoConfig: false,
Public: logConfig.IsPublic,
Topic1CanView: &logConfig.Topic1CanView,
Topic2CanView: &logConfig.Topic2CanView,
Topic3CanView: &logConfig.Topic3CanView,
SenderCanView: &logConfig.VisibleToSender,
}
}

return cfg, nil
}

func logReceipt(r *types.Receipt, logger gethlog.Logger) {
if logger.Enabled(context.Background(), gethlog.LevelTrace) {
logger.Trace("Receipt", log.TxKey, r.TxHash, "Result", receiptToString(r))
Expand Down Expand Up @@ -367,3 +418,19 @@ func newRevertError(result *gethcore.ExecutionResult) error {
Code: 3, // todo - magic number, really needs thought around the value and made a constant
}
}

// used as a wrapper around the vm.EVM to allow for easier calling of smart contract view functions
type localContractCaller struct {
evm *vm.EVM
maxGasForVisibility uint64
}

// CodeAt - not implemented because it's not needed for our use case. It just has to return something non-nil
func (cc *localContractCaller) CodeAt(_ context.Context, _ gethcommon.Address, _ *big.Int) ([]byte, error) {
return []byte{0}, nil
}

func (cc *localContractCaller) CallContract(_ context.Context, call ethereum.CallMsg, _ *big.Int) ([]byte, error) {
ret, _, err := cc.evm.Call(vm.AccountRef(call.From), *call.To, call.Data, cc.maxGasForVisibility, uint256.NewInt(0))
return ret, err
}
9 changes: 6 additions & 3 deletions go/enclave/storage/events_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ func (es *eventsStorage) storeTopics(ctx context.Context, dbTX *sql.Tx, eventTyp
}

// this function contains visibility logic
func (es *eventsStorage) storeTopic(ctx context.Context, dbTX *sql.Tx, eventType *enclavedb.EventType, i int, topic gethcommon.Hash) (uint64, error) {
relevantAddress, err := es.determineRelevantAddressForTopic(ctx, dbTX, eventType, i, topic)
func (es *eventsStorage) storeTopic(ctx context.Context, dbTX *sql.Tx, eventType *enclavedb.EventType, topicNo int, topic gethcommon.Hash) (uint64, error) {
relevantAddress, err := es.determineRelevantAddressForTopic(ctx, dbTX, eventType, topicNo, topic)
if err != nil && !errors.Is(err, errutil.ErrNotFound) {
return 0, fmt.Errorf("could not determine visibility rules. cause: %w", err)
}
Expand Down Expand Up @@ -275,8 +275,11 @@ func (es *eventsStorage) determineRelevantAddressForTopic(ctx context.Context, d

case eventType.IsTopicRelevant(topicNumber):
relevantAddress = common.ExtractPotentialAddress(topic)
// it is possible for contracts to emit events without an actual address.
// for example. ERC20.mint emits a transfer event from a "0" address
if relevantAddress == nil {
return nil, fmt.Errorf("invalid configuration. expected address in topic %d : %s", topicNumber, topic.String())
es.logger.Debug(fmt.Sprintf("invalid configuration. expected address in topic %d : %s", topicNumber, topic.String()))
return nil, errutil.ErrNotFound
}

case !eventType.IsTopicRelevant(topicNumber):
Expand Down
56 changes: 44 additions & 12 deletions integration/erc20contract/ObsERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,63 @@ pragma solidity ^0.8.4;
import "libs/openzeppelin/contracts/token/ERC20/ERC20.sol";
//import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// todo - can't import from the /contracts folder
// implement this interface if you want to configure the visibility rules of your smart contract
// the TEN platform will interpret this information
interface ContractTransparencyConfig {
// configuration per event log type
struct EventLogConfig {
bytes eventSignature;
bool isPublic; // everyone can see and query for this event
bool topic1CanView; // If the event is private, and this is true, it means that the address from topic1 is an EOA that can view this event
bool topic2CanView; // same
bool topic3CanView; // same
bool visibleToSender; // if true, the tx signer will see this event. Default false
}

struct VisibilityConfig {
bool isTransparent; // If true - the internal state via getStorageAt will be accessible to everyone. All events will be public. Default false
EventLogConfig[] eventLogConfigs; // mapping from event signature to visibility configs per event
}

// keep the logic independent of the environment
// max gas: 1 Million
function visibilityRules() external pure returns (VisibilityConfig memory);
}

interface Structs {
struct CrossChainMessage {
address sender;
uint64 sequence;
uint32 nonce;
bytes topic;
bytes payload;
uint8 consistencyLevel;
uint64 sequence;
uint32 nonce;
bytes topic;
bytes payload;
uint8 consistencyLevel;
}
}

interface IMessageBus {
function publishMessage(
uint32 nonce,
uint32 topic,
bytes calldata payload,
bytes calldata payload,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
}

// This is an implementation of a canonical ERC20 as used in the Obscuro network
// where access to data has to be restricted.
contract ObsERC20 is ERC20 {
contract ObsERC20 is ERC20, ContractTransparencyConfig {

address bridge = 0xdeB34A740ECa1eC42C8b8204CBEC0bA34FDD27f3;

IMessageBus bus;

enum Topics{
MINT,
TRANSFER
enum Topics{
MINT,
TRANSFER
}

struct AssetTransferMessage {
address sender;
address receiver;
Expand All @@ -52,14 +77,21 @@ contract ObsERC20 is ERC20 {
bus = IMessageBus(busAddress);
}

function visibilityRules() public override pure returns (VisibilityConfig memory){
EventLogConfig[] memory configs = new EventLogConfig[](1);
// erc20 transfer
configs[0] = EventLogConfig(hex"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", false, true, true, false, false);
return VisibilityConfig(false, configs);
}

function _beforeTokenTransfer(address from, address to, uint256 amount)
internal virtual override {
//Only deposit messages.
if (address(bus) == address(0x0)) {
return;
}

if (to == bridge) {
if (to == bridge) {
AssetTransferMessage memory message = AssetTransferMessage(from, to, amount);
uint64 sequence = bus.publishMessage(uint32(block.number), uint32(Topics.TRANSFER), abi.encode(message), 0);
}
Expand Down
Loading

0 comments on commit e0c8f78

Please sign in to comment.