diff --git a/e2e/framework/helper.go b/e2e/framework/helper.go index c8cebe319b..6b5eecba54 100644 --- a/e2e/framework/helper.go +++ b/e2e/framework/helper.go @@ -460,6 +460,11 @@ func NewTestServers(t *testing.T, num int, conf func(*TestServerConfig)) []*Test } }) + logsDir, err := initLogsDir(t) + if err != nil { + t.Fatal(err) + } + // It is safe to use a dummy MultiAddr here, since this init method // is called for the Dev consensus mode, and IBFT servers are initialized with NewIBFTServersManager. // This method needs to be standardized in the future @@ -471,7 +476,11 @@ func NewTestServers(t *testing.T, num int, conf func(*TestServerConfig)) []*Test t.Fatal(err) } - srv := NewTestServer(t, dataDir, conf) + srv := NewTestServer(t, dataDir, func(c *TestServerConfig) { + c.SetLogsDir(logsDir) + c.SetSaveLogs(true) + conf(c) + }) srv.Config.SetBootnodes(bootnodes) srvs = append(srvs, srv) diff --git a/go.mod b/go.mod index 0cb94b5b59..6d38c66173 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/klauspost/compress v1.15.5 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mitchellh/mapstructure v1.5.0 - github.com/umbracle/ethgo v0.1.4-0.20220722090909-c8ac32939570 + github.com/umbracle/ethgo v0.1.4-0.20221117101647-b81ef2f07953 github.com/valyala/fastjson v1.6.3 // indirect go.uber.org/zap v1.22.0 // indirect golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect @@ -88,7 +88,6 @@ require ( github.com/andybalholm/brotli v1.0.4 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/boltdb/bolt v1.3.1 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect github.com/bwesterb/go-ristretto v1.2.0 // indirect diff --git a/go.sum b/go.sum index ea5581e957..eda247d048 100644 --- a/go.sum +++ b/go.sum @@ -96,8 +96,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= @@ -739,8 +737,8 @@ github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKw github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/umbracle/ethgo v0.1.4-0.20220722090909-c8ac32939570 h1:/KyTftQQhxq0iRIVRocn0F2D4zoHmstIfB4FTDjsZbw= -github.com/umbracle/ethgo v0.1.4-0.20220722090909-c8ac32939570/go.mod h1:g9zclCLixH8liBI27Py82klDkW7Oo33AxUOr+M9lzrU= +github.com/umbracle/ethgo v0.1.4-0.20221117101647-b81ef2f07953 h1:ep+lwZyyeh1iw8UojTxslDsqPw0305Vu6lOiEebWS8k= +github.com/umbracle/ethgo v0.1.4-0.20221117101647-b81ef2f07953/go.mod h1:8QIHEG/YfGnW4I5AND2Znl9W0LU3tXR9IGqgmSieiGo= github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 h1:10Nbw6cACsnQm7r34zlpJky+IzxVLRk6MKTS2d3Vp0E= github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722/go.mod h1:c8J0h9aULj2i3umrfyestM6jCq0LK0U6ly6bWy96nd4= github.com/umbracle/go-eth-bn256 v0.0.0-20190607160430-b36caf4e0f6b h1:t3nz9xXkLZJz+ZlTGFT3ixsCGO5AHx1Yift2EAfjnnc= diff --git a/helper/predeployment/predeployment.go b/helper/predeployment/predeployment.go index 633a4c457f..ce6d0f4ce6 100644 --- a/helper/predeployment/predeployment.go +++ b/helper/predeployment/predeployment.go @@ -124,7 +124,7 @@ func getPredeployAccount(address types.Address, input, deployedBytecode []byte) snapshot := st.NewSnapshot() // Create a radix - radix := state.NewTxn(st, snapshot) + radix := state.NewTxn(snapshot) // Create the contract object for the EVM contract := runtime.NewContractCreation( @@ -141,7 +141,7 @@ func getPredeployAccount(address types.Address, input, deployedBytecode []byte) config := chain.AllForksEnabled.At(0) // Create a transition - transition := state.NewTransition(config, radix) + transition := state.NewTransition(config, snapshot, radix) // Run the transition through the EVM res := evm.NewEVM().Run(contract, transition, &config) diff --git a/jsonrpc/eth_blockchain_test.go b/jsonrpc/eth_blockchain_test.go index eb5e88ef29..74025ea8c5 100644 --- a/jsonrpc/eth_blockchain_test.go +++ b/jsonrpc/eth_blockchain_test.go @@ -450,15 +450,6 @@ func (m *mockBlockStore) GetReceiptsByHash(hash types.Hash) ([]*types.Receipt, e return receipts, nil } -func (m *mockBlockStore) GetHeaderByNumber(blockNumber uint64) (*types.Header, bool) { - b, ok := m.GetBlockByNumber(blockNumber, false) - if !ok { - return nil, false - } - - return b.Header, true -} - func (m *mockBlockStore) GetBlockByNumber(blockNumber uint64, full bool) (*types.Block, bool) { for _, b := range m.blocks { if b.Number() == blockNumber { diff --git a/jsonrpc/eth_endpoint.go b/jsonrpc/eth_endpoint.go index 99cbdc4a08..7997d23acf 100644 --- a/jsonrpc/eth_endpoint.go +++ b/jsonrpc/eth_endpoint.go @@ -27,20 +27,22 @@ type ethTxPoolStore interface { GetPendingTx(txHash types.Hash) (*types.Transaction, bool) } +type Account struct { + Balance *big.Int + Nonce uint64 +} + type ethStateStore interface { - GetAccount(root types.Hash, addr types.Address) (*state.Account, error) + GetAccount(root types.Hash, addr types.Address) (*Account, error) GetStorage(root types.Hash, addr types.Address, slot types.Hash) ([]byte, error) GetForksInTime(blockNumber uint64) chain.ForksInTime - GetCode(hash types.Hash) ([]byte, error) + GetCode(root types.Hash, addr types.Address) ([]byte, error) } type ethBlockchainStore interface { // Header returns the current header of the chain (genesis if empty) Header() *types.Header - // GetHeaderByNumber returns the header by number - GetHeaderByNumber(block uint64) (*types.Header, bool) - // GetBlockByHash gets a block using the provided hash GetBlockByHash(hash types.Hash, full bool) (*types.Block, bool) @@ -729,7 +731,7 @@ func (e *Eth) GetCode(address types.Address, filter BlockNumberOrHash) (interfac } emptySlice := []byte{} - acc, err := e.store.GetAccount(header.StateRoot, address) + code, err := e.store.GetCode(header.StateRoot, address) if errors.Is(err, ErrStateNotFound) { // If the account doesn't exist / is not initialized yet, @@ -739,12 +741,6 @@ func (e *Eth) GetCode(address types.Address, filter BlockNumberOrHash) (interfac return argBytesPtr(emptySlice), err } - code, err := e.store.GetCode(types.BytesToHash(acc.CodeHash)) - if err != nil { - // TODO This is just a workaround. Figure out why CodeHash is populated for regular accounts - return argBytesPtr(emptySlice), nil - } - return argBytesPtr(code), nil } @@ -779,24 +775,24 @@ func (e *Eth) getBlockHeader(number BlockNumber) (*types.Header, error) { return e.store.Header(), nil case EarliestBlockNumber: - header, ok := e.store.GetHeaderByNumber(uint64(0)) + block, ok := e.store.GetBlockByNumber(uint64(0), false) if !ok { return nil, fmt.Errorf("error fetching genesis block header") } - return header, nil + return block.Header, nil case PendingBlockNumber: return nil, fmt.Errorf("fetching the pending header is not supported") default: // Convert the block number from hex to uint64 - header, ok := e.store.GetHeaderByNumber(uint64(number)) + block, ok := e.store.GetBlockByNumber(uint64(number), false) if !ok { return nil, fmt.Errorf("error fetching block number %d header", uint64(number)) } - return header, nil + return block.Header, nil } } diff --git a/jsonrpc/eth_endpoint_test.go b/jsonrpc/eth_endpoint_test.go index f9b104559d..580fb2ac91 100644 --- a/jsonrpc/eth_endpoint_test.go +++ b/jsonrpc/eth_endpoint_test.go @@ -5,7 +5,6 @@ import ( "math/big" "testing" - "github.com/0xPolygon/polygon-edge/state" "github.com/0xPolygon/polygon-edge/types" "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/assert" @@ -16,7 +15,7 @@ func TestEth_DecodeTxn(t *testing.T) { tests := []struct { name string - accounts map[types.Address]*state.Account + accounts map[types.Address]*Account arg *txnArgs res *types.Transaction err error @@ -77,7 +76,7 @@ func TestEth_DecodeTxn(t *testing.T) { }, { name: "should set latest nonce as default", - accounts: map[types.Address]*state.Account{ + accounts: map[types.Address]*Account{ addr1: { Nonce: 10, }, @@ -171,11 +170,11 @@ func TestEth_GetNextNonce(t *testing.T) { // Set up the mock accounts accounts := []struct { address types.Address - account *state.Account + account *Account }{ { types.StringToAddress("123"), - &state.Account{ + &Account{ Nonce: 5, }, }, diff --git a/jsonrpc/eth_state_test.go b/jsonrpc/eth_state_test.go index 0714005c8e..e9b1d2b482 100644 --- a/jsonrpc/eth_state_test.go +++ b/jsonrpc/eth_state_test.go @@ -1,9 +1,7 @@ package jsonrpc import ( - "bytes" "errors" - "fmt" "math/big" "testing" @@ -26,7 +24,7 @@ func TestEth_State_GetBalance(t *testing.T) { store := &mockSpecialStore{ account: &mockAccount{ address: addr0, - account: &state.Account{ + account: &Account{ Balance: big.NewInt(100), }, storage: make(map[types.Hash][]byte), @@ -158,7 +156,7 @@ func TestEth_State_GetTransactionCount(t *testing.T) { store := &mockSpecialStore{ account: &mockAccount{ address: addr0, - account: &state.Account{ + account: &Account{ Balance: big.NewInt(100), Nonce: 100, }, @@ -276,10 +274,9 @@ func TestEth_State_GetCode(t *testing.T) { store := &mockSpecialStore{ account: &mockAccount{ address: addr0, - account: &state.Account{ - Balance: big.NewInt(100), - Nonce: 100, - CodeHash: types.BytesToHash(addr0.Bytes()).Bytes(), + account: &Account{ + Balance: big.NewInt(100), + Nonce: 100, }, code: code0, }, @@ -401,7 +398,7 @@ func TestEth_State_GetStorageAt(t *testing.T) { store := &mockSpecialStore{ account: &mockAccount{ address: addr0, - account: &state.Account{ + account: &Account{ Balance: big.NewInt(100), Nonce: 100, }, @@ -550,7 +547,7 @@ func TestEth_State_GetStorageAt(t *testing.T) { for addr, storage := range tt.initialStorage { store.account = &mockAccount{ address: addr, - account: &state.Account{ + account: &Account{ Balance: big.NewInt(100), Nonce: 100, }, @@ -598,7 +595,7 @@ func getExampleStore() *mockSpecialStore { return &mockSpecialStore{ account: &mockAccount{ address: addr0, - account: &state.Account{ + account: &Account{ Balance: big.NewInt(100), Nonce: 0, }, @@ -777,27 +774,27 @@ type mockSpecialStore struct { } func (m *mockSpecialStore) GetBlockByHash(hash types.Hash, full bool) (*types.Block, bool) { - if m.block.Header.Hash.String() != hash.String() { + if m.block.Header.Hash != hash { return nil, false } return m.block, true } -func (m *mockSpecialStore) GetAccount(root types.Hash, addr types.Address) (*state.Account, error) { - if m.account.address.String() != addr.String() { +func (m *mockSpecialStore) GetAccount(root types.Hash, addr types.Address) (*Account, error) { + if m.account.address != addr { return nil, ErrStateNotFound } return m.account.account, nil } -func (m *mockSpecialStore) GetHeaderByNumber(blockNumber uint64) (*types.Header, bool) { +func (m *mockSpecialStore) GetBlockByNumber(blockNumber uint64, full bool) (*types.Block, bool) { if m.block.Number() != blockNumber { return nil, false } - return m.block.Header, true + return m.block, true } func (m *mockSpecialStore) Header() *types.Header { @@ -809,7 +806,7 @@ func (m *mockSpecialStore) GetNonce(addr types.Address) uint64 { } func (m *mockSpecialStore) GetStorage(root types.Hash, addr types.Address, slot types.Hash) ([]byte, error) { - if m.account.address.String() != addr.String() { + if m.account.address != addr { return nil, ErrStateNotFound } @@ -823,12 +820,12 @@ func (m *mockSpecialStore) GetStorage(root types.Hash, addr types.Address, slot return val, nil } -func (m *mockSpecialStore) GetCode(hash types.Hash) ([]byte, error) { - if bytes.Equal(m.account.account.CodeHash, hash.Bytes()) { - return m.account.code, nil +func (m *mockSpecialStore) GetCode(root types.Hash, addr types.Address) ([]byte, error) { + if m.account.address != addr { + return nil, ErrStateNotFound } - return nil, fmt.Errorf("code not found") + return m.account.code, nil } func (m *mockSpecialStore) GetForksInTime(blockNumber uint64) chain.ForksInTime { diff --git a/jsonrpc/eth_txpool_test.go b/jsonrpc/eth_txpool_test.go index 9b07dd1410..6bab83823b 100644 --- a/jsonrpc/eth_txpool_test.go +++ b/jsonrpc/eth_txpool_test.go @@ -4,7 +4,6 @@ import ( "math/big" "testing" - "github.com/0xPolygon/polygon-edge/state" "github.com/0xPolygon/polygon-edge/types" "github.com/stretchr/testify/assert" ) @@ -69,7 +68,7 @@ func (m *mockStoreTxn) AddAccount(addr types.Address) *mockAccount { acct := &mockAccount{ address: addr, - account: &state.Account{}, + account: &Account{}, storage: make(map[types.Hash][]byte), } m.accounts[addr] = acct @@ -81,7 +80,7 @@ func (m *mockStoreTxn) Header() *types.Header { return &types.Header{} } -func (m *mockStoreTxn) GetAccount(root types.Hash, addr types.Address) (*state.Account, error) { +func (m *mockStoreTxn) GetAccount(root types.Hash, addr types.Address) (*Account, error) { acct, ok := m.accounts[addr] if !ok { return nil, ErrStateNotFound diff --git a/jsonrpc/filter_manager.go b/jsonrpc/filter_manager.go index 2635a05c0c..07ea05dffd 100644 --- a/jsonrpc/filter_manager.go +++ b/jsonrpc/filter_manager.go @@ -7,6 +7,7 @@ import ( "fmt" "net" "sync" + "sync/atomic" "time" "github.com/0xPolygon/polygon-edge/blockchain" @@ -114,7 +115,7 @@ type blockFilter struct { } // takeBlockUpdates advances blocks from head to latest and returns header array -func (f *blockFilter) takeBlockUpdates() []*types.Header { +func (f *blockFilter) takeBlockUpdates() []*block { updates, newHead := f.block.getUpdates() f.setHeadElem(newHead) @@ -255,7 +256,6 @@ func NewFilterManager(logger hclog.Logger, store filterManagerStore, blockRangeL logger: logger.Named("filter"), timeout: defaultTimeout, store: store, - blockStream: &blockStream{}, blockRangeLimit: blockRangeLimit, filters: make(map[string]filter), timeouts: timeHeapImpl{}, @@ -265,7 +265,10 @@ func NewFilterManager(logger hclog.Logger, store filterManagerStore, blockRangeL // start blockstream with the current header header := store.Header() - m.blockStream.push(header) + + // TODO: Make Header return jsonrpc.block object directly + block := toBlock(&types.Block{Header: header}, false) + m.blockStream = newBlockStream(block) // start the head watcher m.subscription = store.SubscribeEvents() @@ -292,11 +295,11 @@ func (f *FilterManager) Run() { for { // check for the next filter to be removed - filterBase := f.nextTimeoutFilter() + filterID, filterExpiresAt := f.nextTimeoutFilter() // set timer to remove filter - if filterBase != nil { - timeoutCh = time.After(time.Until(filterBase.expiresAt)) + if filterID != "" { + timeoutCh = time.After(time.Until(filterExpiresAt)) } select { @@ -309,8 +312,8 @@ func (f *FilterManager) Run() { case <-timeoutCh: // timeout for filter // if filter still exists - if !f.Uninstall(filterBase.id) { - f.logger.Warn("failed to uninstall filter", "id", filterBase.id) + if !f.Uninstall(filterID) { + f.logger.Warn("failed to uninstall filter", "id", filterID) } case <-f.updateCh: @@ -332,7 +335,7 @@ func (f *FilterManager) Close() { func (f *FilterManager) NewBlockFilter(ws wsConn) string { filter := &blockFilter{ filterBase: newFilterBase(ws), - block: f.blockStream.Head(), + block: f.blockStream.getHead(), } if filter.hasWSConn() { @@ -615,18 +618,18 @@ func (f *FilterManager) emitSignalToUpdateCh() { // nextTimeoutFilter returns the filter that will be expired next // nextTimeoutFilter returns the only filter with timeout -func (f *FilterManager) nextTimeoutFilter() *filterBase { +func (f *FilterManager) nextTimeoutFilter() (string, time.Time) { f.RLock() defer f.RUnlock() if len(f.timeouts) == 0 { - return nil + return "", time.Time{} } // peek the first item base := f.timeouts[0] - return base + return base.id, base.expiresAt } // dispatchEvent is an event handler for new block event @@ -648,25 +651,34 @@ func (f *FilterManager) processEvent(evnt *blockchain.Event) { defer f.RUnlock() for _, header := range evnt.NewChain { + block := toBlock(&types.Block{Header: header}, false) + // first include all the new headers in the blockstream for BlockFilter - f.blockStream.push(header) + f.blockStream.push(block) // process new chain to include new logs for LogFilter - if processErr := f.appendLogsToFilters(header); processErr != nil { + if processErr := f.appendLogsToFilters(block); processErr != nil { f.logger.Error(fmt.Sprintf("Unable to process block, %v", processErr)) } } } // appendLogsToFilters makes each LogFilters append logs in the header -func (f *FilterManager) appendLogsToFilters(header *types.Header) error { +func (f *FilterManager) appendLogsToFilters(header *block) error { receipts, err := f.store.GetReceiptsByHash(header.Hash) if err != nil { return err } // Get logFilters from filters - logFilters := f.getLogFilters() + logFilters := make([]*logFilter, 0) + + for _, f := range f.filters { + if logFilter, ok := f.(*logFilter); ok { + logFilters = append(logFilters, logFilter) + } + } + if len(logFilters) == 0 { return nil } @@ -691,7 +703,7 @@ func (f *FilterManager) appendLogsToFilters(header *types.Header) error { Address: log.Address, Topics: log.Topics, Data: argBytes(log.Data), - BlockNumber: argUint64(header.Number), + BlockNumber: header.Number, BlockHash: header.Hash, TxHash: receipt.TxHash, TxIndex: argUint64(indx), @@ -747,22 +759,6 @@ func (f *FilterManager) flushWsFilters() error { return nil } -// getLogFilters returns logFilters -func (f *FilterManager) getLogFilters() []*logFilter { - f.RLock() - defer f.RUnlock() - - logFilters := make([]*logFilter, 0) - - for _, f := range f.filters { - if logFilter, ok := f.(*logFilter); ok { - logFilters = append(logFilters, logFilter) - } - } - - return logFilters -} - type timeHeapImpl []*filterBase func (t *timeHeapImpl) addFilter(filter *filterBase) { @@ -813,17 +809,23 @@ func (t *timeHeapImpl) Pop() interface{} { // of the stream at any point type blockStream struct { lock sync.Mutex - head *headElem + head atomic.Value } -func (b *blockStream) Head() *headElem { - b.lock.Lock() - defer b.lock.Unlock() +func newBlockStream(head *block) *blockStream { + b := &blockStream{} + b.head.Store(&headElem{header: head}) + + return b +} + +func (b *blockStream) getHead() *headElem { + head, _ := b.head.Load().(*headElem) - return b.head + return head } -func (b *blockStream) push(header *types.Header) { +func (b *blockStream) push(header *block) { b.lock.Lock() defer b.lock.Unlock() @@ -831,26 +833,34 @@ func (b *blockStream) push(header *types.Header) { header: header.Copy(), } - if b.head != nil { - b.head.next = newHead - } + oldHead, _ := b.head.Load().(*headElem) + oldHead.next.Store(newHead) - b.head = newHead + b.head.Store(newHead) } type headElem struct { - header *types.Header - next *headElem + header *block + next atomic.Value } -func (h *headElem) getUpdates() ([]*types.Header, *headElem) { - res := make([]*types.Header, 0) - +func (h *headElem) getUpdates() ([]*block, *headElem) { + res := make([]*block, 0) cur := h - for cur.next != nil { - cur = cur.next - res = append(res, cur.header) + for { + next := cur.next.Load() + if next == nil { + // no more messages + break + } else { + nextElem, _ := next.(*headElem) + + if nextElem.header != nil { + res = append(res, nextElem.header) + } + cur = nextElem + } } return res, cur diff --git a/jsonrpc/filter_manager_test.go b/jsonrpc/filter_manager_test.go index 19354d8946..578a57e810 100644 --- a/jsonrpc/filter_manager_test.go +++ b/jsonrpc/filter_manager_test.go @@ -3,7 +3,9 @@ package jsonrpc import ( "context" "errors" + "fmt" "math/big" + "math/rand" "net" "strconv" "testing" @@ -508,18 +510,16 @@ func newMockWsConnWithMsgCh() (*mockWsConn, <-chan []byte) { return mock, msgCh } -func TestHeadStream(t *testing.T) { +func TestHeadStream_Basic(t *testing.T) { t.Parallel() - b := &blockStream{} + b := newBlockStream(&block{Hash: types.StringToHash("1")}) + b.push(&block{Hash: types.StringToHash("2")}) - b.push(&types.Header{Hash: types.StringToHash("1")}) - b.push(&types.Header{Hash: types.StringToHash("2")}) + cur := b.getHead() - cur := b.Head() - - b.push(&types.Header{Hash: types.StringToHash("3")}) - b.push(&types.Header{Hash: types.StringToHash("4")}) + b.push(&block{Hash: types.StringToHash("3")}) + b.push(&block{Hash: types.StringToHash("4")}) // get the updates, there are two new entries updates, next := cur.getUpdates() @@ -532,6 +532,70 @@ func TestHeadStream(t *testing.T) { assert.Len(t, updates, 0) } +func TestHeadStream_Concurrent(t *testing.T) { + t.Parallel() + + nReaders := 20 + nMessages := 10 + + b := newBlockStream(&block{Number: 0}) + + // Write co-routine with jitter + go func() { + seed := time.Now().UnixNano() + t.Logf("Using seed %d", seed) + + z := rand.NewZipf(rand.New(rand.NewSource(seed)), 1.5, 1.5, 50) + + for i := 0; i < nMessages; i++ { + b.push(&block{Number: argUint64(i)}) + + wait := time.Duration(z.Uint64()) * time.Millisecond + time.Sleep(wait) + } + }() + + // Run n subscribers following and verifying + errCh := make(chan error, nReaders) + + // All subscribers start from the same point + head := b.getHead() + + for i := 0; i < nReaders; i++ { + go func(i int) { + item := head + expect := uint64(0) + + for { + blocks, next := item.getUpdates() + + for _, block := range blocks { + if num := uint64(block.Number); num != expect { + errCh <- fmt.Errorf("subscriber %05d bad event want=%d, got=%d", i, num, expect) + + return + } + expect++ + + if expect == uint64(nMessages) { + // Succeeded + errCh <- nil + + return + } + } + + item = next + } + }(i) + } + + for i := 0; i < nReaders; i++ { + err := <-errCh + assert.NoError(t, err) + } +} + type MockClosedWSConnection struct{} func (m *MockClosedWSConnection) SetFilterID(_filterID string) {} diff --git a/jsonrpc/mocks_test.go b/jsonrpc/mocks_test.go index 70b66e8d6a..7ab64e4478 100644 --- a/jsonrpc/mocks_test.go +++ b/jsonrpc/mocks_test.go @@ -5,14 +5,13 @@ import ( "sync" "github.com/0xPolygon/polygon-edge/blockchain" - "github.com/0xPolygon/polygon-edge/state" "github.com/0xPolygon/polygon-edge/types" ) type mockAccount struct { address types.Address code []byte - account *state.Account + account *Account storage map[types.Hash][]byte } @@ -21,9 +20,7 @@ func (m *mockAccount) Storage(k types.Hash, v []byte) { } func (m *mockAccount) Code(code []byte) { - codeHash := types.BytesToHash(m.address.Bytes()) m.code = code - m.account.CodeHash = codeHash.Bytes() } func (m *mockAccount) Nonce(n uint64) { @@ -51,7 +48,7 @@ type mockStore struct { subscription *blockchain.MockSubscription receiptsLock sync.Mutex receipts map[types.Hash][]*types.Receipt - accounts map[types.Address]*state.Account + accounts map[types.Address]*Account // headers is the list of historical headers headers []*types.Header @@ -61,7 +58,7 @@ func newMockStore() *mockStore { m := &mockStore{ header: &types.Header{Number: 0}, subscription: blockchain.NewMockSubscription(), - accounts: map[types.Address]*state.Account{}, + accounts: map[types.Address]*Account{}, } m.addHeader(m.header) @@ -87,6 +84,7 @@ func (m *mockStore) headerLoop(cond func(h *types.Header) bool) *types.Header { } func (m *mockStore) emitEvent(evnt *mockEvent) { + m.receiptsLock.Lock() if m.receipts == nil { m.receipts = map[types.Hash][]*types.Receipt{} } @@ -105,11 +103,12 @@ func (m *mockStore) emitEvent(evnt *mockEvent) { m.receipts[i.header.Hash] = i.receipts bEvnt.OldChain = append(bEvnt.OldChain, i.header) } + m.receiptsLock.Unlock() m.subscription.Push(bEvnt) } -func (m *mockStore) GetAccount(root types.Hash, addr types.Address) (*state.Account, error) { +func (m *mockStore) GetAccount(root types.Hash, addr types.Address) (*Account, error) { if acc, ok := m.accounts[addr]; ok { return acc, nil } @@ -117,7 +116,7 @@ func (m *mockStore) GetAccount(root types.Hash, addr types.Address) (*state.Acco return nil, ErrStateNotFound } -func (m *mockStore) SetAccount(addr types.Address, account *state.Account) { +func (m *mockStore) SetAccount(addr types.Address, account *Account) { m.accounts[addr] = account } @@ -138,14 +137,6 @@ func (m *mockStore) SubscribeEvents() blockchain.Subscription { return m.subscription } -func (m *mockStore) GetHeaderByNumber(number uint64) (*types.Header, bool) { - header := m.headerLoop(func(header *types.Header) bool { - return header.Number == number - }) - - return header, header != nil -} - func (m *mockStore) GetBlockByHash(hash types.Hash, full bool) (*types.Block, bool) { header := m.headerLoop(func(header *types.Header) bool { return header.Hash == hash diff --git a/jsonrpc/testsuite/block-empty.json b/jsonrpc/testsuite/block-empty.json new file mode 100644 index 0000000000..3c77c97506 --- /dev/null +++ b/jsonrpc/testsuite/block-empty.json @@ -0,0 +1,22 @@ +{ + "parentHash": "0x0100000000000000000000000000000000000000000000000000000000000000", + "sha3Uncles": "0x0200000000000000000000000000000000000000000000000000000000000000", + "miner": "0x0100000000000000000000000000000000000000", + "stateRoot": "0x0400000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot": "0x0500000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot": "0x0600000000000000000000000000000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0xa", + "totalDifficulty": "0x0", + "size": "0x0", + "number": "0xb", + "gasLimit": "0xc", + "gasUsed": "0xd", + "timestamp": "0xe", + "extraData": "0x616263646566", + "mixHash": "0x0700000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0a00000000000000", + "hash": "0x0800000000000000000000000000000000000000000000000000000000000000", + "transactions": null, + "uncles": null +} \ No newline at end of file diff --git a/jsonrpc/testsuite/block-with-txn-hashes.json b/jsonrpc/testsuite/block-with-txn-hashes.json new file mode 100644 index 0000000000..e3cebad1b7 --- /dev/null +++ b/jsonrpc/testsuite/block-with-txn-hashes.json @@ -0,0 +1,24 @@ +{ + "parentHash": "0x0100000000000000000000000000000000000000000000000000000000000000", + "sha3Uncles": "0x0200000000000000000000000000000000000000000000000000000000000000", + "miner": "0x0100000000000000000000000000000000000000", + "stateRoot": "0x0400000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot": "0x0500000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot": "0x0600000000000000000000000000000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0xa", + "totalDifficulty": "0x0", + "size": "0x0", + "number": "0xb", + "gasLimit": "0xc", + "gasUsed": "0xd", + "timestamp": "0xe", + "extraData": "0x616263646566", + "mixHash": "0x0700000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0a00000000000000", + "hash": "0x0800000000000000000000000000000000000000000000000000000000000000", + "transactions": [ + "0x0800000000000000000000000000000000000000000000000000000000000000" + ], + "uncles": null +} \ No newline at end of file diff --git a/jsonrpc/types.go b/jsonrpc/types.go index 3270be92ca..e09bb89327 100644 --- a/jsonrpc/types.go +++ b/jsonrpc/types.go @@ -104,6 +104,19 @@ type block struct { Uncles []types.Hash `json:"uncles"` } +func (b *block) Copy() *block { + bb := new(block) + *bb = *b + + bb.Miner = make([]byte, len(b.Miner)) + copy(bb.Miner[:], b.Miner[:]) + + bb.ExtraData = make([]byte, len(b.ExtraData)) + copy(bb.ExtraData[:], b.ExtraData[:]) + + return bb +} + func toBlock(b *types.Block, fullTx bool) *block { h := b.Header res := &block{ diff --git a/jsonrpc/types_test.go b/jsonrpc/types_test.go index 2a39050e26..f1016211e9 100644 --- a/jsonrpc/types_test.go +++ b/jsonrpc/types_test.go @@ -2,6 +2,7 @@ package jsonrpc import ( "bytes" + "embed" "encoding/json" "math/big" "reflect" @@ -13,6 +14,7 @@ import ( "github.com/0xPolygon/polygon-edge/types" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestBasicTypes_Encode(t *testing.T) { @@ -125,3 +127,74 @@ func TestToTransaction_Returns_V_R_S_ValuesWithoutLeading0(t *testing.T) { assert.Equal(t, hexWithoutLeading0, string(jsonR)) assert.Equal(t, hexWithoutLeading0, string(jsonS)) } + +func TestBlock_Copy(t *testing.T) { + b := &block{ + ExtraData: []byte{0x1}, + Miner: []byte{0x2}, + Uncles: []types.Hash{{0x0, 0x1}}, + } + + bb := b.Copy() + require.Equal(t, b, bb) +} + +//go:embed testsuite/* +var testsuite embed.FS + +func TestBlock_Encoding(t *testing.T) { + b := block{ + ParentHash: types.Hash{0x1}, + Sha3Uncles: types.Hash{0x2}, + Miner: types.Address{0x1}.Bytes(), + StateRoot: types.Hash{0x4}, + TxRoot: types.Hash{0x5}, + ReceiptsRoot: types.Hash{0x6}, + LogsBloom: types.Bloom{0x0}, + Difficulty: 10, + Number: 11, + GasLimit: 12, + GasUsed: 13, + Timestamp: 14, + ExtraData: []byte{97, 98, 99, 100, 101, 102}, + MixHash: types.Hash{0x7}, + Nonce: types.Nonce{10}, + Hash: types.Hash{0x8}, + } + + testBlock := func(name string) { + res, err := json.Marshal(b) + require.NoError(t, err) + + data, err := testsuite.ReadFile(name) + require.NoError(t, err) + + data = removeWhiteSpace(data) + require.Equal(t, res, data) + } + + t.Run("empty block", func(t *testing.T) { + testBlock("testsuite/block-empty.json") + }) + + t.Run("block with transactions", func(t *testing.T) { + b.Transactions = []transactionOrHash{ + transactionHash{0x8}, + } + testBlock("testsuite/block-with-txn-hashes.json") + }) + + t.Run("block with transactions", func(t *testing.T) { + // TODO: do the same tests for the transaction object and enable this test + t.Skip("TODO") + }) +} + +func removeWhiteSpace(d []byte) []byte { + s := string(d) + s = strings.Replace(s, "\n", "", -1) + s = strings.Replace(s, "\t", "", -1) + s = strings.Replace(s, " ", "", -1) + + return []byte(s) +} diff --git a/server/server.go b/server/server.go index d6702e19a9..617c5775bf 100644 --- a/server/server.go +++ b/server/server.go @@ -18,7 +18,6 @@ import ( "github.com/0xPolygon/polygon-edge/crypto" "github.com/0xPolygon/polygon-edge/helper/common" configHelper "github.com/0xPolygon/polygon-edge/helper/config" - "github.com/0xPolygon/polygon-edge/helper/keccak" "github.com/0xPolygon/polygon-edge/helper/progress" "github.com/0xPolygon/polygon-edge/jsonrpc" "github.com/0xPolygon/polygon-edge/network" @@ -305,19 +304,14 @@ type txpoolHub struct { } func (t *txpoolHub) GetNonce(root types.Hash, addr types.Address) uint64 { + // TODO: Use a function that returns only Account snap, err := t.state.NewSnapshotAt(root) if err != nil { return 0 } - result, ok := snap.Get(keccak.Keccak256(nil, addr.Bytes())) - if !ok { - return 0 - } - - var account state.Account - - if err := account.UnmarshalRlp(result); err != nil { + account, err := snap.GetAccount(addr) + if err != nil { return 0 } @@ -330,14 +324,9 @@ func (t *txpoolHub) GetBalance(root types.Hash, addr types.Address) (*big.Int, e return nil, fmt.Errorf("unable to get snapshot for root, %w", err) } - result, ok := snap.Get(keccak.Keccak256(nil, addr.Bytes())) - if !ok { - return big.NewInt(0), nil - } - - var account state.Account - if err = account.UnmarshalRlp(result); err != nil { - return nil, fmt.Errorf("unable to unmarshal account from snapshot, %w", err) + account, err := snap.GetAccount(addr) + if err != nil { + return big.NewInt(0), err } return account.Balance, nil @@ -449,36 +438,36 @@ func (j *jsonRPCHub) GetPeers() int { return len(j.Server.Peers()) } -func (j *jsonRPCHub) getState(root types.Hash, slot []byte) ([]byte, error) { - // the values in the trie are the hashed objects of the keys - key := keccak.Keccak256(nil, slot) - +func (j *jsonRPCHub) getAccountImpl(root types.Hash, addr types.Address) (*state.Account, error) { snap, err := j.state.NewSnapshotAt(root) if err != nil { return nil, err } - result, ok := snap.Get(key) + account, err := snap.GetAccount(addr) + if err != nil { + return nil, err + } - if !ok { + if account == nil { return nil, jsonrpc.ErrStateNotFound } - return result, nil + return account, nil } -func (j *jsonRPCHub) GetAccount(root types.Hash, addr types.Address) (*state.Account, error) { - obj, err := j.getState(root, addr.Bytes()) +func (j *jsonRPCHub) GetAccount(root types.Hash, addr types.Address) (*jsonrpc.Account, error) { + acct, err := j.getAccountImpl(root, addr) if err != nil { return nil, err } - var account state.Account - if err := account.UnmarshalRlp(obj); err != nil { - return nil, err + account := &jsonrpc.Account{ + Nonce: acct.Nonce, + Balance: new(big.Int).Set(acct.Balance), } - return &account, nil + return account, nil } // GetForksInTime returns the active forks at the given block height @@ -486,30 +475,34 @@ func (j *jsonRPCHub) GetForksInTime(blockNumber uint64) chain.ForksInTime { return j.Executor.GetForksInTime(blockNumber) } -func (j *jsonRPCHub) GetStorage(root types.Hash, addr types.Address, slot types.Hash) ([]byte, error) { - account, err := j.GetAccount(root, addr) - +func (j *jsonRPCHub) GetStorage(stateRoot types.Hash, addr types.Address, slot types.Hash) ([]byte, error) { + account, err := j.getAccountImpl(stateRoot, addr) if err != nil { return nil, err } - obj, err := j.getState(account.Root, slot.Bytes()) - + snap, err := j.state.NewSnapshotAt(stateRoot) if err != nil { return nil, err } - return obj, nil + res := snap.GetStorage(addr, account.Root, slot) + + return res.Bytes(), nil } -func (j *jsonRPCHub) GetCode(hash types.Hash) ([]byte, error) { - res, ok := j.state.GetCode(hash) +func (j *jsonRPCHub) GetCode(root types.Hash, addr types.Address) ([]byte, error) { + account, err := j.getAccountImpl(root, addr) + if err != nil { + return nil, err + } + code, ok := j.state.GetCode(account.Root) if !ok { return nil, fmt.Errorf("unable to fetch code") } - return res, nil + return code, nil } func (j *jsonRPCHub) ApplyTxn( diff --git a/server/system_service.go b/server/system_service.go index 913201b702..a5b0550a70 100644 --- a/server/system_service.go +++ b/server/system_service.go @@ -224,6 +224,7 @@ func (s *systemService) Export(req *proto.ExportRequest, stream proto.System_Exp const ( defaultMaxGRPCPayloadSize uint64 = 512 * 1024 // 4MB + maxHeaderInfoSize int = 3 * 8 //Number of header fields * bytes per field (From, To, Latest all them uint64) ) type blockStreamWriter struct { @@ -250,8 +251,7 @@ func newBlockStreamWriter( func (w *blockStreamWriter) appendBlock(b *types.Block) error { data := b.MarshalRLP() - - if uint64(w.buf.Len()+len(data)) >= w.maxPayload { + if uint64(maxHeaderInfoSize+w.buf.Len()+len(data)) >= w.maxPayload { // send buffered data to client first if err := w.flush(); err != nil { return err diff --git a/state/executor.go b/state/executor.go index 085c4fc612..7f702478be 100644 --- a/state/executor.go +++ b/state/executor.go @@ -53,7 +53,7 @@ func NewExecutor(config *chain.Params, s State, logger hclog.Logger) *Executor { func (e *Executor) WriteGenesis(alloc map[types.Address]*chain.GenesisAccount) types.Hash { snap := e.state.NewSnapshot() - txn := NewTxn(e.state, snap) + txn := NewTxn(snap) config := e.config.Forks.At(0) env := runtime.TxContext{ @@ -161,7 +161,7 @@ func (e *Executor) BeginTxn( return nil, err } - newTxn := NewTxn(e.state, auxSnap2) + newTxn := NewTxn(auxSnap2) env2 := runtime.TxContext{ Coinbase: coinbaseReceiver, @@ -176,6 +176,7 @@ func (e *Executor) BeginTxn( logger: e.logger, ctx: env2, state: newTxn, + snap: auxSnap2, getHash: e.GetHash(header), auxState: e.state, config: config, @@ -197,6 +198,7 @@ type Transition struct { // dummy auxState State + snap Snapshot config chain.ForksInTime state *Txn @@ -215,10 +217,11 @@ type Transition struct { precompiles *precompiled.Precompiled } -func NewTransition(config chain.ForksInTime, radix *Txn) *Transition { +func NewTransition(config chain.ForksInTime, snap Snapshot, radix *Txn) *Transition { return &Transition{ config: config, state: radix, + snap: snap, evm: evm.NewEVM(), precompiles: precompiled.NewPrecompiled(), } @@ -293,8 +296,6 @@ func (t *Transition) Write(txn *types.Transaction) error { logs := t.state.Logs() - var root []byte - receipt := &types.Receipt{ CumulativeGasUsed: t.totalGas, TransactionType: txn.Type, @@ -302,21 +303,13 @@ func (t *Transition) Write(txn *types.Transaction) error { GasUsed: result.GasUsed, } - if t.config.Byzantium { - // The suicided accounts are set as deleted for the next iteration - t.state.CleanDeleteObjects(true) + // The suicided accounts are set as deleted for the next iteration + t.state.CleanDeleteObjects(true) - if result.Failed() { - receipt.SetStatus(types.ReceiptFailed) - } else { - receipt.SetStatus(types.ReceiptSuccess) - } + if result.Failed() { + receipt.SetStatus(types.ReceiptFailed) } else { - objs := t.state.Commit(t.config.EIP155) - ss, aux := t.state.snapshot.Commit(objs) - t.state = NewTxn(t.auxState, ss) - root = aux - receipt.Root = types.BytesToHash(root) + receipt.SetStatus(types.ReceiptSuccess) } // if the transaction created a contract, store the creation address in the receipt. @@ -335,7 +328,7 @@ func (t *Transition) Write(txn *types.Transaction) error { // Commit commits the final result func (t *Transition) Commit() (Snapshot, types.Hash) { objs := t.state.Commit(t.config.EIP155) - s2, root := t.state.snapshot.Commit(objs) + s2, root := t.snap.Commit(objs) return s2, types.BytesToHash(root) } @@ -354,10 +347,6 @@ func (t *Transition) addGasPool(amount uint64) { t.gasPool += amount } -func (t *Transition) SetTxn(txn *Txn) { - t.state = txn -} - func (t *Transition) Txn() *Txn { return t.state } diff --git a/state/immutable-trie/snapshot.go b/state/immutable-trie/snapshot.go new file mode 100644 index 0000000000..13fd20f8ef --- /dev/null +++ b/state/immutable-trie/snapshot.go @@ -0,0 +1,78 @@ +package itrie + +import ( + "github.com/0xPolygon/polygon-edge/crypto" + "github.com/0xPolygon/polygon-edge/state" + "github.com/0xPolygon/polygon-edge/types" + "github.com/umbracle/fastrlp" +) + +type Snapshot struct { + state *State + trie *Trie +} + +var emptyStateHash = types.StringToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + +func (s *Snapshot) GetStorage(addr types.Address, root types.Hash, rawkey types.Hash) types.Hash { + var ( + err error + trie *Trie + ) + + if root == emptyStateHash { + trie = s.state.newTrie() + } else { + trie, err = s.state.newTrieAt(root) + if err != nil { + return types.Hash{} + } + } + + key := crypto.Keccak256(rawkey.Bytes()) + + val, ok := trie.Get(key) + if !ok { + return types.Hash{} + } + + p := &fastrlp.Parser{} + + v, err := p.Parse(val) + if err != nil { + return types.Hash{} + } + + res := []byte{} + if res, err = v.GetBytes(res[:0]); err != nil { + return types.Hash{} + } + + return types.BytesToHash(res) +} + +func (s *Snapshot) GetAccount(addr types.Address) (*state.Account, error) { + key := crypto.Keccak256(addr.Bytes()) + + data, ok := s.trie.Get(key) + if !ok { + return nil, nil + } + + var account state.Account + if err := account.UnmarshalRlp(data); err != nil { + return nil, err + } + + return &account, nil +} + +func (s *Snapshot) GetCode(hash types.Hash) ([]byte, bool) { + return s.state.GetCode(hash) +} + +func (s *Snapshot) Commit(objs []*state.Object) (state.Snapshot, []byte) { + trie, root := s.trie.Commit(objs) + + return &Snapshot{trie: trie, state: s.state}, root +} diff --git a/state/immutable-trie/state.go b/state/immutable-trie/state.go index 65836edec7..d0538e50f2 100644 --- a/state/immutable-trie/state.go +++ b/state/immutable-trie/state.go @@ -1,7 +1,6 @@ package itrie import ( - "errors" "fmt" lru "github.com/hashicorp/golang-lru" @@ -27,6 +26,19 @@ func NewState(storage Storage) *State { } func (s *State) NewSnapshot() state.Snapshot { + return &Snapshot{state: s, trie: s.newTrie()} +} + +func (s *State) NewSnapshotAt(root types.Hash) (state.Snapshot, error) { + t, err := s.newTrieAt(root) + if err != nil { + return nil, err + } + + return &Snapshot{state: s, trie: t}, nil +} + +func (s *State) newTrie() *Trie { t := NewTrie() t.state = s t.storage = s.storage @@ -42,33 +54,32 @@ func (s *State) GetCode(hash types.Hash) ([]byte, bool) { return s.storage.GetCode(hash) } -func (s *State) NewSnapshotAt(root types.Hash) (state.Snapshot, error) { +func (s *State) newTrieAt(root types.Hash) (*Trie, error) { if root == types.EmptyRootHash { // empty state - return s.NewSnapshot(), nil + return s.newTrie(), nil } tt, ok := s.cache.Get(root) if ok { t, ok := tt.(*Trie) if !ok { - return nil, errors.New("invalid type assertion") + return nil, fmt.Errorf("invalid type assertion on root: %s", root) } t.state = s trie, ok := tt.(*Trie) if !ok { - return nil, errors.New("invalid type assertion") + return nil, fmt.Errorf("invalid type assertion on root: %s", root) } return trie, nil } n, ok, err := GetNode(root.Bytes(), s.storage) - if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get storage root %s: %w", root, err) } if !ok { diff --git a/state/immutable-trie/state_test.go b/state/immutable-trie/state_test.go index 85021442dc..e4e2ce4a98 100644 --- a/state/immutable-trie/state_test.go +++ b/state/immutable-trie/state_test.go @@ -10,10 +10,10 @@ func TestState(t *testing.T) { state.TestState(t, buildPreState) } -func buildPreState(pre state.PreStates) (state.State, state.Snapshot) { +func buildPreState(pre state.PreStates) state.Snapshot { storage := NewMemoryStorage() st := NewState(storage) snap := st.NewSnapshot() - return st, snap + return snap } diff --git a/state/immutable-trie/trie.go b/state/immutable-trie/trie.go index f6076840b5..d802119cd0 100644 --- a/state/immutable-trie/trie.go +++ b/state/immutable-trie/trie.go @@ -118,7 +118,7 @@ var accountArenaPool fastrlp.ArenaPool var stateArenaPool fastrlp.ArenaPool // TODO, Remove once we do update in fastrlp -func (t *Trie) Commit(objs []*state.Object) (state.Snapshot, []byte) { +func (t *Trie) Commit(objs []*state.Object) (*Trie, []byte) { // Create an insertion batch for all the entries batch := t.storage.Batch() @@ -143,16 +143,11 @@ func (t *Trie) Commit(objs []*state.Object) (state.Snapshot, []byte) { } if len(obj.Storage) != 0 { - localSnapshot, err := t.state.NewSnapshotAt(obj.Root) + trie, err := t.state.newTrieAt(obj.Root) if err != nil { panic(err) } - trie, ok := localSnapshot.(*Trie) - if !ok { - panic("invalid type assertion") - } - localTxn := trie.Txn() localTxn.batch = batch diff --git a/state/state.go b/state/state.go index ded9038209..36c0ac01b0 100644 --- a/state/state.go +++ b/state/state.go @@ -19,13 +19,9 @@ type State interface { } type Snapshot interface { - Get(k []byte) ([]byte, bool) - Commit(objs []*Object) (Snapshot, []byte) -} + readSnapshot -// account trie -type accountTrie interface { - Get(k []byte) ([]byte, bool) + Commit(objs []*Object) (Snapshot, []byte) } // Account is the account reference in the ethereum state @@ -34,7 +30,6 @@ type Account struct { Balance *big.Int Root types.Hash CodeHash []byte - Trie accountTrie } func (a *Account) MarshalWith(ar *fastrlp.Arena) *fastrlp.Value { @@ -103,7 +98,6 @@ func (a *Account) Copy() *Account { aa.Nonce = a.Nonce aa.CodeHash = a.CodeHash aa.Root = a.Root - aa.Trie = a.Trie return aa } @@ -124,30 +118,6 @@ func (s *StateObject) Empty() bool { return s.Account.Nonce == 0 && s.Account.Balance.Sign() == 0 && bytes.Equal(s.Account.CodeHash, emptyCodeHash) } -var stateStateParserPool fastrlp.ParserPool - -func (s *StateObject) GetCommitedState(key types.Hash) types.Hash { - val, ok := s.Account.Trie.Get(key.Bytes()) - if !ok { - return types.Hash{} - } - - p := stateStateParserPool.Get() - defer stateStateParserPool.Put(p) - - v, err := p.Parse(val) - if err != nil { - return types.Hash{} - } - - res := []byte{} - if res, err = v.GetBytes(res[:0]); err != nil { - return types.Hash{} - } - - return types.BytesToHash(res) -} - // Copy makes a copy of the state object func (s *StateObject) Copy() *StateObject { ss := new(StateObject) diff --git a/state/testing.go b/state/testing.go index f7f2371237..6880725b12 100644 --- a/state/testing.go +++ b/state/testing.go @@ -34,7 +34,7 @@ type PreState struct { // PreStates is a set of pre states type PreStates map[types.Address]*PreState -type buildPreState func(p PreStates) (State, Snapshot) +type buildPreState func(p PreStates) Snapshot // TestState tests a set of tests on a state func TestState(t *testing.T, buildPreState buildPreState) { @@ -106,8 +106,8 @@ func TestState(t *testing.T, buildPreState buildPreState) { func testDeleteCommonStateRoot(t *testing.T, buildPreState buildPreState) { t.Helper() - state, snap := buildPreState(nil) - txn := newTxn(state, snap) + snap := buildPreState(nil) + txn := newTxn(snap) txn.SetNonce(addr1, 1) txn.SetState(addr1, hash0, hash1) @@ -120,14 +120,14 @@ func testDeleteCommonStateRoot(t *testing.T, buildPreState buildPreState) { txn.SetState(addr2, hash2, hash1) snap2, _ := snap.Commit(txn.Commit(false)) - txn2 := newTxn(state, snap2) + txn2 := newTxn(snap2) txn2.SetState(addr1, hash0, hash0) txn2.SetState(addr1, hash1, hash0) snap3, _ := snap2.Commit(txn2.Commit(false)) - txn3 := newTxn(state, snap3) + txn3 := newTxn(snap3) assert.Equal(t, hash1, txn3.GetState(addr1, hash2)) assert.Equal(t, hash1, txn3.GetState(addr2, hash0)) assert.Equal(t, hash1, txn3.GetState(addr2, hash1)) @@ -137,8 +137,8 @@ func testDeleteCommonStateRoot(t *testing.T, buildPreState buildPreState) { func testWriteState(t *testing.T, buildPreState buildPreState) { t.Helper() - state, snap := buildPreState(nil) - txn := newTxn(state, snap) + snap := buildPreState(nil) + txn := newTxn(snap) txn.SetState(addr1, hash1, hash1) txn.SetState(addr1, hash2, hash2) @@ -148,7 +148,7 @@ func testWriteState(t *testing.T, buildPreState buildPreState) { snap, _ = snap.Commit(txn.Commit(false)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.Equal(t, hash1, txn.GetState(addr1, hash1)) assert.Equal(t, hash2, txn.GetState(addr1, hash2)) } @@ -156,24 +156,24 @@ func testWriteState(t *testing.T, buildPreState buildPreState) { func testWriteEmptyState(t *testing.T, buildPreState buildPreState) { t.Helper() // Create account and write empty state - state, snap := buildPreState(nil) - txn := newTxn(state, snap) + snap := buildPreState(nil) + txn := newTxn(snap) // Without EIP150 the data is added txn.SetState(addr1, hash1, hash0) snap, _ = snap.Commit(txn.Commit(false)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.True(t, txn.Exist(addr1)) - _, snap = buildPreState(nil) - txn = newTxn(state, snap) + snap = buildPreState(nil) + txn = newTxn(snap) // With EIP150 the empty data is removed txn.SetState(addr1, hash1, hash0) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } @@ -181,16 +181,16 @@ func testUpdateStateWithEmpty(t *testing.T, buildPreState buildPreState) { t.Helper() // If the state (in prestate) is updated to empty it should be removed - state, snap := buildPreState(defaultPreState) + snap := buildPreState(defaultPreState) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.SetState(addr1, hash1, hash0) // TODO, test with false (should not be deleted) // TODO, test with balance on the account and nonce snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } @@ -198,22 +198,22 @@ func testSuicideAccountInPreState(t *testing.T, buildPreState buildPreState) { t.Helper() // Suicide an account created in the prestate - state, snap := buildPreState(defaultPreState) + snap := buildPreState(defaultPreState) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.Suicide(addr1) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } func testSuicideAccount(t *testing.T, buildPreState buildPreState) { t.Helper() // Create a new account and suicide it - state, snap := buildPreState(nil) + snap := buildPreState(nil) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.SetState(addr1, hash1, hash1) txn.Suicide(addr1) @@ -222,16 +222,16 @@ func testSuicideAccount(t *testing.T, buildPreState buildPreState) { snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } func testSuicideAccountWithData(t *testing.T, buildPreState buildPreState) { t.Helper() // Data (nonce, balance, code) from a suicided account should be empty - state, snap := buildPreState(nil) + snap := buildPreState(nil) - txn := newTxn(state, snap) + txn := newTxn(snap) code := []byte{0x1, 0x2, 0x3} @@ -243,7 +243,7 @@ func testSuicideAccountWithData(t *testing.T, buildPreState buildPreState) { txn.Suicide(addr1) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.Equal(t, big.NewInt(0), txn.GetBalance(addr1)) assert.Equal(t, uint64(0), txn.GetNonce(addr1)) @@ -259,23 +259,23 @@ func testSuicideAccountWithData(t *testing.T, buildPreState buildPreState) { func testSuicideCoinbase(t *testing.T, buildPreState buildPreState) { t.Helper() // Suicide the coinbase of the block - state, snap := buildPreState(defaultPreState) + snap := buildPreState(defaultPreState) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.Suicide(addr1) txn.AddSealingReward(addr1, big.NewInt(10)) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.Equal(t, big.NewInt(10), txn.GetBalance(addr1)) } func testSuicideWithIntermediateCommit(t *testing.T, buildPreState buildPreState) { t.Helper() - state, snap := buildPreState(defaultPreState) + snap := buildPreState(defaultPreState) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.SetNonce(addr1, 10) txn.Suicide(addr1) @@ -292,9 +292,9 @@ func testRestartRefunds(t *testing.T, buildPreState buildPreState) { t.Helper() // refunds are only valid per single txn so after each // intermediateCommit they have to be restarted - state, snap := buildPreState(nil) + snap := buildPreState(nil) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.AddRefund(1000) assert.Equal(t, uint64(1000), txn.GetRefund()) @@ -314,27 +314,27 @@ func testChangePrestateAccountBalanceToZero(t *testing.T, buildPreState buildPre }, } - state, snap := buildPreState(preState) + snap := buildPreState(preState) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.SetBalance(addr1, big.NewInt(0)) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } func testChangeAccountBalanceToZero(t *testing.T, buildPreState buildPreState) { t.Helper() // If the balance of the account changes to zero the account is deleted - state, snap := buildPreState(nil) + snap := buildPreState(nil) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.SetBalance(addr1, big.NewInt(10)) txn.SetBalance(addr1, big.NewInt(0)) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } diff --git a/state/txn.go b/state/txn.go index 755a9d5e23..4084809822 100644 --- a/state/txn.go +++ b/state/txn.go @@ -8,13 +8,18 @@ import ( "github.com/0xPolygon/polygon-edge/chain" "github.com/0xPolygon/polygon-edge/crypto" - "github.com/0xPolygon/polygon-edge/helper/keccak" "github.com/0xPolygon/polygon-edge/state/runtime" "github.com/0xPolygon/polygon-edge/types" ) var emptyStateHash = types.StringToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") +type readSnapshot interface { + GetStorage(addr types.Address, root types.Hash, key types.Hash) types.Hash + GetAccount(addr types.Address) (*Account, error) + GetCode(hash types.Hash) ([]byte, bool) +} + var ( // logIndex is the index of the logs in the trie logIndex = types.BytesToHash([]byte{2}).Bytes() @@ -25,45 +30,33 @@ var ( // Txn is a reference of the state type Txn struct { - snapshot Snapshot - state State + snapshot readSnapshot snapshots []*iradix.Tree txn *iradix.Txn codeCache *lru.Cache - hash *keccak.Keccak } -func NewTxn(state State, snapshot Snapshot) *Txn { - return newTxn(state, snapshot) +func NewTxn(snapshot Snapshot) *Txn { + return newTxn(snapshot) } func (txn *Txn) GetRadix() *iradix.Txn { return txn.txn } -func newTxn(state State, snapshot Snapshot) *Txn { +func newTxn(snapshot readSnapshot) *Txn { i := iradix.New() codeCache, _ := lru.New(20) return &Txn{ snapshot: snapshot, - state: state, snapshots: []*iradix.Tree{}, txn: i.Txn(), codeCache: codeCache, - hash: keccak.NewKeccak256(), } } -func (txn *Txn) hashit(src []byte) []byte { - txn.hash.Reset() - txn.hash.Write(src) - // hashit is used to make queries so we do not need to - // make copies of the result - return txn.hash.Read() -} - // Snapshot takes a snapshot at this point in time func (txn *Txn) Snapshot() int { t := txn.txn.CommitOnly() @@ -106,28 +99,15 @@ func (txn *Txn) getStateObject(addr types.Address) (*StateObject, bool) { return obj.Copy(), true } - data, ok := txn.snapshot.Get(txn.hashit(addr.Bytes())) - if !ok { + account, err := txn.snapshot.GetAccount(addr) + if err != nil { return nil, false } - var err error - - var account Account - if err = account.UnmarshalRlp(data); err != nil { + if account == nil { return nil, false } - // Load trie from memory if there is some state - if account.Root == emptyStateHash { - account.Trie = txn.state.NewSnapshot() - } else { - account.Trie, err = txn.state.NewSnapshotAt(account.Root) - if err != nil { - return nil, false - } - } - obj := &StateObject{ Account: account.Copy(), } @@ -141,7 +121,6 @@ func (txn *Txn) upsertAccount(addr types.Address, create bool, f func(object *St object = &StateObject{ Account: &Account{ Balance: big.NewInt(0), - Trie: txn.state.NewSnapshot(), CodeHash: emptyCodeHash, Root: emptyStateHash, }, @@ -360,10 +339,7 @@ func (txn *Txn) GetState(addr types.Address, key types.Hash) types.Hash { } } - // If the object was not found in the radix trie due to no state update, we fetch it from the trie tre - k := txn.hashit(key.Bytes()) - - return object.GetCommitedState(types.BytesToHash(k)) + return txn.snapshot.GetStorage(addr, object.Account.Root, key) } // Nonce @@ -420,7 +396,7 @@ func (txn *Txn) GetCode(addr types.Address) []byte { return v.([]byte) } - code, _ := txn.state.GetCode(types.BytesToHash(object.Account.CodeHash)) + code, _ := txn.snapshot.GetCode(types.BytesToHash(object.Account.CodeHash)) txn.codeCache.Add(addr, code) return code @@ -504,7 +480,7 @@ func (txn *Txn) GetCommittedState(addr types.Address, key types.Hash) types.Hash return types.Hash{} } - return obj.GetCommitedState(types.BytesToHash(txn.hashit(key.Bytes()))) + return txn.snapshot.GetStorage(addr, obj.Account.Root, key) } func (txn *Txn) TouchAccount(addr types.Address) { @@ -534,7 +510,6 @@ func newStateObject(txn *Txn) *StateObject { return &StateObject{ Account: &Account{ Balance: big.NewInt(0), - Trie: txn.state.NewSnapshot(), CodeHash: emptyCodeHash, Root: emptyStateHash, }, @@ -545,7 +520,6 @@ func (txn *Txn) CreateAccount(addr types.Address) { obj := &StateObject{ Account: &Account{ Balance: big.NewInt(0), - Trie: txn.state.NewSnapshot(), CodeHash: emptyCodeHash, Root: emptyStateHash, }, diff --git a/state/txn_test.go b/state/txn_test.go index 0a3a56a1f2..d1ff74b519 100644 --- a/state/txn_test.go +++ b/state/txn_test.go @@ -1,128 +1,56 @@ package state import ( - "bytes" - "crypto/rand" "fmt" "math/big" "testing" - "github.com/0xPolygon/polygon-edge/helper/hex" "github.com/0xPolygon/polygon-edge/types" "github.com/stretchr/testify/assert" - "github.com/umbracle/fastrlp" - "golang.org/x/crypto/sha3" ) -type mockState struct { - snapshots map[types.Hash]Snapshot +type mockSnapshot struct { + state map[types.Address]*PreState } -func (m *mockState) NewSnapshotAt(root types.Hash) (Snapshot, error) { - t, ok := m.snapshots[root] +func (m *mockSnapshot) GetStorage(addr types.Address, root types.Hash, key types.Hash) types.Hash { + raw, ok := m.state[addr] if !ok { - return nil, fmt.Errorf("not found") + return types.Hash{} } - return t, nil -} - -func (m *mockState) NewSnapshot() Snapshot { - return &mockSnapshot{data: map[string][]byte{}} -} - -func (m *mockState) GetCode(hash types.Hash) ([]byte, bool) { - panic("Not implemented in tests") -} - -type mockSnapshot struct { - data map[string][]byte -} - -func (m *mockSnapshot) Get(k []byte) ([]byte, bool) { - v, ok := m.data[hex.EncodeToHex(k)] - - return v, ok -} + res, ok := raw.State[key] + if !ok { + return types.Hash{} + } -func (m *mockSnapshot) Commit(objs []*Object) (Snapshot, []byte) { - panic("Not implemented in tests") + return res } -func newStateWithPreState(preState map[types.Address]*PreState) (*mockState, *mockSnapshot) { - state := &mockState{ - snapshots: map[types.Hash]Snapshot{}, - } - snapshot := &mockSnapshot{ - data: map[string][]byte{}, +func (m *mockSnapshot) GetAccount(addr types.Address) (*Account, error) { + raw, ok := m.state[addr] + if !ok { + return nil, fmt.Errorf("account not found") } - ar := &fastrlp.Arena{} - - for addr, p := range preState { - account, snap := buildMockPreState(p) - if snap != nil { - state.snapshots[account.Root] = snap - } - - v := account.MarshalWith(ar) - accountRlp := v.MarshalTo(nil) - /* - accountRlp, err := rlp.EncodeToBytes(account) - if err != nil { - panic(err) - } - */ - snapshot.data[hex.EncodeToHex(hashit(addr.Bytes()))] = accountRlp + acct := &Account{ + Balance: new(big.Int).SetUint64(raw.Balance), + Nonce: raw.Nonce, } - return state, snapshot + return acct, nil } -func newTestTxn(p map[types.Address]*PreState) *Txn { - return newTxn(newStateWithPreState(p)) +func (m *mockSnapshot) GetCode(hash types.Hash) ([]byte, bool) { + return nil, false } -func buildMockPreState(p *PreState) (*Account, *mockSnapshot) { - var snap *mockSnapshot - - root := emptyStateHash - - ar := &fastrlp.Arena{} - - if p.State != nil { - data := map[string][]byte{} - - for k, v := range p.State { - vv := ar.NewBytes(bytes.TrimLeft(v.Bytes(), "\x00")) - data[k.String()] = vv.MarshalTo(nil) - } - - root = randomHash() - snap = &mockSnapshot{ - data: data, - } - } - - account := &Account{ - Nonce: p.Nonce, - Balance: big.NewInt(int64(p.Balance)), - Root: root, - } - - return account, snap +func newStateWithPreState(preState map[types.Address]*PreState) readSnapshot { + return &mockSnapshot{state: preState} } -const letterBytes = "0123456789ABCDEF" - -func randomHash() types.Hash { - b := make([]byte, types.HashLength) - for i := range b { - randNum, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letterBytes)))) - b[i] = letterBytes[randNum.Int64()] - } - - return types.BytesToHash(b) +func newTestTxn(p map[types.Address]*PreState) *Txn { + return newTxn(newStateWithPreState(p)) } func TestSnapshotUpdateData(t *testing.T) { @@ -138,10 +66,3 @@ func TestSnapshotUpdateData(t *testing.T) { txn.RevertToSnapshot(ss) assert.Equal(t, hash1, txn.GetState(addr1, hash1)) } - -func hashit(k []byte) []byte { - h := sha3.NewLegacyKeccak256() - h.Write(k) - - return h.Sum(nil) -} diff --git a/tests/testing.go b/tests/testing.go index 5efb83ec37..f24d2c6755 100644 --- a/tests/testing.go +++ b/tests/testing.go @@ -228,7 +228,7 @@ func buildState( s := itrie.NewState(itrie.NewMemoryStorage()) snap := s.NewSnapshot() - txn := state.NewTxn(s, snap) + txn := state.NewTxn(snap) for addr, alloc := range allocs { txn.CreateAccount(addr) diff --git a/types/header.go b/types/header.go index 665e0bbc39..ffc1eeff44 100644 --- a/types/header.go +++ b/types/header.go @@ -1,11 +1,7 @@ package types import ( - "database/sql/driver" "encoding/binary" - goHex "encoding/hex" - "encoding/json" - "errors" "fmt" "sync/atomic" @@ -14,116 +10,22 @@ import ( // Header represents a block header in the Ethereum blockchain. type Header struct { - ParentHash Hash `json:"parentHash"` - Sha3Uncles Hash `json:"sha3Uncles"` - Miner []byte `json:"miner"` - StateRoot Hash `json:"stateRoot"` - TxRoot Hash `json:"transactionsRoot"` - ReceiptsRoot Hash `json:"receiptsRoot"` - LogsBloom Bloom `json:"logsBloom"` - Difficulty uint64 `json:"difficulty"` - Number uint64 `json:"number"` - GasLimit uint64 `json:"gasLimit"` - GasUsed uint64 `json:"gasUsed"` - Timestamp uint64 `json:"timestamp"` - ExtraData []byte `json:"extraData"` - MixHash Hash `json:"mixHash"` - Nonce Nonce `json:"nonce"` - Hash Hash `json:"hash"` -} - -// headerJSON represents a block header used for json calls -type headerJSON struct { - ParentHash Hash `json:"parentHash"` - Sha3Uncles Hash `json:"sha3Uncles"` - Miner string `json:"miner"` - StateRoot Hash `json:"stateRoot"` - TxRoot Hash `json:"transactionsRoot"` - ReceiptsRoot Hash `json:"receiptsRoot"` - LogsBloom Bloom `json:"logsBloom"` - Difficulty string `json:"difficulty"` - Number string `json:"number"` - GasLimit string `json:"gasLimit"` - GasUsed string `json:"gasUsed"` - Timestamp string `json:"timestamp"` - ExtraData string `json:"extraData"` - MixHash Hash `json:"mixHash"` - Nonce Nonce `json:"nonce"` - Hash Hash `json:"hash"` -} - -func (h *Header) MarshalJSON() ([]byte, error) { - var header headerJSON - - header.ParentHash = h.ParentHash - header.Sha3Uncles = h.Sha3Uncles - header.Miner = hex.EncodeToHex(h.Miner) - header.StateRoot = h.StateRoot - header.TxRoot = h.TxRoot - header.ReceiptsRoot = h.ReceiptsRoot - header.LogsBloom = h.LogsBloom - - header.MixHash = h.MixHash - header.Nonce = h.Nonce - header.Hash = h.Hash - - header.Difficulty = hex.EncodeUint64(h.Difficulty) - header.Number = hex.EncodeUint64(h.Number) - header.GasLimit = hex.EncodeUint64(h.GasLimit) - header.GasUsed = hex.EncodeUint64(h.GasUsed) - header.Timestamp = hex.EncodeUint64(h.Timestamp) - header.ExtraData = hex.EncodeToHex(h.ExtraData) - - return json.Marshal(&header) -} - -func (h *Header) UnmarshalJSON(input []byte) error { - var header headerJSON - if err := json.Unmarshal(input, &header); err != nil { - return err - } - - h.ParentHash = header.ParentHash - h.Sha3Uncles = header.Sha3Uncles - h.StateRoot = header.StateRoot - h.TxRoot = header.TxRoot - h.ReceiptsRoot = header.ReceiptsRoot - h.LogsBloom = header.LogsBloom - h.MixHash = header.MixHash - h.Nonce = header.Nonce - h.Hash = header.Hash - - var err error - - if h.Miner, err = hex.DecodeHex(header.Miner); err != nil { - return err - } - - if h.Difficulty, err = hex.DecodeUint64(header.Difficulty); err != nil { - return err - } - - if h.Number, err = hex.DecodeUint64(header.Number); err != nil { - return err - } - - if h.GasLimit, err = hex.DecodeUint64(header.GasLimit); err != nil { - return err - } - - if h.GasUsed, err = hex.DecodeUint64(header.GasUsed); err != nil { - return err - } - - if h.Timestamp, err = hex.DecodeUint64(header.Timestamp); err != nil { - return err - } - - if h.ExtraData, err = hex.DecodeHex(header.ExtraData); err != nil { - return err - } - - return nil + ParentHash Hash + Sha3Uncles Hash + Miner []byte + StateRoot Hash + TxRoot Hash + ReceiptsRoot Hash + LogsBloom Bloom + Difficulty uint64 + Number uint64 + GasLimit uint64 + GasUsed uint64 + Timestamp uint64 + ExtraData []byte + MixHash Hash + Nonce Nonce + Hash Hash } func (h *Header) Equal(hh *Header) bool { @@ -152,42 +54,11 @@ func (n Nonce) String() string { return hex.EncodeToHex(n[:]) } -func (n Nonce) Value() (driver.Value, error) { - return n.String(), nil -} - -func (n *Nonce) Scan(src interface{}) error { - stringVal, ok := src.([]byte) - if !ok { - return errors.New("invalid type assert") - } - - nn, decodeErr := hex.DecodeHex(string(stringVal)) - if decodeErr != nil { - return fmt.Errorf("unable to decode value, %w", decodeErr) - } - - copy(n[:], nn[:]) - - return nil -} - // MarshalText implements encoding.TextMarshaler func (n Nonce) MarshalText() ([]byte, error) { return []byte(n.String()), nil } -func (n *Nonce) UnmarshalText(input []byte) error { - if _, err := goHex.Decode( - n[:], - hex.DropHexPrefix(input), - ); err != nil { - return err - } - - return nil -} - func (h *Header) Copy() *Header { newHeader := &Header{ ParentHash: h.ParentHash, diff --git a/types/header_test.go b/types/header_test.go deleted file mode 100644 index 1f916d0361..0000000000 --- a/types/header_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package types - -import ( - "encoding/json" - "regexp" - "testing" - - "github.com/stretchr/testify/assert" -) - -// TestHeader_JSON makes sure the Header is properly -// marshalled and unmarshalled from JSON -func TestHeader_JSON(t *testing.T) { - t.Parallel() - - var ( - headerJSON = `{ - "parentHash": "0x0100000000000000000000000000000000000000000000000000000000000000", - "sha3Uncles" : "0x0200000000000000000000000000000000000000000000000000000000000000", - "miner": "0x0100000000000000000000000000000000000000", - "stateRoot" : "0x0400000000000000000000000000000000000000000000000000000000000000", - "transactionsRoot" : "0x0500000000000000000000000000000000000000000000000000000000000000", - "receiptsRoot" : "0x0600000000000000000000000000000000000000000000000000000000000000", - "logsBloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "difficulty" : "0xa", - "number" : "0xb", - "gasLimit" : "0xc", - "gasUsed" : "0xd", - "timestamp" : "0xe", - "extraData":"0x616263646566", - "mixHash" : "0x0700000000000000000000000000000000000000000000000000000000000000", - "nonce" : "0x0a00000000000000", - "hash" : "0x0800000000000000000000000000000000000000000000000000000000000000" - }` - header = Header{ - ParentHash: Hash{0x1}, - Sha3Uncles: Hash{0x2}, - Miner: Address{0x1}.Bytes(), - StateRoot: Hash{0x4}, - TxRoot: Hash{0x5}, - ReceiptsRoot: Hash{0x6}, - LogsBloom: Bloom{0x0}, - Difficulty: 10, - Number: 11, - GasLimit: 12, - GasUsed: 13, - Timestamp: 14, - ExtraData: []byte{97, 98, 99, 100, 101, 102}, - MixHash: Hash{0x7}, - Nonce: Nonce{10}, - Hash: Hash{0x8}, - } - rg = regexp.MustCompile(`(\t|\n| )+`) - ) - - t.Run("Header marshalled to JSON", func(t *testing.T) { - t.Parallel() - - marshalledHeader, err := json.Marshal(&header) - if err != nil { - t.Fatalf("Unable to marshal header, %v", err) - } - - assert.Equal(t, rg.ReplaceAllString(headerJSON, ""), string(marshalledHeader)) - }) - - t.Run("Header unmarshalled from JSON", func(t *testing.T) { - t.Parallel() - - unmarshalledHeader := Header{} - if err := json.Unmarshal([]byte(headerJSON), &unmarshalledHeader); err != nil { - t.Fatalf("unable to unmarshall JSON, %v", err) - } - - assert.Equal(t, header, unmarshalledHeader) - }) -} diff --git a/types/receipt.go b/types/receipt.go index b6da47a784..8b15186257 100644 --- a/types/receipt.go +++ b/types/receipt.go @@ -1,10 +1,6 @@ package types import ( - "database/sql/driver" - "errors" - "fmt" - goHex "encoding/hex" "github.com/0xPolygon/polygon-edge/helper/hex" @@ -71,26 +67,6 @@ func (b Bloom) String() string { return hex.EncodeToHex(b[:]) } -func (b Bloom) Value() (driver.Value, error) { - return b.String(), nil -} - -func (b *Bloom) Scan(src interface{}) error { - stringVal, ok := src.([]byte) - if !ok { - return errors.New("invalid type assert") - } - - bb, decodeErr := hex.DecodeHex(string(stringVal)) - if decodeErr != nil { - return fmt.Errorf("unable to decode value, %w", decodeErr) - } - - copy(b[:], bb[:]) - - return nil -} - // MarshalText implements encoding.TextMarshaler func (b Bloom) MarshalText() ([]byte, error) { return []byte(b.String()), nil diff --git a/types/types.go b/types/types.go index 50028eb969..2f17f61614 100644 --- a/types/types.go +++ b/types/types.go @@ -1,8 +1,6 @@ package types import ( - "database/sql/driver" - "errors" "fmt" "strings" "unicode" @@ -50,26 +48,6 @@ func (h Hash) String() string { return hex.EncodeToHex(h[:]) } -func (h Hash) Value() (driver.Value, error) { - return h.String(), nil -} - -func (h *Hash) Scan(src interface{}) error { - stringVal, ok := src.([]byte) - if !ok { - return errors.New("invalid type assert") - } - - hh, decodeErr := hex.DecodeHex(string(stringVal)) - if decodeErr != nil { - return fmt.Errorf("unable to decode value, %w", decodeErr) - } - - copy(h[:], hh[:]) - - return nil -} - // checksumEncode returns the checksummed address with 0x prefix, as by EIP-55 // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md func (a Address) checksumEncode() string { @@ -107,26 +85,6 @@ func (a Address) Bytes() []byte { return a[:] } -func (a Address) Value() (driver.Value, error) { - return a.String(), nil -} - -func (a *Address) Scan(src interface{}) error { - stringVal, ok := src.([]byte) - if !ok { - return errors.New("invalid type assert") - } - - aa, decodeErr := hex.DecodeHex(string(stringVal)) - if decodeErr != nil { - return fmt.Errorf("unable to decode value, %w", decodeErr) - } - - copy(a[:], aa[:]) - - return nil -} - func StringToHash(str string) Hash { return BytesToHash(StringToBytes(str)) } diff --git a/validators/store/contract/contract_test.go b/validators/store/contract/contract_test.go index 75f7f8ffb4..b7d81bb35f 100644 --- a/validators/store/contract/contract_test.go +++ b/validators/store/contract/contract_test.go @@ -473,21 +473,26 @@ func TestContractValidatorStore_CacheChange(t *testing.T) { ) ) - testCache := func(t *testing.T, expectedCache map[uint64]validators.Validators) { + type testCase struct { + height uint64 + expected validators.Validators + } + + testCache := func(t *testing.T, testCases ...testCase) { t.Helper() - assert.Equal(t, len(expectedCache), store.validatorSetCache.Len()) + assert.Equal(t, len(testCases), store.validatorSetCache.Len()) - for height, expected := range expectedCache { - cache, ok := store.validatorSetCache.Get(height) + for _, testCase := range testCases { + cache, ok := store.validatorSetCache.Get(testCase.height) - assert.Truef(t, ok, "validators for %d must exist, but not found") - assert.Equal(t, expected, cache) + assert.Truef(t, ok, "validators at %d must exist, but not found", testCase.height) + assert.Equal(t, testCase.expected, cache) } } // initial cache is empty - testCache(t, nil) + testCache(t) // overflow doesn't occur assert.False( @@ -495,19 +500,21 @@ func TestContractValidatorStore_CacheChange(t *testing.T) { store.saveToValidatorSetCache(0, ecdsaValidators1), ) - testCache(t, map[uint64]validators.Validators{ - 0: ecdsaValidators1, - }) + testCache( + t, + testCase{height: 0, expected: ecdsaValidators1}, + ) assert.False( t, store.saveToValidatorSetCache(1, ecdsaValidators2), ) - testCache(t, map[uint64]validators.Validators{ - 0: ecdsaValidators1, - 1: ecdsaValidators2, - }) + testCache( + t, + testCase{height: 0, expected: ecdsaValidators1}, + testCase{height: 1, expected: ecdsaValidators2}, + ) // make sure ecdsaValidators2 is loaded at the end for LRU cache store.validatorSetCache.Get(1) @@ -518,10 +525,11 @@ func TestContractValidatorStore_CacheChange(t *testing.T) { store.saveToValidatorSetCache(2, blsValidators), ) - testCache(t, map[uint64]validators.Validators{ - 1: ecdsaValidators2, - 2: blsValidators, - }) + testCache( + t, + testCase{height: 1, expected: ecdsaValidators2}, + testCase{height: 2, expected: blsValidators}, + ) } func TestContractValidatorStore_NoCache(t *testing.T) {