From 3b47282c372532f34e8fcf8d9c44acfde6860dbe Mon Sep 17 00:00:00 2001 From: laizy Date: Thu, 24 Mar 2022 23:49:35 +0800 Subject: [PATCH] add evm EventWitness system contract (#1396) * ttt * add evm EventWitness system contract * add license * use merkle leaf hash scheme in EventWitness.sol * update solc output * expose jsonrpc api --- core/store/ledgerstore/ledger_store.go | 26 +++++++-- core/store/ledgerstore/tx_handler.go | 40 ++++++++++++-- core/store/store.go | 1 + http/base/actor/ledger.go | 4 ++ http/base/common/common.go | 5 ++ http/ethrpc/web3/api.go | 3 +- http/jsonrpc/interfaces.go | 20 +++++++ http/jsonrpc/rpc_server.go | 1 + .../service/evm/witness/EventWitness.json | 44 +++++++++++++++ .../service/evm/witness/EventWitness.sol | 14 +++++ smartcontract/service/evm/witness/witness.go | 54 +++++++++++++++++++ 11 files changed, 203 insertions(+), 9 deletions(-) create mode 100644 smartcontract/service/evm/witness/EventWitness.json create mode 100644 smartcontract/service/evm/witness/EventWitness.sol create mode 100644 smartcontract/service/evm/witness/witness.go diff --git a/core/store/ledgerstore/ledger_store.go b/core/store/ledgerstore/ledger_store.go index b0708a732..cbfc1c446 100644 --- a/core/store/ledgerstore/ledger_store.go +++ b/core/store/ledgerstore/ledger_store.go @@ -54,6 +54,7 @@ import ( "github.com/ontio/ontology/smartcontract/event" "github.com/ontio/ontology/smartcontract/service/evm" types4 "github.com/ontio/ontology/smartcontract/service/evm/types" + "github.com/ontio/ontology/smartcontract/service/evm/witness" "github.com/ontio/ontology/smartcontract/service/native/ong" "github.com/ontio/ontology/smartcontract/service/native/utils" "github.com/ontio/ontology/smartcontract/service/neovm" @@ -743,6 +744,7 @@ func (this *LedgerStoreImp) saveBlockToBlockStore(block *types.Block) error { func (this *LedgerStoreImp) executeBlock(block *types.Block) (result store.ExecuteResult, err error) { overlay := this.stateStore.NewOverlayDB() + var evmWitness common2.Address if block.Header.Height != 0 { config := &smartcontract.Config{ Time: block.Header.Timestamp, @@ -754,6 +756,7 @@ func (this *LedgerStoreImp) executeBlock(block *types.Block) (result store.Execu if err != nil { return } + evmWitness = getEvmSystemWitnessAddress(config, storage.NewCacheDB(this.stateStore.NewOverlayDB()), this) } gasTable := make(map[string]uint64) neovm.GAS_TABLE.Range(func(k, value interface{}) bool { @@ -766,7 +769,7 @@ func (this *LedgerStoreImp) executeBlock(block *types.Block) (result store.Execu cache := storage.NewCacheDB(overlay) for i, tx := range block.Transactions { cache.Reset() - notify, crossStateHashes, e := this.handleTransaction(overlay, cache, gasTable, block, tx, uint32(i)) + notify, crossStateHashes, e := this.handleTransaction(overlay, cache, gasTable, block, tx, uint32(i), evmWitness) if e != nil { err = e return @@ -1023,7 +1026,7 @@ func (this *LedgerStoreImp) saveBlock(block *types.Block, ccMsg *types.CrossChai } func (this *LedgerStoreImp) handleTransaction(overlay *overlaydb.OverlayDB, cache *storage.CacheDB, gasTable map[string]uint64, - block *types.Block, tx *types.Transaction, txIndex uint32) (*event.ExecuteNotify, []common.Uint256, error) { + block *types.Block, tx *types.Transaction, txIndex uint32, evmWitness common2.Address) (*event.ExecuteNotify, []common.Uint256, error) { txHash := tx.Hash() notify := &event.ExecuteNotify{TxHash: txHash, State: event.CONTRACT_STATE_FAIL, TxIndex: txIndex} var crossStateHashes []common.Uint256 @@ -1057,13 +1060,24 @@ func (this *LedgerStoreImp) handleTransaction(overlay *overlaydb.OverlayDB, cach Height: block.Header.Height, Timestamp: block.Header.Timestamp, } - _, err = this.stateStore.HandleEIP155Transaction(this, cache, eiptx, ctx, notify, true) + _, receipt, err := this.stateStore.HandleEIP155Transaction(this, cache, eiptx, ctx, notify, true) if overlay.Error() != nil { return nil, nil, fmt.Errorf("HandleInvokeTransaction tx %s error %s", txHash.ToHexString(), overlay.Error()) } if err != nil { log.Debugf("HandleInvokeTransaction tx %s error %s", txHash.ToHexString(), err) } + for _, log := range receipt.Logs { + if log.Address != evmWitness { + continue + } + event, err := witness.DecodeEventWitness(log) + if err != nil { + continue + } + + crossStateHashes = append(crossStateHashes, event.Hash) + } } return notify, crossStateHashes, nil } @@ -1122,6 +1136,10 @@ func (this *LedgerStoreImp) GetBlockRootWithNewTxRoots(startHeight uint32, txRoo return this.stateStore.GetBlockRootWithNewTxRoots(needs) } +func (this *LedgerStoreImp) GetCrossStates(height uint32) ([]common.Uint256, error) { + return this.stateStore.GetCrossStates(height) +} + func (this *LedgerStoreImp) GetCrossStatesRoot(height uint32) (common.Uint256, error) { return this.stateStore.GetCrossStatesRoot(height) } @@ -1265,7 +1283,7 @@ func (this *LedgerStoreImp) PreExecuteEIP155(tx *types3.Transaction, ctx Eip155C cache := storage.NewCacheDB(overlay) notify := &event.ExecuteNotify{State: event.CONTRACT_STATE_FAIL, TxIndex: ctx.TxIndex} - result, err := this.stateStore.HandleEIP155Transaction(this, cache, tx, ctx, notify, false) + result, _, err := this.stateStore.HandleEIP155Transaction(this, cache, tx, ctx, notify, false) return result, notify, err } diff --git a/core/store/ledgerstore/tx_handler.go b/core/store/ledgerstore/tx_handler.go index 6a2e37de7..c0bfc1a19 100644 --- a/core/store/ledgerstore/tx_handler.go +++ b/core/store/ledgerstore/tx_handler.go @@ -39,6 +39,7 @@ import ( "github.com/ontio/ontology/smartcontract/event" evm2 "github.com/ontio/ontology/smartcontract/service/evm" types3 "github.com/ontio/ontology/smartcontract/service/evm/types" + "github.com/ontio/ontology/smartcontract/service/evm/witness" "github.com/ontio/ontology/smartcontract/service/native/global_params" ninit "github.com/ontio/ontology/smartcontract/service/native/init" "github.com/ontio/ontology/smartcontract/service/native/ong" @@ -339,6 +340,37 @@ func chargeCostGas(payer common.Address, gas uint64, config *smartcontract.Confi return sc.Notifications, nil } +func getEvmSystemWitnessAddress(config *smartcontract.Config, cache *storage.CacheDB, store store.LedgerStore) common2.Address { + sink := common.NewZeroCopySink(nil) + utils.EncodeVarUint(sink, 1) + sink.WriteString(witness.WitnessGlobalParamKey) + + sc := smartcontract.SmartContract{ + Config: config, + CacheDB: cache, + Store: store, + Gas: math.MaxUint64, + } + + service, _ := sc.NewNativeService() + result, err := service.NativeCall(utils.ParamContractAddress, "getGlobalParam", sink.Bytes()) + if err != nil { + log.Errorf("get witness address error: %s", err) + return common2.Address{} + } + params := new(global_params.Params) + if err := params.Deserialization(common.NewZeroCopySource(result)); err != nil { + log.Errorf("deserialize global params error:%s", err) + return common2.Address{} + } + n, ps := params.GetParam(witness.WitnessGlobalParamKey) + if n != -1 && ps.Value != "" { + return common2.HexToAddress(ps.Value) + } + + return common2.Address{} +} + func refreshGlobalParam(config *smartcontract.Config, cache *storage.CacheDB, store store.LedgerStore) error { sink := common.NewZeroCopySink(nil) utils.EncodeVarUint(sink, uint64(len(neovm.GAS_TABLE_KEYS))) @@ -420,7 +452,7 @@ type Eip155Context struct { } func (self *StateStore) HandleEIP155Transaction(store store.LedgerStore, cache *storage.CacheDB, - tx *types2.Transaction, ctx Eip155Context, notify *event.ExecuteNotify, checkNonce bool) (*types3.ExecutionResult, error) { + tx *types2.Transaction, ctx Eip155Context, notify *event.ExecuteNotify, checkNonce bool) (*types3.ExecutionResult, *types.Receipt, error) { usedGas := uint64(0) config := params.GetChainConfig(sysconfig.DefConfig.P2PNode.EVMChainId) statedb := storage.NewStateDB(cache, tx.Hash(), common2.Hash(ctx.BlockHash), ong.OngBalanceHandle{}) @@ -429,15 +461,15 @@ func (self *StateStore) HandleEIP155Transaction(store store.LedgerStore, cache * if err != nil { cache.SetDbErr(err) - return nil, err + return nil, nil, err } if err = statedb.DbErr(); err != nil { cache.SetDbErr(err) - return nil, err + return nil, nil, err } receipt.TxIndex = ctx.TxIndex *notify = *event.ExecuteNotifyFromEthReceipt(receipt) - return result, nil + return result, receipt, nil } diff --git a/core/store/store.go b/core/store/store.go index 3babba4fc..6fb62b4b0 100644 --- a/core/store/store.go +++ b/core/store/store.go @@ -79,6 +79,7 @@ type LedgerStore interface { GetEthAccount(address common2.Address) (*storage.EthAccount, error) //cross chain states root GetCrossStatesRoot(height uint32) (common.Uint256, error) + GetCrossStates(height uint32) ([]common.Uint256, error) GetCrossChainMsg(height uint32) (*types.CrossChainMsg, error) GetCrossStatesProof(height uint32, key []byte) ([]byte, error) EnableBlockPrune(numBeforeCurr uint32) diff --git a/http/base/actor/ledger.go b/http/base/actor/ledger.go index 2b8721551..c1ed1c485 100644 --- a/http/base/actor/ledger.go +++ b/http/base/actor/ledger.go @@ -117,6 +117,10 @@ func GetCrossChainMsg(height uint32) (*types.CrossChainMsg, error) { return ledger.DefLedger.GetCrossChainMsg(height) } +func GetCrossStatesLeafHashes(height uint32) ([]common.Uint256, error) { + return ledger.DefLedger.GetCrossStates(height) +} + func GetCrossStatesProof(height uint32, key []byte) ([]byte, error) { return ledger.DefLedger.GetCrossStatesProof(height, key) } diff --git a/http/base/common/common.go b/http/base/common/common.go index 05e592948..ea26434e3 100644 --- a/http/base/common/common.go +++ b/http/base/common/common.go @@ -133,6 +133,11 @@ type CrossStatesProof struct { AuditPath string } +type CrossStatesLeafHashes struct { + Height uint32 + Hashes []string +} + type Transactions struct { Version byte Nonce uint32 diff --git a/http/ethrpc/web3/api.go b/http/ethrpc/web3/api.go index 9f345dc07..5cb45bbc5 100644 --- a/http/ethrpc/web3/api.go +++ b/http/ethrpc/web3/api.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" + "github.com/ontio/ontology/common/config" ) // PublicWeb3API is the web3_ prefixed set of APIs in the Web3 JSON-RPC spec. @@ -34,7 +35,7 @@ func NewAPI() *PublicWeb3API { // ClientVersion returns the client version in the Web3 user agent format. func (PublicWeb3API) ClientVersion() string { - return fmt.Sprintf("%s-%s", "Ontology", "1.0.0") + return fmt.Sprintf("%s-%s", "Ontology", config.Version) } // Sha3 returns the keccak-256 hash of the passed-in input. diff --git a/http/jsonrpc/interfaces.go b/http/jsonrpc/interfaces.go index 3b7c4f4a9..e2675f369 100644 --- a/http/jsonrpc/interfaces.go +++ b/http/jsonrpc/interfaces.go @@ -694,6 +694,26 @@ func GetCrossChainMsg(params []interface{}) map[string]interface{} { return rpc.ResponseSuccess(bcomn.TransferCrossChainMsg(msg, header.Bookkeepers)) } +func GetCrossStatesLeafHashes(params []interface{}) map[string]interface{} { + if len(params) < 1 { + return rpc.ResponsePack(berr.INVALID_PARAMS, "") + } + height, ok := (params[0]).(float64) + if !ok { + return rpc.ResponsePack(berr.INVALID_PARAMS, "") + } + hashes, err := bactor.GetCrossStatesLeafHashes(uint32(height)) + if err != nil { + log.Errorf("GetCrossStateLeafHashes error: %s", err) + return rpc.ResponsePack(berr.INTERNAL_ERROR, "") + } + var hexHashes []string + for _, v := range hashes { + hexHashes = append(hexHashes, v.ToHexString()) + } + return rpc.ResponseSuccess(bcomn.CrossStatesLeafHashes{Height: uint32(height), Hashes: hexHashes}) +} + //get cross chain state proof func GetCrossStatesProof(params []interface{}) map[string]interface{} { if len(params) < 1 { diff --git a/http/jsonrpc/rpc_server.go b/http/jsonrpc/rpc_server.go index c52de16b9..5d316c9f2 100644 --- a/http/jsonrpc/rpc_server.go +++ b/http/jsonrpc/rpc_server.go @@ -66,6 +66,7 @@ func StartRPCServer() error { rpc.HandleFunc("getcrosschainmsg", GetCrossChainMsg) rpc.HandleFunc("getcrossstatesproof", GetCrossStatesProof) + rpc.HandleFunc("getcrossstatesleafhashes", GetCrossStatesLeafHashes) err := http.ListenAndServe(":"+strconv.Itoa(int(cfg.DefConfig.Rpc.HttpJsonPort)), nil) if err != nil { diff --git a/smartcontract/service/evm/witness/EventWitness.json b/smartcontract/service/evm/witness/EventWitness.json new file mode 100644 index 000000000..16cdb7669 --- /dev/null +++ b/smartcontract/service/evm/witness/EventWitness.json @@ -0,0 +1,44 @@ +{ + "abi": [ + { + "type": "function", + "name": "witness", + "inputs": [ + { + "internalType": "bytes", + "name": "evt", + "type": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "EventWitnessed", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true + }, + { + "name": "hash", + "type": "bytes32", + "indexed": true + } + ], + "anonymous": false + } + ], + "bytecode": { + "object": "0x608060405234801561001057600080fd5b50610232806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c806301520d3714610030575b600080fd5b61004361003e3660046100f8565b610045565b005b60405160009061005f90829033908690869060200161016a565b6040516020818303038152906040529050600060028260405161008291906101a8565b602060405180830381855afa15801561009f573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906100c291906101e3565b604051909150819033907f79898126427c5e89fd1ee4f972f2a7555a74aa1248b7a0e1e50b8e0180be00bd90600090a350505050565b6000806020838503121561010b57600080fd5b823567ffffffffffffffff8082111561012357600080fd5b818501915085601f83011261013757600080fd5b81358181111561014657600080fd5b86602082850101111561015857600080fd5b60209290920196919550909350505050565b6001600160f81b031985168152606084901b6bffffffffffffffffffffffff1916600182015281836015830137600091016015019081529392505050565b6000825160005b818110156101c957602081860181015185830152016101af565b818111156101d8576000828501525b509190910192915050565b6000602082840312156101f557600080fd5b505191905056fea2646970667358221220261559f20ea5fce40c92f04e51feb223d0c0b5ed0f4f82e98eda6922b001bb3f64736f6c634300080a0033", + "sourceMap": "64:460:1:-:0;;;;;;;;;;;;;;;;;;;", + "linkReferences": {} + }, + "deployed_bytecode": { + "object": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c806301520d3714610030575b600080fd5b61004361003e3660046100f8565b610045565b005b60405160009061005f90829033908690869060200161016a565b6040516020818303038152906040529050600060028260405161008291906101a8565b602060405180830381855afa15801561009f573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906100c291906101e3565b604051909150819033907f79898126427c5e89fd1ee4f972f2a7555a74aa1248b7a0e1e50b8e0180be00bd90600090a350505050565b6000806020838503121561010b57600080fd5b823567ffffffffffffffff8082111561012357600080fd5b818501915085601f83011261013757600080fd5b81358181111561014657600080fd5b86602082850101111561015857600080fd5b60209290920196919550909350505050565b6001600160f81b031985168152606084901b6bffffffffffffffffffffffff1916600182015281836015830137600091016015019081529392505050565b6000825160005b818110156101c957602081860181015185830152016101af565b818111156101d8576000828501525b509190910192915050565b6000602082840312156101f557600080fd5b505191905056fea2646970667358221220261559f20ea5fce40c92f04e51feb223d0c0b5ed0f4f82e98eda6922b001bb3f64736f6c634300080a0033", + "sourceMap": "64:460:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;165:357;;;;;;:::i;:::-;;:::i;:::-;;;360:44;;333:24;;360:44;;333:24;;388:10;;400:3;;;;360:44;;;:::i;:::-;;;;;;;;;;;;;333:71;;414:22;439:19;446:11;439:19;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;473:42;;414:44;;-1:-1:-1;414:44:1;;488:10;;473:42;;;;;211:311;;165:357;;:::o;14:591:4:-;84:6;92;145:2;133:9;124:7;120:23;116:32;113:52;;;161:1;158;151:12;113:52;201:9;188:23;230:18;271:2;263:6;260:14;257:34;;;287:1;284;277:12;257:34;325:6;314:9;310:22;300:32;;370:7;363:4;359:2;355:13;351:27;341:55;;392:1;389;382:12;341:55;432:2;419:16;458:2;450:6;447:14;444:34;;;474:1;471;464:12;444:34;519:7;514:2;505:6;501:2;497:15;493:24;490:37;487:57;;;540:1;537;530:12;487:57;571:2;563:11;;;;;593:6;;-1:-1:-1;14:591:4;;-1:-1:-1;;;;14:591:4:o;610:474::-;-1:-1:-1;;;;;;833:26:4;;821:39;;897:2;893:15;;;-1:-1:-1;;889:53:4;885:1;876:11;;869:74;987:6;979;974:2;965:12;;952:42;803:3;1017:16;;1035:2;1013:25;1047:13;;;1013:25;610:474;-1:-1:-1;;;610:474:4:o;1089:426::-;1218:3;1256:6;1250:13;1281:1;1291:129;1305:6;1302:1;1299:13;1291:129;;;1403:4;1387:14;;;1383:25;;1377:32;1364:11;;;1357:53;1320:12;1291:129;;;1438:6;1435:1;1432:13;1429:48;;;1473:1;1464:6;1459:3;1455:16;1448:27;1429:48;-1:-1:-1;1493:16:4;;;;;1089:426;-1:-1:-1;;1089:426:4:o;1520:184::-;1590:6;1643:2;1631:9;1622:7;1618:23;1614:32;1611:52;;;1659:1;1656;1649:12;1611:52;-1:-1:-1;1682:16:4;;1520:184;-1:-1:-1;1520:184:4:o", + "linkReferences": {} + } +} \ No newline at end of file diff --git a/smartcontract/service/evm/witness/EventWitness.sol b/smartcontract/service/evm/witness/EventWitness.sol new file mode 100644 index 000000000..3afd04e28 --- /dev/null +++ b/smartcontract/service/evm/witness/EventWitness.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +contract EventWitness { + event EventWitnessed(address indexed sender, bytes32 indexed hash); + + function witness(bytes calldata evt) external { + // ugly hack: append byte1(0) since we are hashing a merkle leaf. see: ontology/merkle/merkle_hasher.go + bytes memory leafData = abi.encodePacked(bytes1(0), msg.sender, evt); + bytes32 merkleLeafHash = sha256(leafData); + emit EventWitnessed(msg.sender, merkleLeafHash); + } +} + diff --git a/smartcontract/service/evm/witness/witness.go b/smartcontract/service/evm/witness/witness.go new file mode 100644 index 000000000..499211406 --- /dev/null +++ b/smartcontract/service/evm/witness/witness.go @@ -0,0 +1,54 @@ +// Copyright (C) 2021 The Ontology Authors +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package witness + +import ( + "errors" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ontio/ontology/common" + "github.com/ontio/ontology/core/types" +) + +const WitnessGlobalParamKey = "evm.witness" // value is the deployed hex addresss(evm form) of witness contract. + +var EventWitnessedEventID = crypto.Keccak256Hash([]byte("EventWitnessed(address,bytes32)")) + +type EventWitnessEvent struct { + Sender common.Address + Hash common.Uint256 +} + +func DecodeEventWitness(log *types.StorageLog) (*EventWitnessEvent, error) { + if len(log.Topics) != 3 { + return nil, errors.New("witness: wrong topic number") + } + if log.Topics[0] != EventWitnessedEventID { + return nil, errors.New("witness: wrong event id") + } + + sender, err := common.AddressParseFromBytes(log.Topics[1][12:]) + if err != nil { + return nil, err + } + + return &EventWitnessEvent{ + Sender: sender, + Hash: common.Uint256(log.Topics[2]), + }, nil +}