github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/ethapi_backend.go (about) 1 package gossip 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "math/big" 8 "strconv" 9 "strings" 10 "time" 11 12 "github.com/pkg/errors" 13 "github.com/unicornultrafoundation/go-helios/hash" 14 "github.com/unicornultrafoundation/go-helios/native/idx" 15 16 "github.com/unicornultrafoundation/go-u2u/accounts" 17 "github.com/unicornultrafoundation/go-u2u/common" 18 "github.com/unicornultrafoundation/go-u2u/core/state" 19 "github.com/unicornultrafoundation/go-u2u/core/types" 20 "github.com/unicornultrafoundation/go-u2u/core/vm" 21 "github.com/unicornultrafoundation/go-u2u/ethapi" 22 "github.com/unicornultrafoundation/go-u2u/ethdb" 23 notify "github.com/unicornultrafoundation/go-u2u/event" 24 "github.com/unicornultrafoundation/go-u2u/evmcore" 25 "github.com/unicornultrafoundation/go-u2u/evmcore/txtracer" 26 "github.com/unicornultrafoundation/go-u2u/gossip/evmstore" 27 "github.com/unicornultrafoundation/go-u2u/native" 28 "github.com/unicornultrafoundation/go-u2u/native/iblockproc" 29 "github.com/unicornultrafoundation/go-u2u/params" 30 "github.com/unicornultrafoundation/go-u2u/rpc" 31 "github.com/unicornultrafoundation/go-u2u/topicsdb" 32 "github.com/unicornultrafoundation/go-u2u/tracing" 33 "github.com/unicornultrafoundation/go-u2u/u2u" 34 ) 35 36 // EthAPIBackend implements ethapi.Backend. 37 type EthAPIBackend struct { 38 extRPCEnabled bool 39 svc *Service 40 state *EvmStateReader 41 signer types.Signer 42 allowUnprotectedTxs bool 43 } 44 45 // SetExtRPCEnabled updates extRPCEnabled 46 func (b *EthAPIBackend) SetExtRPCEnabled(v bool) { 47 b.extRPCEnabled = v 48 } 49 50 // ChainConfig returns the active chain configuration. 51 func (b *EthAPIBackend) ChainConfig() *params.ChainConfig { 52 return b.svc.store.GetEvmChainConfig() 53 } 54 55 func (b *EthAPIBackend) CurrentBlock() *evmcore.EvmBlock { 56 return b.state.CurrentBlock() 57 } 58 59 func (b *EthAPIBackend) ResolveRpcBlockNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (idx.Block, error) { 60 latest := b.svc.store.GetLatestBlockIndex() 61 if number, ok := blockNrOrHash.Number(); ok && (number == rpc.LatestBlockNumber || number == rpc.PendingBlockNumber) { 62 return latest, nil 63 } else if number, ok := blockNrOrHash.Number(); ok { 64 if idx.Block(number) > latest { 65 return 0, errors.New("block not found") 66 } 67 return idx.Block(number), nil 68 } else if h, ok := blockNrOrHash.Hash(); ok { 69 index := b.svc.store.GetBlockIndex(hash.Event(h)) 70 if index == nil { 71 return 0, errors.New("block not found") 72 } 73 return *index, nil 74 } 75 return 0, errors.New("unknown header selector") 76 } 77 78 // HeaderByNumber returns evm block header by its number, or nil if not exists. 79 func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*evmcore.EvmHeader, error) { 80 blk, err := b.BlockByNumber(ctx, number) 81 if err != nil { 82 return nil, err 83 } 84 if blk == nil { 85 return nil, nil 86 } 87 return blk.Header(), err 88 } 89 90 // HeaderByHash returns evm block header by its (atropos) hash, or nil if not exists. 91 func (b *EthAPIBackend) HeaderByHash(ctx context.Context, h common.Hash) (*evmcore.EvmHeader, error) { 92 index := b.svc.store.GetBlockIndex(hash.Event(h)) 93 if index == nil { 94 return nil, nil 95 } 96 return b.HeaderByNumber(ctx, rpc.BlockNumber(*index)) 97 } 98 99 // TxTraceByHash returns transaction trace from store db by the hash. 100 func (b *EthAPIBackend) TxTraceByHash(ctx context.Context, h common.Hash) (*[]txtracer.ActionTrace, error) { 101 if b.state.store.txtracer == nil { 102 return nil, errors.New("Transaction trace key-value store db is not initialized") 103 } 104 txBytes := b.state.store.txtracer.GetTx(h) 105 traces := make([]txtracer.ActionTrace, 0) 106 json.Unmarshal(txBytes, &traces) 107 if len(traces) == 0 { 108 return nil, fmt.Errorf("No trace for tx hash: %s", h.String()) 109 } 110 return &traces, nil 111 } 112 113 // TxTraceSave saves transaction trace into store db 114 func (b *EthAPIBackend) TxTraceSave(ctx context.Context, h common.Hash, traces []byte) error { 115 if b.state.store.txtracer != nil { 116 return b.state.store.txtracer.SetTxTrace(h, traces) 117 } 118 return errors.New("Transaction trace key-value store db is not initialized") 119 } 120 121 // BlockByNumber returns evm block by its number, or nil if not exists. 122 func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*evmcore.EvmBlock, error) { 123 if number == rpc.PendingBlockNumber { 124 number = rpc.LatestBlockNumber 125 } 126 // Otherwise, resolve and return the block 127 var blk *evmcore.EvmBlock 128 if number == rpc.LatestBlockNumber { 129 blk = b.state.CurrentBlock() 130 } else { 131 n := uint64(number.Int64()) 132 blk = b.state.GetBlock(common.Hash{}, n) 133 } 134 135 return blk, nil 136 } 137 138 // StateAndHeaderByNumberOrHash returns evm state and block header by block number or block hash, err if not exists. 139 func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *evmcore.EvmHeader, error) { 140 var header *evmcore.EvmHeader 141 if number, ok := blockNrOrHash.Number(); ok && (number == rpc.LatestBlockNumber || number == rpc.PendingBlockNumber) { 142 header = &b.state.CurrentBlock().EvmHeader 143 } else if number, ok := blockNrOrHash.Number(); ok { 144 header = b.state.GetHeader(common.Hash{}, uint64(number)) 145 } else if h, ok := blockNrOrHash.Hash(); ok { 146 index := b.svc.store.GetBlockIndex(hash.Event(h)) 147 if index == nil { 148 return nil, nil, errors.New("header not found") 149 } 150 header = b.state.GetHeader(common.Hash{}, uint64(*index)) 151 } else { 152 return nil, nil, errors.New("unknown header selector") 153 } 154 if header == nil { 155 return nil, nil, errors.New("header not found") 156 } 157 stateDb, err := b.svc.store.evm.StateDB(hash.Hash(header.Root)) 158 if err != nil { 159 return nil, nil, err 160 } 161 return stateDb, header, nil 162 } 163 164 // decodeShortEventID decodes ShortID 165 // example of a ShortID: "5:26:a2395846", where 5 is epoch, 26 is lamport, a2395846 are first bytes of the hash 166 // s is a string splitted by ":" separator 167 func decodeShortEventID(s []string) (idx.Epoch, idx.Lamport, []byte, error) { 168 if len(s) != 3 { 169 return 0, 0, nil, errors.New("incorrect format of short event ID (need Epoch:Lamport:Hash") 170 } 171 epoch, err := strconv.ParseUint(s[0], 10, 32) 172 if err != nil { 173 return 0, 0, nil, errors.Wrap(err, "short hash parsing error (lamport)") 174 } 175 lamport, err := strconv.ParseUint(s[1], 10, 32) 176 if err != nil { 177 return 0, 0, nil, errors.Wrap(err, "short hash parsing error (lamport)") 178 } 179 return idx.Epoch(epoch), idx.Lamport(lamport), common.FromHex(s[2]), nil 180 } 181 182 // GetFullEventID "converts" ShortID to full event's hash, by searching in events DB. 183 func (b *EthAPIBackend) GetFullEventID(shortEventID string) (hash.Event, error) { 184 s := strings.Split(shortEventID, ":") 185 if len(s) == 1 { 186 // it's a full hash 187 return hash.HexToEventHash(shortEventID), nil 188 } 189 // short hash 190 epoch, lamport, prefix, err := decodeShortEventID(s) 191 if err != nil { 192 return hash.Event{}, err 193 } 194 195 options := b.svc.store.FindEventHashes(epoch, lamport, prefix) 196 if len(options) == 0 { 197 return hash.Event{}, errors.New("event not found by short ID") 198 } 199 if len(options) > 1 { 200 return hash.Event{}, errors.New("there're multiple events with the same short ID, please use full ID") 201 } 202 return options[0], nil 203 } 204 205 // GetEventPayload returns Hashgraph event by hash or short ID. 206 func (b *EthAPIBackend) GetEventPayload(ctx context.Context, shortEventID string) (*native.EventPayload, error) { 207 id, err := b.GetFullEventID(shortEventID) 208 if err != nil { 209 return nil, err 210 } 211 return b.svc.store.GetEventPayload(id), nil 212 } 213 214 // GetEvent returns the Hashgraph event header by hash or short ID. 215 func (b *EthAPIBackend) GetEvent(ctx context.Context, shortEventID string) (*native.Event, error) { 216 id, err := b.GetFullEventID(shortEventID) 217 if err != nil { 218 return nil, err 219 } 220 return b.svc.store.GetEvent(id), nil 221 } 222 223 // GetHeads returns IDs of all the epoch events with no descendants. 224 // * When epoch is -2 the heads for latest epoch are returned. 225 // * When epoch is -1 the heads for latest sealed epoch are returned. 226 func (b *EthAPIBackend) GetHeads(ctx context.Context, epoch rpc.BlockNumber) (heads hash.Events, err error) { 227 current := b.svc.store.GetEpoch() 228 229 requested, err := b.epochWithDefault(ctx, epoch) 230 if err != nil { 231 return nil, err 232 } 233 234 if requested == current { 235 heads = b.svc.store.GetHeadsSlice(requested) 236 } else { 237 err = errors.New("heads for previous epochs are not available") 238 return 239 } 240 241 if heads == nil { 242 heads = hash.Events{} 243 } 244 245 return 246 } 247 248 func (b *EthAPIBackend) epochWithDefault(ctx context.Context, epoch rpc.BlockNumber) (requested idx.Epoch, err error) { 249 current := b.svc.store.GetEpoch() 250 251 switch { 252 case epoch == rpc.PendingBlockNumber: 253 requested = current 254 case epoch == rpc.LatestBlockNumber: 255 requested = current - 1 256 case epoch >= 0 && idx.Epoch(epoch) <= current: 257 requested = idx.Epoch(epoch) 258 default: 259 err = errors.New("epoch is not in range") 260 return 261 } 262 return requested, nil 263 } 264 265 // ForEachEpochEvent iterates all the events which are observed by head, and accepted by a filter. 266 // filter CANNOT called twice for the same event. 267 func (b *EthAPIBackend) ForEachEpochEvent(ctx context.Context, epoch rpc.BlockNumber, onEvent func(event *native.EventPayload) bool) error { 268 requested, err := b.epochWithDefault(ctx, epoch) 269 if err != nil { 270 return err 271 } 272 273 b.svc.store.ForEachEpochEvent(requested, onEvent) 274 return nil 275 } 276 277 func (b *EthAPIBackend) BlockByHash(ctx context.Context, h common.Hash) (*evmcore.EvmBlock, error) { 278 index := b.svc.store.GetBlockIndex(hash.Event(h)) 279 if index == nil { 280 return nil, nil 281 } 282 283 if rpc.BlockNumber(*index) == rpc.PendingBlockNumber { 284 return nil, errors.New("pending block request isn't allowed") 285 } 286 // Otherwise resolve and return the block 287 var blk *evmcore.EvmBlock 288 if rpc.BlockNumber(*index) == rpc.LatestBlockNumber { 289 blk = b.state.CurrentBlock() 290 } else { 291 n := uint64(*index) 292 blk = b.state.GetBlock(common.Hash{}, n) 293 } 294 295 return blk, nil 296 } 297 298 // GetReceiptsByNumber returns receipts by block number. 299 func (b *EthAPIBackend) GetReceiptsByNumber(ctx context.Context, number rpc.BlockNumber) (types.Receipts, error) { 300 if !b.svc.config.TxIndex { 301 return nil, errors.New("transactions index is disabled (enable TxIndex and re-process the DAGs)") 302 } 303 304 if number == rpc.PendingBlockNumber { 305 number = rpc.LatestBlockNumber 306 } 307 if number == rpc.LatestBlockNumber { 308 header := b.state.CurrentHeader() 309 number = rpc.BlockNumber(header.Number.Uint64()) 310 } 311 312 block := b.state.GetBlock(common.Hash{}, uint64(number)) 313 receipts := b.svc.store.evm.GetReceipts(idx.Block(number), b.signer, block.Hash, block.Transactions) 314 return receipts, nil 315 } 316 317 // GetReceipts retrieves the receipts for all transactions in a given block. 318 func (b *EthAPIBackend) GetReceipts(ctx context.Context, block common.Hash) (types.Receipts, error) { 319 number := b.svc.store.GetBlockIndex(hash.Event(block)) 320 if number == nil { 321 return nil, nil 322 } 323 324 return b.GetReceiptsByNumber(ctx, rpc.BlockNumber(*number)) 325 } 326 327 func (b *EthAPIBackend) GetLogs(ctx context.Context, block common.Hash) ([][]*types.Log, error) { 328 receipts, err := b.GetReceipts(ctx, block) 329 if receipts == nil || err != nil { 330 return nil, err 331 } 332 logs := make([][]*types.Log, receipts.Len()) 333 for i, receipt := range receipts { 334 logs[i] = receipt.Logs 335 } 336 return logs, nil 337 } 338 339 func (b *EthAPIBackend) GetTd(_ common.Hash) *big.Int { 340 return big.NewInt(0) 341 } 342 343 func (b *EthAPIBackend) GetEVM(ctx context.Context, msg evmcore.Message, state *state.StateDB, header *evmcore.EvmHeader, vmConfig *vm.Config) (*vm.EVM, func() error, error) { 344 vmError := func() error { return nil } 345 346 if vmConfig == nil { 347 vmConfig = &u2u.DefaultVMConfig 348 } 349 txContext := evmcore.NewEVMTxContext(msg) 350 context := evmcore.NewEVMBlockContext(header, b.state, nil) 351 config := b.ChainConfig() 352 return vm.NewEVM(context, txContext, state, config, *vmConfig), vmError, nil 353 } 354 355 func (b *EthAPIBackend) GetBlockContext(header *evmcore.EvmHeader) vm.BlockContext { 356 return evmcore.NewEVMBlockContext(header, b.state, nil) 357 } 358 359 func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { 360 err := b.svc.txpool.AddLocal(signedTx) 361 if err == nil { 362 // NOTE: only sent txs tracing, see TxPool.addTxs() for all 363 tracing.StartTx(signedTx.Hash(), "EthAPIBackend.SendTx()") 364 } 365 return err 366 } 367 368 func (b *EthAPIBackend) SubscribeLogsNotify(ch chan<- []*types.Log) notify.Subscription { 369 return b.svc.feed.SubscribeNewLogs(ch) 370 } 371 372 func (b *EthAPIBackend) SubscribeNewBlockNotify(ch chan<- evmcore.ChainHeadNotify) notify.Subscription { 373 return b.svc.feed.SubscribeNewBlock(ch) 374 } 375 376 func (b *EthAPIBackend) SubscribeNewTxsNotify(ch chan<- evmcore.NewTxsNotify) notify.Subscription { 377 return b.svc.txpool.SubscribeNewTxsNotify(ch) 378 } 379 380 func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) { 381 pending, err := b.svc.txpool.Pending(false) 382 if err != nil { 383 return nil, err 384 } 385 var txs types.Transactions 386 for _, batch := range pending { 387 txs = append(txs, batch...) 388 } 389 return txs, nil 390 } 391 392 func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction { 393 return b.svc.txpool.Get(hash) 394 } 395 396 func (b *EthAPIBackend) GetTxPosition(txHash common.Hash) *evmstore.TxPosition { 397 return b.svc.store.evm.GetTxPosition(txHash) 398 } 399 400 func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, uint64, uint64, error) { 401 if !b.svc.config.TxIndex { 402 return nil, 0, 0, errors.New("transactions index is disabled (enable TxIndex and re-process the DAG)") 403 } 404 405 position := b.svc.store.evm.GetTxPosition(txHash) 406 if position == nil { 407 return nil, 0, 0, nil 408 } 409 410 var tx *types.Transaction 411 if position.Event.IsZero() { 412 tx = b.svc.store.evm.GetTx(txHash) 413 } else { 414 event := b.svc.store.GetEventPayload(position.Event) 415 if position.EventOffset > uint32(event.Txs().Len()) { 416 return nil, 0, 0, fmt.Errorf("transactions index is corrupted (offset is larger than number of txs in event), event=%s, txid=%s, block=%d, offset=%d, txs_num=%d", 417 position.Event.String(), 418 txHash.String(), 419 position.Block, 420 position.EventOffset, 421 event.Txs().Len()) 422 } 423 tx = event.Txs()[position.EventOffset] 424 } 425 426 return tx, uint64(position.Block), uint64(position.BlockOffset), nil 427 } 428 429 func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { 430 return b.svc.txpool.Nonce(addr), nil 431 } 432 433 func (b *EthAPIBackend) Stats() (pending int, queued int) { 434 return b.svc.txpool.Stats() 435 } 436 437 func (b *EthAPIBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { 438 return b.svc.txpool.Content() 439 } 440 441 // Progress returns current synchronization status of this node 442 func (b *EthAPIBackend) Progress() ethapi.PeerProgress { 443 p2pProgress := b.svc.handler.myProgress() 444 highestP2pProgress := b.svc.handler.highestPeerProgress() 445 lastBlock := b.svc.store.GetBlock(p2pProgress.LastBlockIdx) 446 447 return ethapi.PeerProgress{ 448 CurrentEpoch: p2pProgress.Epoch, 449 CurrentBlock: p2pProgress.LastBlockIdx, 450 CurrentBlockHash: p2pProgress.LastBlockAtropos, 451 CurrentBlockTime: lastBlock.Time, 452 HighestBlock: highestP2pProgress.LastBlockIdx, 453 HighestEpoch: highestP2pProgress.Epoch, 454 } 455 } 456 457 func (b *EthAPIBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) { 458 return b.svc.txpool.ContentFrom(addr) 459 } 460 461 func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context, certainty uint64) *big.Int { 462 return b.svc.gpo.SuggestTip(certainty) 463 } 464 465 func (b *EthAPIBackend) EffectiveMinGasPrice(ctx context.Context) *big.Int { 466 return b.svc.gpo.EffectiveMinGasPrice() 467 } 468 469 func (b *EthAPIBackend) ChainDb() ethdb.Database { 470 return b.svc.store.evm.EvmDb 471 } 472 473 func (b *EthAPIBackend) AccountManager() *accounts.Manager { 474 return b.svc.AccountManager() 475 } 476 477 func (b *EthAPIBackend) ExtRPCEnabled() bool { 478 return b.extRPCEnabled 479 } 480 481 func (b *EthAPIBackend) UnprotectedAllowed() bool { 482 return b.allowUnprotectedTxs 483 } 484 485 func (b *EthAPIBackend) RPCGasCap() uint64 { 486 return b.svc.config.RPCGasCap 487 } 488 489 func (b *EthAPIBackend) RPCTimeout() time.Duration { 490 return b.svc.config.RPCTimeout 491 } 492 493 func (b *EthAPIBackend) RPCTxFeeCap() float64 { 494 return b.svc.config.RPCTxFeeCap 495 } 496 497 func (b *EthAPIBackend) EvmLogIndex() topicsdb.Index { 498 return b.svc.store.evm.EvmLogs 499 } 500 501 // CurrentEpoch returns current epoch number. 502 func (b *EthAPIBackend) CurrentEpoch(ctx context.Context) idx.Epoch { 503 return b.svc.store.GetEpoch() 504 } 505 506 func (b *EthAPIBackend) MinGasPrice() *big.Int { 507 return b.state.MinGasPrice() 508 } 509 func (b *EthAPIBackend) MaxGasLimit() uint64 { 510 return b.state.MaxGasLimit() 511 } 512 513 func (b *EthAPIBackend) GetUptime(ctx context.Context, vid idx.ValidatorID) (*big.Int, error) { 514 // Note: loads bs and es atomically to avoid a race condition 515 bs, es := b.svc.store.GetBlockEpochState() 516 if !es.Validators.Exists(vid) { 517 return nil, nil 518 } 519 return new(big.Int).SetUint64(uint64(bs.GetValidatorState(vid, es.Validators).Uptime)), nil 520 } 521 522 func (b *EthAPIBackend) GetOriginatedFee(ctx context.Context, vid idx.ValidatorID) (*big.Int, error) { 523 // Note: loads bs and es atomically to avoid a race condition 524 bs, es := b.svc.store.GetBlockEpochState() 525 if !es.Validators.Exists(vid) { 526 return nil, nil 527 } 528 return bs.GetValidatorState(vid, es.Validators).Originated, nil 529 } 530 531 func (b *EthAPIBackend) GetDowntime(ctx context.Context, vid idx.ValidatorID) (idx.Block, native.Timestamp, error) { 532 // Note: loads bs and es atomically to avoid a race condition 533 bs, es := b.svc.store.GetBlockEpochState() 534 if !es.Validators.Exists(vid) { 535 return 0, 0, nil 536 } 537 vs := bs.GetValidatorState(vid, es.Validators) 538 missedBlocks := idx.Block(0) 539 if bs.LastBlock.Idx > vs.LastBlock { 540 missedBlocks = bs.LastBlock.Idx - vs.LastBlock 541 } 542 missedTime := native.Timestamp(0) 543 if bs.LastBlock.Time > vs.LastOnlineTime { 544 missedTime = bs.LastBlock.Time - vs.LastOnlineTime 545 } 546 if missedBlocks < es.Rules.Economy.BlockMissedSlack { 547 return 0, 0, nil 548 } 549 return missedBlocks, missedTime, nil 550 } 551 552 func (b *EthAPIBackend) GetEpochBlockState(ctx context.Context, epoch rpc.BlockNumber) (*iblockproc.BlockState, *iblockproc.EpochState, error) { 553 if epoch == rpc.PendingBlockNumber { 554 bs, es := b.svc.store.GetBlockState(), b.svc.store.GetEpochState() 555 return &bs, &es, nil 556 } 557 if epoch == rpc.LatestBlockNumber { 558 epoch = rpc.BlockNumber(b.svc.store.GetEpoch()) 559 } 560 bs, es := b.svc.store.GetHistoryBlockEpochState(idx.Epoch(epoch)) 561 return bs, es, nil 562 } 563 564 func (b *EthAPIBackend) CalcBlockExtApi() bool { 565 return b.svc.config.RPCBlockExt 566 } 567 568 func (b *EthAPIBackend) SealedEpochTiming(ctx context.Context) (start native.Timestamp, end native.Timestamp) { 569 es := b.svc.store.GetEpochState() 570 return es.PrevEpochStart, es.EpochStart 571 } 572 573 func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *evmcore.EvmBlock, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) { 574 return b.svc.stateAtBlock(block, reexec, base, checkLive) 575 } 576 577 func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *evmcore.EvmBlock, txIndex int, reexec uint64) (evmcore.Message, vm.BlockContext, *state.StateDB, error) { 578 return b.svc.stateAtTransaction(block, txIndex, reexec) 579 }