github.com/MikyChow/arbitrum-go-ethereum@v0.0.0-20230306102812-078da49636de/arbitrum/apibackend.go (about) 1 package arbitrum 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 ethereum "github.com/MikyChow/arbitrum-go-ethereum" 8 "math/big" 9 "strconv" 10 "strings" 11 "time" 12 13 "github.com/MikyChow/arbitrum-go-ethereum/eth" 14 "github.com/MikyChow/arbitrum-go-ethereum/eth/tracers" 15 "github.com/MikyChow/arbitrum-go-ethereum/log" 16 17 "github.com/MikyChow/arbitrum-go-ethereum/accounts" 18 "github.com/MikyChow/arbitrum-go-ethereum/common" 19 "github.com/MikyChow/arbitrum-go-ethereum/consensus" 20 "github.com/MikyChow/arbitrum-go-ethereum/core" 21 "github.com/MikyChow/arbitrum-go-ethereum/core/bloombits" 22 "github.com/MikyChow/arbitrum-go-ethereum/core/rawdb" 23 "github.com/MikyChow/arbitrum-go-ethereum/core/state" 24 "github.com/MikyChow/arbitrum-go-ethereum/core/types" 25 "github.com/MikyChow/arbitrum-go-ethereum/core/vm" 26 "github.com/MikyChow/arbitrum-go-ethereum/eth/filters" 27 "github.com/MikyChow/arbitrum-go-ethereum/ethdb" 28 "github.com/MikyChow/arbitrum-go-ethereum/event" 29 "github.com/MikyChow/arbitrum-go-ethereum/internal/ethapi" 30 "github.com/MikyChow/arbitrum-go-ethereum/params" 31 "github.com/MikyChow/arbitrum-go-ethereum/rpc" 32 ) 33 34 type APIBackend struct { 35 b *Backend 36 37 fallbackClient types.FallbackClient 38 sync SyncProgressBackend 39 } 40 41 type timeoutFallbackClient struct { 42 impl types.FallbackClient 43 timeout time.Duration 44 } 45 46 func (c *timeoutFallbackClient) CallContext(ctxIn context.Context, result interface{}, method string, args ...interface{}) error { 47 ctx, cancel := context.WithTimeout(ctxIn, c.timeout) 48 defer cancel() 49 return c.impl.CallContext(ctx, result, method, args...) 50 } 51 52 func CreateFallbackClient(fallbackClientUrl string, fallbackClientTimeout time.Duration) (types.FallbackClient, error) { 53 if fallbackClientUrl == "" { 54 return nil, nil 55 } 56 if strings.HasPrefix(fallbackClientUrl, "error:") { 57 fields := strings.Split(fallbackClientUrl, ":")[1:] 58 errNumber, convErr := strconv.ParseInt(fields[0], 0, 0) 59 if convErr == nil { 60 fields = fields[1:] 61 } else { 62 errNumber = -32000 63 } 64 types.SetFallbackError(strings.Join(fields, ":"), int(errNumber)) 65 return nil, nil 66 } 67 var fallbackClient types.FallbackClient 68 var err error 69 fallbackClient, err = rpc.Dial(fallbackClientUrl) 70 if fallbackClient == nil || err != nil { 71 return nil, fmt.Errorf("failed creating fallback connection: %w", err) 72 } 73 if fallbackClientTimeout != 0 { 74 fallbackClient = &timeoutFallbackClient{ 75 impl: fallbackClient, 76 timeout: fallbackClientTimeout, 77 } 78 } 79 return fallbackClient, nil 80 } 81 82 type SyncProgressBackend interface { 83 SyncProgressMap() map[string]interface{} 84 SafeBlockNumber(ctx context.Context) (uint64, error) 85 FinalizedBlockNumber(ctx context.Context) (uint64, error) 86 } 87 88 func createRegisterAPIBackend(backend *Backend, sync SyncProgressBackend, filterConfig filters.Config, fallbackClientUrl string, fallbackClientTimeout time.Duration) (*filters.FilterSystem, error) { 89 fallbackClient, err := CreateFallbackClient(fallbackClientUrl, fallbackClientTimeout) 90 if err != nil { 91 return nil, err 92 } 93 backend.apiBackend = &APIBackend{ 94 b: backend, 95 fallbackClient: fallbackClient, 96 sync: sync, 97 } 98 filterSystem := filters.NewFilterSystem(backend.apiBackend, filterConfig) 99 backend.stack.RegisterAPIs(backend.apiBackend.GetAPIs(filterSystem)) 100 return filterSystem, nil 101 } 102 103 func (a *APIBackend) GetAPIs(filterSystem *filters.FilterSystem) []rpc.API { 104 apis := ethapi.GetAPIs(a) 105 106 apis = append(apis, rpc.API{ 107 Namespace: "eth", 108 Version: "1.0", 109 Service: filters.NewFilterAPI(filterSystem, false), 110 Public: true, 111 }) 112 113 apis = append(apis, rpc.API{ 114 Namespace: "net", 115 Version: "1.0", 116 Service: NewPublicNetAPI(a.ChainConfig().ChainID.Uint64()), 117 Public: true, 118 }) 119 120 apis = append(apis, rpc.API{ 121 Namespace: "txpool", 122 Version: "1.0", 123 Service: NewPublicTxPoolAPI(), 124 Public: true, 125 }) 126 127 apis = append(apis, tracers.APIs(a)...) 128 129 return apis 130 } 131 132 func (a *APIBackend) blockChain() *core.BlockChain { 133 return a.b.arb.BlockChain() 134 } 135 136 func (a *APIBackend) GetArbitrumNode() interface{} { 137 return a.b.arb.ArbNode() 138 } 139 140 // General Ethereum API 141 func (a *APIBackend) SyncProgressMap() map[string]interface{} { 142 return a.sync.SyncProgressMap() 143 } 144 145 func (a *APIBackend) SyncProgress() ethereum.SyncProgress { 146 progress := a.sync.SyncProgressMap() 147 148 if progress == nil || len(progress) == 0 { 149 return ethereum.SyncProgress{} 150 } 151 return ethereum.SyncProgress{ 152 CurrentBlock: 0, 153 HighestBlock: 1, 154 } 155 } 156 157 func (a *APIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { 158 return big.NewInt(0), nil // there's no tips in L2 159 } 160 161 func (a *APIBackend) FeeHistory( 162 ctx context.Context, 163 blocks int, 164 newestBlock rpc.BlockNumber, 165 rewardPercentiles []float64, 166 ) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { 167 168 if core.GetArbOSSpeedLimitPerSecond == nil { 169 return nil, nil, nil, nil, errors.New("ArbOS not installed") 170 } 171 172 nitroGenesis := rpc.BlockNumber(a.ChainConfig().ArbitrumChainParams.GenesisBlockNum) 173 newestBlock, latestBlock := a.blockChain().ClipToPostNitroGenesis(newestBlock) 174 175 maxFeeHistory := int(a.b.config.FeeHistoryMaxBlockCount) 176 if blocks > maxFeeHistory { 177 log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory) 178 blocks = maxFeeHistory 179 } 180 if blocks < 1 { 181 // returning with no data and no error means there are no retrievable blocks 182 return common.Big0, nil, nil, nil, nil 183 } 184 185 // don't attempt to include blocks before genesis 186 if rpc.BlockNumber(blocks) > (newestBlock - nitroGenesis) { 187 blocks = int(newestBlock - nitroGenesis + 1) 188 } 189 oldestBlock := int(newestBlock) + 1 - blocks 190 191 // inform that tipping has no effect on inclusion 192 rewards := make([][]*big.Int, blocks) 193 zeros := make([]*big.Int, len(rewardPercentiles)) 194 for i := range zeros { 195 zeros[i] = common.Big0 196 } 197 for i := range rewards { 198 rewards[i] = zeros 199 } 200 if len(rewardPercentiles) == 0 { 201 rewards = nil 202 } 203 204 // use the most recent average compute rate for all blocks 205 // note: while we could query this value for each block, it'd be prohibitively expensive 206 state, _, err := a.StateAndHeaderByNumber(ctx, rpc.BlockNumber(newestBlock)) 207 if err != nil { 208 return common.Big0, nil, nil, nil, err 209 } 210 speedLimit, err := core.GetArbOSSpeedLimitPerSecond(state) 211 if err != nil { 212 return common.Big0, nil, nil, nil, err 213 } 214 215 gasUsed := make([]float64, blocks) 216 basefees := make([]*big.Int, blocks+1) // the RPC semantics are to predict the future value 217 218 // collect the basefees 219 baseFeeLookup := newestBlock + 1 220 if newestBlock == latestBlock { 221 baseFeeLookup = newestBlock 222 } 223 var prevTimestamp uint64 224 var timeSinceLastTimeChange uint64 225 var currentTimestampGasUsed uint64 226 if rpc.BlockNumber(oldestBlock) > nitroGenesis { 227 header, err := a.HeaderByNumber(ctx, rpc.BlockNumber(oldestBlock-1)) 228 if err != nil { 229 return common.Big0, nil, nil, nil, err 230 } 231 prevTimestamp = header.Time 232 } 233 for block := oldestBlock; block <= int(baseFeeLookup); block++ { 234 header, err := a.HeaderByNumber(ctx, rpc.BlockNumber(block)) 235 if err != nil { 236 return common.Big0, nil, nil, nil, err 237 } 238 basefees[block-oldestBlock] = header.BaseFee 239 240 if block > int(newestBlock) { 241 break 242 } 243 244 if header.Time > prevTimestamp { 245 timeSinceLastTimeChange = header.Time - prevTimestamp 246 currentTimestampGasUsed = 0 247 } 248 249 receipts := a.blockChain().GetReceiptsByHash(header.ReceiptHash) 250 for _, receipt := range receipts { 251 if receipt.GasUsed > receipt.GasUsedForL1 { 252 currentTimestampGasUsed += receipt.GasUsed - receipt.GasUsedForL1 253 } 254 } 255 256 prevTimestamp = header.Time 257 258 // In vanilla geth, this RPC returns the gasUsed ratio so a client can know how the basefee will change 259 // To emulate this, we translate the compute rate into something similar, centered at an analogous 0.5 260 var fullnessAnalogue float64 261 if timeSinceLastTimeChange > 0 { 262 fullnessAnalogue = float64(currentTimestampGasUsed) / float64(speedLimit) / float64(timeSinceLastTimeChange) / 2.0 263 if fullnessAnalogue > 1.0 { 264 fullnessAnalogue = 1.0 265 } 266 } else { 267 // We haven't looked far enough back to know the last timestamp change, 268 // so treat this block as full. 269 fullnessAnalogue = 1.0 270 } 271 gasUsed[block-oldestBlock] = fullnessAnalogue 272 273 } 274 if newestBlock == latestBlock { 275 basefees[blocks] = basefees[blocks-1] // guess the basefee won't change 276 } 277 278 return big.NewInt(int64(oldestBlock)), rewards, basefees, gasUsed, nil 279 } 280 281 func (a *APIBackend) ChainDb() ethdb.Database { 282 return a.b.chainDb 283 } 284 285 func (a *APIBackend) AccountManager() *accounts.Manager { 286 return a.b.stack.AccountManager() 287 } 288 289 func (a *APIBackend) ExtRPCEnabled() bool { 290 panic("not implemented") // TODO: Implement 291 } 292 293 func (a *APIBackend) RPCGasCap() uint64 { 294 return a.b.config.RPCGasCap 295 } 296 297 func (a *APIBackend) RPCTxFeeCap() float64 { 298 return a.b.config.RPCTxFeeCap 299 } 300 301 func (a *APIBackend) RPCEVMTimeout() time.Duration { 302 return a.b.config.RPCEVMTimeout 303 } 304 305 func (a *APIBackend) UnprotectedAllowed() bool { 306 return true // TODO: is that true? 307 } 308 309 // Blockchain API 310 func (a *APIBackend) SetHead(number uint64) { 311 panic("not implemented") // TODO: Implement 312 } 313 314 func (a *APIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { 315 return a.headerByNumberImpl(ctx, number) 316 } 317 318 func (a *APIBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { 319 return a.blockChain().GetHeaderByHash(hash), nil 320 } 321 322 func (a *APIBackend) blockNumberToUint(ctx context.Context, number rpc.BlockNumber) (uint64, error) { 323 if number == rpc.LatestBlockNumber || number == rpc.PendingBlockNumber { 324 return a.blockChain().CurrentBlock().Number().Uint64(), nil 325 } 326 if number == rpc.SafeBlockNumber { 327 return a.sync.SafeBlockNumber(ctx) 328 } 329 if number == rpc.FinalizedBlockNumber { 330 return a.sync.FinalizedBlockNumber(ctx) 331 } 332 if number < 0 { 333 return 0, errors.New("block number not supported") 334 } 335 return uint64(number.Int64()), nil 336 } 337 338 func (a *APIBackend) headerByNumberImpl(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { 339 if number == rpc.LatestBlockNumber || number == rpc.PendingBlockNumber { 340 return a.blockChain().CurrentBlock().Header(), nil 341 } 342 numUint, err := a.blockNumberToUint(ctx, number) 343 if err != nil { 344 return nil, err 345 } 346 return a.blockChain().GetHeaderByNumber(numUint), nil 347 } 348 349 func (a *APIBackend) headerByNumberOrHashImpl(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) { 350 number, isnum := blockNrOrHash.Number() 351 if isnum { 352 return a.headerByNumberImpl(ctx, number) 353 } 354 hash, ishash := blockNrOrHash.Hash() 355 if ishash { 356 return a.blockChain().GetHeaderByHash(hash), nil 357 } 358 return nil, errors.New("invalid arguments; neither block nor hash specified") 359 } 360 361 func (a *APIBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) { 362 return a.headerByNumberOrHashImpl(ctx, blockNrOrHash) 363 } 364 365 func (a *APIBackend) CurrentHeader() *types.Header { 366 return a.blockChain().CurrentHeader() 367 } 368 369 func (a *APIBackend) CurrentBlock() *types.Block { 370 return a.blockChain().CurrentBlock() 371 } 372 373 func (a *APIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { 374 if number == rpc.LatestBlockNumber || number == rpc.PendingBlockNumber { 375 return a.blockChain().CurrentBlock(), nil 376 } 377 numUint, err := a.blockNumberToUint(ctx, number) 378 if err != nil { 379 return nil, err 380 } 381 return a.blockChain().GetBlockByNumber(numUint), nil 382 } 383 384 func (a *APIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { 385 return a.blockChain().GetBlockByHash(hash), nil 386 } 387 388 func (a *APIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) { 389 number, isnum := blockNrOrHash.Number() 390 if isnum { 391 return a.BlockByNumber(ctx, number) 392 } 393 hash, ishash := blockNrOrHash.Hash() 394 if ishash { 395 return a.BlockByHash(ctx, hash) 396 } 397 return nil, errors.New("invalid arguments; neither block nor hash specified") 398 } 399 400 func (a *APIBackend) stateAndHeaderFromHeader(header *types.Header, err error) (*state.StateDB, *types.Header, error) { 401 if err != nil { 402 return nil, header, err 403 } 404 if header == nil { 405 return nil, nil, errors.New("header not found") 406 } 407 if !a.blockChain().Config().IsArbitrumNitro(header.Number) { 408 return nil, header, types.ErrUseFallback 409 } 410 state, err := a.blockChain().StateAt(header.Root) 411 return state, header, err 412 } 413 414 func (a *APIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { 415 return a.stateAndHeaderFromHeader(a.HeaderByNumber(ctx, number)) 416 } 417 418 func (a *APIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { 419 return a.stateAndHeaderFromHeader(a.HeaderByNumberOrHash(ctx, blockNrOrHash)) 420 } 421 422 func (a *APIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) { 423 if !a.blockChain().Config().IsArbitrumNitro(block.Number()) { 424 return nil, types.ErrUseFallback 425 } 426 // DEV: This assumes that `StateAtBlock` only accesses the blockchain and chainDb fields 427 return eth.NewArbEthereum(a.b.arb.BlockChain(), a.ChainDb()).StateAtBlock(block, reexec, base, checkLive, preferDisk) 428 } 429 430 func (a *APIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { 431 if !a.blockChain().Config().IsArbitrumNitro(block.Number()) { 432 return nil, vm.BlockContext{}, nil, types.ErrUseFallback 433 } 434 // DEV: This assumes that `StateAtTransaction` only accesses the blockchain and chainDb fields 435 return eth.NewArbEthereum(a.b.arb.BlockChain(), a.ChainDb()).StateAtTransaction(block, txIndex, reexec) 436 } 437 438 func (a *APIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { 439 return a.blockChain().GetReceiptsByHash(hash), nil 440 } 441 442 func (a *APIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { 443 if header := a.blockChain().GetHeaderByHash(hash); header != nil { 444 return a.blockChain().GetTd(hash, header.Number.Uint64()) 445 } 446 return nil 447 } 448 449 func (a *APIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) { 450 vmError := func() error { return nil } 451 if vmConfig == nil { 452 vmConfig = a.blockChain().GetVMConfig() 453 } 454 txContext := core.NewEVMTxContext(msg) 455 context := core.NewEVMBlockContext(header, a.blockChain(), nil) 456 return vm.NewEVM(context, txContext, state, a.blockChain().Config(), *vmConfig), vmError, nil 457 } 458 459 func (a *APIBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { 460 return a.blockChain().SubscribeChainEvent(ch) 461 } 462 463 func (a *APIBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { 464 return a.blockChain().SubscribeChainHeadEvent(ch) 465 } 466 467 func (a *APIBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { 468 return a.blockChain().SubscribeChainSideEvent(ch) 469 } 470 471 // Transaction pool API 472 func (a *APIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { 473 return a.b.EnqueueL2Message(ctx, signedTx) 474 } 475 476 func (a *APIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { 477 tx, blockHash, blockNumber, index := rawdb.ReadTransaction(a.b.chainDb, txHash) 478 return tx, blockHash, blockNumber, index, nil 479 } 480 481 func (a *APIBackend) GetPoolTransactions() (types.Transactions, error) { 482 // Arbitrum doesn't have a pool 483 return types.Transactions{}, nil 484 } 485 486 func (a *APIBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { 487 // Arbitrum doesn't have a pool 488 return nil 489 } 490 491 func (a *APIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { 492 stateDB, err := a.blockChain().State() 493 if err != nil { 494 return 0, err 495 } 496 return stateDB.GetNonce(addr), nil 497 } 498 499 func (a *APIBackend) Stats() (pending int, queued int) { 500 panic("not implemented") // TODO: Implement 501 } 502 503 func (a *APIBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { 504 panic("not implemented") // TODO: Implement 505 } 506 507 func (a *APIBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) { 508 panic("not implemented") // TODO: Implement 509 } 510 511 func (a *APIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { 512 return a.b.SubscribeNewTxsEvent(ch) 513 } 514 515 // Filter API 516 func (a *APIBackend) BloomStatus() (uint64, uint64) { 517 sections, _, _ := a.b.bloomIndexer.Sections() 518 return a.b.config.BloomBitsBlocks, sections 519 } 520 521 func (a *APIBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) { 522 return rawdb.ReadLogs(a.ChainDb(), hash, number, a.ChainConfig()), nil 523 } 524 525 func (a *APIBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { 526 for i := 0; i < bloomFilterThreads; i++ { 527 go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, a.b.bloomRequests) 528 } 529 } 530 531 func (a *APIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { 532 return a.blockChain().SubscribeLogsEvent(ch) 533 } 534 535 func (a *APIBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription { 536 //Arbitrum doesn't really need pending logs. Logs are published as soon as we know them.. 537 return a.SubscribeLogsEvent(ch) 538 } 539 540 func (a *APIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { 541 return a.blockChain().SubscribeRemovedLogsEvent(ch) 542 } 543 544 func (a *APIBackend) ChainConfig() *params.ChainConfig { 545 return a.blockChain().Config() 546 } 547 548 func (a *APIBackend) Engine() consensus.Engine { 549 return a.blockChain().Engine() 550 } 551 552 func (b *APIBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) { 553 return nil, nil 554 } 555 556 func (b *APIBackend) FallbackClient() types.FallbackClient { 557 return b.fallbackClient 558 }