github.com/phillinzzz/newBsc@v1.1.6/eth/catalyst/api.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package catalyst implements the temporary eth1/eth2 RPC integration. 18 package catalyst 19 20 import ( 21 "errors" 22 "fmt" 23 "math/big" 24 "time" 25 26 "github.com/phillinzzz/newBsc/common" 27 "github.com/phillinzzz/newBsc/core" 28 "github.com/phillinzzz/newBsc/core/state" 29 "github.com/phillinzzz/newBsc/core/types" 30 "github.com/phillinzzz/newBsc/eth" 31 "github.com/phillinzzz/newBsc/log" 32 "github.com/phillinzzz/newBsc/node" 33 chainParams "github.com/phillinzzz/newBsc/params" 34 "github.com/phillinzzz/newBsc/rpc" 35 "github.com/phillinzzz/newBsc/trie" 36 ) 37 38 // Register adds catalyst APIs to the node. 39 func Register(stack *node.Node, backend *eth.Ethereum) error { 40 chainconfig := backend.BlockChain().Config() 41 if chainconfig.CatalystBlock == nil { 42 return errors.New("catalystBlock is not set in genesis config") 43 } else if chainconfig.CatalystBlock.Sign() != 0 { 44 return errors.New("catalystBlock of genesis config must be zero") 45 } 46 47 log.Warn("Catalyst mode enabled") 48 stack.RegisterAPIs([]rpc.API{ 49 { 50 Namespace: "consensus", 51 Version: "1.0", 52 Service: newConsensusAPI(backend), 53 Public: true, 54 }, 55 }) 56 return nil 57 } 58 59 type consensusAPI struct { 60 eth *eth.Ethereum 61 } 62 63 func newConsensusAPI(eth *eth.Ethereum) *consensusAPI { 64 return &consensusAPI{eth: eth} 65 } 66 67 // blockExecutionEnv gathers all the data required to execute 68 // a block, either when assembling it or when inserting it. 69 type blockExecutionEnv struct { 70 chain *core.BlockChain 71 state *state.StateDB 72 tcount int 73 gasPool *core.GasPool 74 75 header *types.Header 76 txs []*types.Transaction 77 receipts []*types.Receipt 78 } 79 80 func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase common.Address) error { 81 vmconfig := *env.chain.GetVMConfig() 82 receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vmconfig, core.NewReceiptBloomGenerator()) 83 if err != nil { 84 return err 85 } 86 env.txs = append(env.txs, tx) 87 env.receipts = append(env.receipts, receipt) 88 return nil 89 } 90 91 func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) (*blockExecutionEnv, error) { 92 state, err := api.eth.BlockChain().StateAt(parent.Root()) 93 if err != nil { 94 return nil, err 95 } 96 env := &blockExecutionEnv{ 97 chain: api.eth.BlockChain(), 98 state: state, 99 header: header, 100 gasPool: new(core.GasPool).AddGas(header.GasLimit), 101 } 102 return env, nil 103 } 104 105 // AssembleBlock creates a new block, inserts it into the chain, and returns the "execution 106 // data" required for eth2 clients to process the new block. 107 func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableData, error) { 108 log.Info("Producing block", "parentHash", params.ParentHash) 109 110 bc := api.eth.BlockChain() 111 parent := bc.GetBlockByHash(params.ParentHash) 112 if parent == nil { 113 log.Warn("Cannot assemble block with parent hash to unknown block", "parentHash", params.ParentHash) 114 return nil, fmt.Errorf("cannot assemble block with unknown parent %s", params.ParentHash) 115 } 116 117 pool := api.eth.TxPool() 118 119 if parent.Time() >= params.Timestamp { 120 return nil, fmt.Errorf("child timestamp lower than parent's: %d >= %d", parent.Time(), params.Timestamp) 121 } 122 if now := uint64(time.Now().Unix()); params.Timestamp > now+1 { 123 wait := time.Duration(params.Timestamp-now) * time.Second 124 log.Info("Producing block too far in the future", "wait", common.PrettyDuration(wait)) 125 time.Sleep(wait) 126 } 127 128 pending, err := pool.Pending() 129 if err != nil { 130 return nil, err 131 } 132 133 coinbase, err := api.eth.Etherbase() 134 if err != nil { 135 return nil, err 136 } 137 num := parent.Number() 138 header := &types.Header{ 139 ParentHash: parent.Hash(), 140 Number: num.Add(num, common.Big1), 141 Coinbase: coinbase, 142 GasLimit: parent.GasLimit(), // Keep the gas limit constant in this prototype 143 Extra: []byte{}, 144 Time: params.Timestamp, 145 } 146 err = api.eth.Engine().Prepare(bc, header) 147 if err != nil { 148 return nil, err 149 } 150 151 env, err := api.makeEnv(parent, header) 152 if err != nil { 153 return nil, err 154 } 155 156 var ( 157 signer = types.MakeSigner(bc.Config(), header.Number) 158 txHeap = types.NewTransactionsByPriceAndNonce(signer, pending) 159 transactions []*types.Transaction 160 ) 161 for { 162 if env.gasPool.Gas() < chainParams.TxGas { 163 log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", chainParams.TxGas) 164 break 165 } 166 tx := txHeap.Peek() 167 if tx == nil { 168 break 169 } 170 171 // The sender is only for logging purposes, and it doesn't really matter if it's correct. 172 from, _ := types.Sender(signer, tx) 173 174 // Execute the transaction 175 env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) 176 err = env.commitTransaction(tx, coinbase) 177 switch err { 178 case core.ErrGasLimitReached: 179 // Pop the current out-of-gas transaction without shifting in the next from the account 180 log.Trace("Gas limit exceeded for current block", "sender", from) 181 txHeap.Pop() 182 183 case core.ErrNonceTooLow: 184 // New head notification data race between the transaction pool and miner, shift 185 log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) 186 txHeap.Shift() 187 188 case core.ErrNonceTooHigh: 189 // Reorg notification data race between the transaction pool and miner, skip account = 190 log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce()) 191 txHeap.Pop() 192 193 case nil: 194 // Everything ok, collect the logs and shift in the next transaction from the same account 195 env.tcount++ 196 txHeap.Shift() 197 transactions = append(transactions, tx) 198 199 default: 200 // Strange error, discard the transaction and get the next in line (note, the 201 // nonce-too-high clause will prevent us from executing in vain). 202 log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) 203 txHeap.Shift() 204 } 205 } 206 207 // Create the block. 208 block, _, err := api.eth.Engine().FinalizeAndAssemble(bc, header, env.state, transactions, nil /* uncles */, env.receipts) 209 if err != nil { 210 return nil, err 211 } 212 return &executableData{ 213 BlockHash: block.Hash(), 214 ParentHash: block.ParentHash(), 215 Miner: block.Coinbase(), 216 StateRoot: block.Root(), 217 Number: block.NumberU64(), 218 GasLimit: block.GasLimit(), 219 GasUsed: block.GasUsed(), 220 Timestamp: block.Time(), 221 ReceiptRoot: block.ReceiptHash(), 222 LogsBloom: block.Bloom().Bytes(), 223 Transactions: encodeTransactions(block.Transactions()), 224 }, nil 225 } 226 227 func encodeTransactions(txs []*types.Transaction) [][]byte { 228 var enc = make([][]byte, len(txs)) 229 for i, tx := range txs { 230 enc[i], _ = tx.MarshalBinary() 231 } 232 return enc 233 } 234 235 func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { 236 var txs = make([]*types.Transaction, len(enc)) 237 for i, encTx := range enc { 238 var tx types.Transaction 239 if err := tx.UnmarshalBinary(encTx); err != nil { 240 return nil, fmt.Errorf("invalid transaction %d: %v", i, err) 241 } 242 txs[i] = &tx 243 } 244 return txs, nil 245 } 246 247 func insertBlockParamsToBlock(params executableData) (*types.Block, error) { 248 txs, err := decodeTransactions(params.Transactions) 249 if err != nil { 250 return nil, err 251 } 252 253 number := big.NewInt(0) 254 number.SetUint64(params.Number) 255 header := &types.Header{ 256 ParentHash: params.ParentHash, 257 UncleHash: types.EmptyUncleHash, 258 Coinbase: params.Miner, 259 Root: params.StateRoot, 260 TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), 261 ReceiptHash: params.ReceiptRoot, 262 Bloom: types.BytesToBloom(params.LogsBloom), 263 Difficulty: big.NewInt(1), 264 Number: number, 265 GasLimit: params.GasLimit, 266 GasUsed: params.GasUsed, 267 Time: params.Timestamp, 268 } 269 block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) 270 return block, nil 271 } 272 273 // NewBlock creates an Eth1 block, inserts it in the chain, and either returns true, 274 // or false + an error. This is a bit redundant for go, but simplifies things on the 275 // eth2 side. 276 func (api *consensusAPI) NewBlock(params executableData) (*newBlockResponse, error) { 277 parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) 278 if parent == nil { 279 return &newBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash) 280 } 281 block, err := insertBlockParamsToBlock(params) 282 if err != nil { 283 return nil, err 284 } 285 286 _, err = api.eth.BlockChain().InsertChainWithoutSealVerification(block) 287 return &newBlockResponse{err == nil}, err 288 } 289 290 // Used in tests to add a the list of transactions from a block to the tx pool. 291 func (api *consensusAPI) addBlockTxs(block *types.Block) error { 292 for _, tx := range block.Transactions() { 293 api.eth.TxPool().AddLocal(tx) 294 } 295 return nil 296 } 297 298 // FinalizeBlock is called to mark a block as synchronized, so 299 // that data that is no longer needed can be removed. 300 func (api *consensusAPI) FinalizeBlock(blockHash common.Hash) (*genericResponse, error) { 301 return &genericResponse{true}, nil 302 } 303 304 // SetHead is called to perform a force choice. 305 func (api *consensusAPI) SetHead(newHead common.Hash) (*genericResponse, error) { 306 return &genericResponse{true}, nil 307 }