-
Notifications
You must be signed in to change notification settings - Fork 21.9k
core: Make ExecuteStateless fully self-validating
#34007
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,7 +20,6 @@ import ( | |
| "errors" | ||
| "fmt" | ||
|
|
||
| "github.com/ethereum/go-ethereum/consensus" | ||
| "github.com/ethereum/go-ethereum/core/state" | ||
| "github.com/ethereum/go-ethereum/core/types" | ||
| "github.com/ethereum/go-ethereum/params" | ||
|
|
@@ -33,16 +32,11 @@ import ( | |
| // BlockValidator implements Validator. | ||
| type BlockValidator struct { | ||
| config *params.ChainConfig // Chain configuration options | ||
| bc *BlockChain // Canonical block chain | ||
| } | ||
|
|
||
| // NewBlockValidator returns a new block validator which is safe for re-use | ||
| func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain) *BlockValidator { | ||
| validator := &BlockValidator{ | ||
| config: config, | ||
| bc: blockchain, | ||
| } | ||
| return validator | ||
| func NewBlockValidator(config *params.ChainConfig) *BlockValidator { | ||
| return &BlockValidator{config: config} | ||
| } | ||
|
|
||
| // ValidateBody validates the given block's uncles and verifies the block | ||
|
|
@@ -53,17 +47,9 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { | |
| if v.config.IsOsaka(block.Number(), block.Time()) && block.Size() > params.MaxBlockSize { | ||
| return ErrBlockOversized | ||
| } | ||
| // Check whether the block is already imported. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The checks that we have moved from here are only needed if you have state and are safe guards to check if we should indeed insert this block into the chain. It has been moved into The checks that remain in this method are essentially checking the consistency between the block body and the header (pre-execution) |
||
| if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) { | ||
| return ErrKnownBlock | ||
| } | ||
|
|
||
| // Header validity is known at this point. Here we verify that uncles, transactions | ||
| // and withdrawals given in the block body match the header. | ||
| header := block.Header() | ||
| if err := v.bc.engine.VerifyUncles(v.bc, block); err != nil { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check is still required even after the Merge, ensuring the uncle list is empty. I believe it's also required in stateless mode. |
||
| return err | ||
| } | ||
| if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { | ||
| return fmt.Errorf("uncle root hash mismatch (header value %x, calculated %x)", header.UncleHash, hash) | ||
| } | ||
|
|
@@ -110,20 +96,12 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { | |
| return errors.New("data blobs present in block body") | ||
| } | ||
| } | ||
|
|
||
| // Ancestor block must be known. | ||
| if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { | ||
| if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) { | ||
| return consensus.ErrUnknownAncestor | ||
| } | ||
| return consensus.ErrPrunedAncestor | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // ValidateState validates the various changes that happen after a state transition, | ||
| // such as amount of used gas, the receipt roots and the state root itself. | ||
| func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, res *ProcessResult, stateless bool) error { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We no longer need to return early when in stateless mode because stateless mode does the same checks as stateful mode |
||
| func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, res *ProcessResult) error { | ||
| if res == nil { | ||
| return errors.New("nil ProcessResult value") | ||
| } | ||
|
|
@@ -141,11 +119,6 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD | |
| if rbloom != header.Bloom { | ||
| return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) | ||
| } | ||
| // In stateless mode, return early because the receipt and state root are not | ||
| // provided through the witness, rather the cross validator needs to return it. | ||
| if stateless { | ||
| return nil | ||
| } | ||
|
Comment on lines
-144
to
-148
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is designed for another use case and is still needed. I agree that, in your case, it should be called, but then it has to be via another flag.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the original intention is that: the stateless mode produces all the hashes without the hints (such as the state root in block header) and these hashes are explicitly checked outside the stateless execution. But I feel Kev is right here. The input for stateless execution is: (a) the block (b) the state (c) the state proofs.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. func ExecuteStateless(ctx context.Context, config *params.ChainConfig, vmconfig vm.Config, block *types.Block, witness *stateless.Witness) error {
// Create and populate the state database to serve as the stateless backend
memdb := witness.MakeHashDB()
db, err := state.New(witness.Root(), state.NewDatabase(triedb.NewDatabase(memdb, triedb.HashDefaults), nil))
if err != nil {
return err
}The pre-execution root is derived from the witness. I am not 100% sure it's correct. Ideally it should be the state root of the parent referred by the block.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yep this is my understanding, so we would:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't look at the pipeline for this part of this part of the code, ie whether
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rjl493456442 my point is that the other use case needs to be fixed before you delete this. Doing this would recompute the state root twice. You can't just delete that check.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you say what this other usecase is?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cross-validation |
||
| // The receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]])) | ||
| receiptSha := types.DeriveSha(res.Receipts, trie.NewStackTrie(nil)) | ||
| if receiptSha != header.ReceiptHash { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -423,7 +423,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine, | |
| return nil, err | ||
| } | ||
| bc.flushInterval.Store(int64(cfg.TrieTimeLimit)) | ||
| bc.validator = NewBlockValidator(chainConfig, bc) | ||
| bc.validator = NewBlockValidator(chainConfig) | ||
| bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc) | ||
| bc.processor = NewStateProcessor(bc.hc) | ||
|
|
||
|
|
@@ -1908,7 +1908,7 @@ func (bc *BlockChain) insertChain(ctx context.Context, chain types.Blocks, setHe | |
| defer close(abort) | ||
|
|
||
| // Peek the error for the first block to decide the directing import logic | ||
| it := newInsertIterator(chain, results, bc.validator) | ||
| it := newInsertIterator(chain, results, bc.validator, bc) | ||
| block, err := it.next() | ||
|
|
||
| // Left-trim all the known blocks that don't need to build snapshot | ||
|
|
@@ -2259,7 +2259,7 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, | |
|
|
||
| vstart := time.Now() | ||
| _, _, spanEnd = telemetry.StartSpan(ctx, "bc.validator.ValidateState") | ||
| err = bc.validator.ValidateState(block, statedb, res, false) | ||
| err = bc.validator.ValidateState(block, statedb, res) | ||
| spanEnd(&err) | ||
| if err != nil { | ||
| bc.reportBadBlock(block, res, err) | ||
|
|
@@ -2276,24 +2276,10 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, | |
| if witness := statedb.Witness(); witness != nil && config.StatelessSelfValidation { | ||
| log.Warn("Running stateless self-validation", "block", block.Number(), "hash", block.Hash()) | ||
|
|
||
| // Remove critical computed fields from the block to force true recalculation | ||
| context := block.Header() | ||
| context.Root = common.Hash{} | ||
| context.ReceiptHash = common.Hash{} | ||
|
|
||
| task := types.NewBlockWithHeader(context).WithBody(*block.Body()) | ||
|
|
||
|
Comment on lines
-2279
to
-2285
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no longer need these because stateless mode takes in the unmodified block. I think it was a bit awkward before because the code was executing a block that has fields in its header that were intentionally zeroed out. As noted above, this led to some parts of the code returning early when in stateless mode or just skipping important checks like ValidateBody |
||
| // Run the stateless self-cross-validation | ||
| crossStateRoot, crossReceiptRoot, err := ExecuteStateless(ctx, bc.chainConfig, bc.cfg.VmConfig, task, witness) | ||
| if err != nil { | ||
| if err := ExecuteStateless(ctx, bc.chainConfig, bc.cfg.VmConfig, block, witness); err != nil { | ||
| return nil, fmt.Errorf("stateless self-validation failed: %v", err) | ||
| } | ||
| if crossStateRoot != block.Root() { | ||
| return nil, fmt.Errorf("stateless self-validation root mismatch (cross: %x local: %x)", crossStateRoot, block.Root()) | ||
| } | ||
| if crossReceiptRoot != block.ReceiptHash() { | ||
| return nil, fmt.Errorf("stateless self-validation receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, block.ReceiptHash()) | ||
| } | ||
| } | ||
|
|
||
| var ( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ import ( | |
|
|
||
| "github.com/ethereum/go-ethereum/common" | ||
| "github.com/ethereum/go-ethereum/common/mclock" | ||
| "github.com/ethereum/go-ethereum/consensus" | ||
| "github.com/ethereum/go-ethereum/core/types" | ||
| "github.com/ethereum/go-ethereum/log" | ||
| ) | ||
|
|
@@ -95,19 +96,21 @@ type insertIterator struct { | |
| results <-chan error // Verification result sink from the consensus engine | ||
| errors []error // Header verification errors for the blocks | ||
|
|
||
| index int // Current offset of the iterator | ||
| validator Validator // Validator to run if verification succeeds | ||
| index int // Current offset of the iterator | ||
| validator Validator // Validator to run if verification succeeds | ||
| bc *BlockChain // Blockchain for admission checks | ||
| } | ||
|
|
||
| // newInsertIterator creates a new iterator based on the given blocks, which are | ||
| // assumed to be a contiguous chain. | ||
| func newInsertIterator(chain types.Blocks, results <-chan error, validator Validator) *insertIterator { | ||
| func newInsertIterator(chain types.Blocks, results <-chan error, validator Validator, bc *BlockChain) *insertIterator { | ||
| return &insertIterator{ | ||
| chain: chain, | ||
| results: results, | ||
| errors: make([]error, 0, len(chain)), | ||
| index: -1, | ||
| validator: validator, | ||
| bc: bc, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -127,8 +130,26 @@ func (it *insertIterator) next() (*types.Block, error) { | |
| if it.errors[it.index] != nil { | ||
| return it.chain[it.index], it.errors[it.index] | ||
| } | ||
| // Block header valid, run body validation and return | ||
| return it.chain[it.index], it.validator.ValidateBody(it.chain[it.index]) | ||
|
|
||
| block := it.chain[it.index] | ||
|
|
||
| // Skip blocks we've already imported and fully processed. | ||
| if it.bc.HasBlockAndState(block.Hash(), block.NumberU64()) { | ||
| return block, ErrKnownBlock | ||
| } | ||
| // Verify uncle blocks against chain history (pre-merge only) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please restore it back to Body validation, it's still required after the merge.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I think I am missing something, I thought that it would be covered by verifyHeader and validateBody. My thinking was that:
Opened a PR here to check
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we have some duplicated checks here, honestly. |
||
| if err := it.bc.engine.VerifyUncles(it.bc, block); err != nil { | ||
| return block, err | ||
| } | ||
| // Ensure the parent block is known and its state is available. | ||
| if !it.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { | ||
| if !it.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) { | ||
| return block, consensus.ErrUnknownAncestor | ||
| } | ||
| return block, consensus.ErrPrunedAncestor | ||
| } | ||
|
Comment on lines
+136
to
+150
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checks that were in ValidateBody have been moved here. These are very much tied to the blockchain instance and not related to verifying a block against its header fields |
||
| // Validate the block body against header | ||
| return block, it.validator.ValidateBody(block) | ||
| } | ||
|
|
||
| // previous returns the previous header that was being processed, or nil. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,56 +27,54 @@ import ( | |
| "github.com/ethereum/go-ethereum/core/stateless" | ||
| "github.com/ethereum/go-ethereum/core/types" | ||
| "github.com/ethereum/go-ethereum/core/vm" | ||
| "github.com/ethereum/go-ethereum/log" | ||
| "github.com/ethereum/go-ethereum/params" | ||
| "github.com/ethereum/go-ethereum/trie" | ||
| "github.com/ethereum/go-ethereum/triedb" | ||
| ) | ||
|
|
||
| // ExecuteStateless runs a stateless execution based on a witness, verifies | ||
| // everything it can locally and returns the state root and receipt root, that | ||
| // need the other side to explicitly check. | ||
| // ExecuteStateless runs a stateless execution based on a witness, fully | ||
| // validating the block including header, body, state root and receipt root. | ||
| // | ||
| // This method is a bit of a sore thumb here, but: | ||
| // - It cannot be placed in core/stateless, because state.New prodces a circular dep | ||
| // - It cannot be placed outside of core, because it needs to construct a dud headerchain | ||
| // | ||
| // TODO(karalabe): Would be nice to resolve both issues above somehow and move it. | ||
| func ExecuteStateless(ctx context.Context, config *params.ChainConfig, vmconfig vm.Config, block *types.Block, witness *stateless.Witness) (common.Hash, common.Hash, error) { | ||
| // Sanity check if the supplied block accidentally contains a set root or | ||
| // receipt hash. If so, be very loud, but still continue. | ||
| if block.Root() != (common.Hash{}) { | ||
| log.Error("stateless runner received state root it's expected to calculate (faulty consensus client)", "block", block.Number()) | ||
| } | ||
| if block.ReceiptHash() != (common.Hash{}) { | ||
| log.Error("stateless runner received receipt root it's expected to calculate (faulty consensus client)", "block", block.Number()) | ||
| } | ||
| func ExecuteStateless(ctx context.Context, config *params.ChainConfig, vmconfig vm.Config, block *types.Block, witness *stateless.Witness) error { | ||
| // Create and populate the state database to serve as the stateless backend | ||
| memdb := witness.MakeHashDB() | ||
| db, err := state.New(witness.Root(), state.NewDatabase(triedb.NewDatabase(memdb, triedb.HashDefaults), nil)) | ||
| if err != nil { | ||
| return common.Hash{}, common.Hash{}, err | ||
| return err | ||
| } | ||
|
|
||
| // Create a blockchain that is idle, but can be used to access headers through | ||
| engine := beacon.New(ethash.NewFaker()) | ||
| chain := &HeaderChain{ | ||
| config: config, | ||
| chainDb: memdb, | ||
| headerCache: lru.NewCache[common.Hash, *types.Header](256), | ||
| engine: beacon.New(ethash.NewFaker()), | ||
| engine: engine, | ||
| } | ||
| processor := NewStateProcessor(chain) | ||
| validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block | ||
| validator := NewBlockValidator(config) | ||
|
|
||
| // Pre-execution: Verify the block header against the parent header | ||
| if err := engine.VerifyHeader(chain, block.Header()); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Run the stateless blocks processing and self-validate certain fields | ||
| // Pre-execution: Verify the block body against the header | ||
| if err := validator.ValidateBody(block); err != nil { | ||
| return err | ||
| } | ||
|
Comment on lines
+67
to
+69
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. new check added, so we now completely validate the block |
||
|
|
||
| // Process the block by executing all transactions | ||
| res, err := processor.Process(ctx, block, db, vmconfig) | ||
| if err != nil { | ||
| return common.Hash{}, common.Hash{}, err | ||
| return err | ||
| } | ||
| if err = validator.ValidateState(block, db, res, true); err != nil { | ||
| return common.Hash{}, common.Hash{}, err | ||
| } | ||
| // Almost everything validated, but receipt and state root needs to be returned | ||
| receiptRoot := types.DeriveSha(res.Receipts, trie.NewStackTrie(nil)) | ||
| stateRoot := db.IntermediateRoot(config.IsEIP158(block.Number())) | ||
| return stateRoot, receiptRoot, nil | ||
|
Comment on lines
-78
to
-81
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We no longer return receiptRoot and stateRoot, however if the block was valid, we can just call methods on |
||
|
|
||
| // Post-execution: Validate gas, bloom, receipts, state root and | ||
| // other post execution artifacts | ||
| return validator.ValidateState(block, db, res) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1690,26 +1690,17 @@ func TestWitnessCreationAndConsumption(t *testing.T) { | |
| t.Fatalf("witness missing from payload") | ||
| } | ||
| // Test stateless execution of the created witness | ||
| wantStateRoot := envelope.ExecutionPayload.StateRoot | ||
| wantReceiptRoot := envelope.ExecutionPayload.ReceiptsRoot | ||
|
|
||
| envelope.ExecutionPayload.StateRoot = common.Hash{} | ||
| envelope.ExecutionPayload.ReceiptsRoot = common.Hash{} | ||
|
|
||
|
Comment on lines
-1696
to
-1698
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. main change, we no longer zero out the state root and receipt root |
||
| res, err := api.ExecuteStatelessPayloadV3(*envelope.ExecutionPayload, []common.Hash{}, &common.Hash{42}, *envelope.Witness) | ||
| if err != nil { | ||
| t.Fatalf("error executing stateless payload witness: %v", err) | ||
| } | ||
| if res.StateRoot != wantStateRoot { | ||
| t.Fatalf("stateless state root mismatch: have %v, want %v", res.StateRoot, wantStateRoot) | ||
| if res.StateRoot != envelope.ExecutionPayload.StateRoot { | ||
| t.Fatalf("stateless state root mismatch: have %v, want %v", res.StateRoot, envelope.ExecutionPayload.StateRoot) | ||
| } | ||
| if res.ReceiptsRoot != wantReceiptRoot { | ||
| t.Fatalf("stateless receipt root mismatch: have %v, want %v", res.ReceiptsRoot, wantReceiptRoot) | ||
| if res.ReceiptsRoot != envelope.ExecutionPayload.ReceiptsRoot { | ||
| t.Fatalf("stateless receipt root mismatch: have %v, want %v", res.ReceiptsRoot, envelope.ExecutionPayload.ReceiptsRoot) | ||
| } | ||
| // Test block insertion with witness creation | ||
| envelope.ExecutionPayload.StateRoot = wantStateRoot | ||
| envelope.ExecutionPayload.ReceiptsRoot = wantReceiptRoot | ||
|
|
||
| res2, err := api.NewPayloadWithWitnessV3(context.Background(), *envelope.ExecutionPayload, []common.Hash{}, &common.Hash{42}) | ||
| if err != nil { | ||
| t.Fatalf("error executing stateless payload witness: %v", err) | ||
|
|
@@ -1718,21 +1709,15 @@ func TestWitnessCreationAndConsumption(t *testing.T) { | |
| t.Fatalf("witness missing from payload") | ||
| } | ||
| // Test stateless execution of the created witness | ||
| wantStateRoot = envelope.ExecutionPayload.StateRoot | ||
| wantReceiptRoot = envelope.ExecutionPayload.ReceiptsRoot | ||
|
|
||
| envelope.ExecutionPayload.StateRoot = common.Hash{} | ||
| envelope.ExecutionPayload.ReceiptsRoot = common.Hash{} | ||
|
|
||
| res, err = api.ExecuteStatelessPayloadV3(*envelope.ExecutionPayload, []common.Hash{}, &common.Hash{42}, *res2.Witness) | ||
| if err != nil { | ||
| t.Fatalf("error executing stateless payload witness: %v", err) | ||
| } | ||
| if res.StateRoot != wantStateRoot { | ||
| t.Fatalf("stateless state root mismatch: have %v, want %v", res.StateRoot, wantStateRoot) | ||
| if res.StateRoot != envelope.ExecutionPayload.StateRoot { | ||
| t.Fatalf("stateless state root mismatch: have %v, want %v", res.StateRoot, envelope.ExecutionPayload.StateRoot) | ||
| } | ||
| if res.ReceiptsRoot != wantReceiptRoot { | ||
| t.Fatalf("stateless receipt root mismatch: have %v, want %v", res.ReceiptsRoot, wantReceiptRoot) | ||
| if res.ReceiptsRoot != envelope.ExecutionPayload.ReceiptsRoot { | ||
| t.Fatalf("stateless receipt root mismatch: have %v, want %v", res.ReceiptsRoot, envelope.ExecutionPayload.ReceiptsRoot) | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -284,11 +284,10 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v | |
| api.lastNewPayloadUpdate.Store(time.Now().Unix()) | ||
|
|
||
| log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash) | ||
| stateRoot, receiptRoot, err := core.ExecuteStateless(context.Background(), api.config(), vm.Config{}, block, witness) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if err != nil { | ||
| if err := core.ExecuteStateless(context.Background(), api.config(), vm.Config{}, block, witness); err != nil { | ||
| log.Warn("ExecuteStatelessPayload: execution failed", "err", err) | ||
| errorMsg := err.Error() | ||
| return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil | ||
| } | ||
| return engine.StatelessPayloadStatusV1{Status: engine.VALID, StateRoot: stateRoot, ReceiptsRoot: receiptRoot}, nil | ||
| return engine.StatelessPayloadStatusV1{Status: engine.VALID, StateRoot: block.Root(), ReceiptsRoot: block.ReceiptHash()}, nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Post state check (
ValidateState) is now done inside ofExecuteStateless