Skip to content
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

Block coordinates on vm query #5447

Merged

Conversation

sstanculeanu
Copy link
Contributor

@sstanculeanu sstanculeanu commented Jul 21, 2023

Reasoning behind the pull request

  • add block coordinates on vm query

Proposed changes

  • added block coordinates option on vm query

Testing procedure

  • will be tested on feat branch

Pre-requisites

Based on the Contributing Guidelines the PR author and the reviewers must check the following requirements are met:

  • was the PR targeted to the correct branch?
  • if this is a larger feature that probably needs more than one PR, is there a feat branch created?
  • if this is a feat branch merging, do all satellite projects have a proper tag inside go.mod?

@codecov
Copy link

codecov bot commented Jul 21, 2023

Codecov Report

❗ No coverage uploaded for pull request base (feat/vmquery_with_block_coordinates@7c5d69e). Click here to learn what that means.
Patch has no changes to coverable lines.

❗ Current head 4743403 differs from pull request most recent head 342c99b. Consider uploading reports for the commit 342c99b to get more accurate results

Additional details and impacted files
@@                          Coverage Diff                           @@
##             feat/vmquery_with_block_coordinates    #5447   +/-   ##
======================================================================
  Coverage                                       ?   80.07%           
======================================================================
  Files                                          ?      706           
  Lines                                          ?    93619           
  Branches                                       ?        0           
======================================================================
  Hits                                           ?    74965           
  Misses                                         ?    13303           
  Partials                                       ?     5351           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@sstanculeanu sstanculeanu marked this pull request as ready for review July 26, 2023 08:26
@AdoAdoAdo AdoAdoAdo self-requested a review July 26, 2023 08:56
@bogdan-rosianu bogdan-rosianu self-requested a review July 26, 2023 10:15
Copy link
Contributor

@bogdan-rosianu bogdan-rosianu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small comments. tested it, works well 👍

api/groups/vmValuesGroup.go Outdated Show resolved Hide resolved
dataRetriever/blockchain/apiBlockchain.go Outdated Show resolved Hide resolved

// GetCurrentBlockRootHash returns the current block root hash from the main chain handler, if no local root hash is set
// if there is a local root hash, it will be returned until its reset
func (abc *apiBlockchain) GetCurrentBlockRootHash() []byte {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in a normal VM Query, is SetCurrentBlockHeaderAndRootHash called each time? Otherwise, a gateway user might send a request for a block from yesterday and the next request (from another user, without coordinates) will use the root hash from yesterday instead of the current one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tried to cover all the cases when executeScCall would early exit, in order to properly reset the root hash and avoid this situation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would it need to reset at exit?

I would instead, from the API call prepare the apiBlockchain with either the main blockchain current data, or with the preferred blockchain data given by the developer, and then do the sc query/etc.

In that case there should be no edge cases that need to be taken care of, and will be more straight forward as you will always set the block coordinates you are working with on each of the calls.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented the suggestion

return process.ErrNilHistoryRepository
}
if check.IfNil(arg.ScheduledTxsExecutionHandler) {
return process.ErrNilScheduledTxsExecutionHandler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure, but I think that for genesis, a disabled scheduled txs execution handler would have been enough

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly, I do not see the reason for injecting this on the genesis processor

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

process/errors.go Outdated Show resolved Hide resolved
api/groups/vmValuesGroup.go Show resolved Hide resolved
pcf.txLogsProcessor = txLogsProcessor
genesisBlocks, initialTxs, err := pcf.generateGenesisHeadersAndApplyInitialBalances()
genesisBlocks, initialTxs, err := pcf.generateGenesisHeadersAndApplyInitialBalances(scheduledTxsExecutionHandler)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change? Why do we need to inject the scheduledTxsExecutionHandler as a parameter?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, I don't see where this will be needed for the VM query

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed it

return process.ErrNilHistoryRepository
}
if check.IfNil(arg.ScheduledTxsExecutionHandler) {
return process.ErrNilScheduledTxsExecutionHandler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly, I do not see the reason for injecting this on the genesis processor

}

return smartContract.NewSCQueryService(argsNewSCQueryService)
}

func createNewAccountsAdapterApi(args *scQueryElementArgs, chainHandler data.ChainHandler) (state.AccountsAdapterAPI, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we will create a new trie for each SC query service instance.
I think we have this, in one form or another, when we create the AccountsDBApi instance. We just need to create more of those instances and pass them to each SC query service element.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AccountsDBApi is indeed called below, what we were missing in the original create accounts adapter was a separate merkle trie instance for each of the sc query instances, and a separate chain handler.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need more AccountsDBApi instances as it is easier to manage the inner trie instances that way. AccountsDB is just a wrapper over the trie.

query = prepareScQuery(query)
vmInput := service.createVMCallInput(query, gasPrice)
vmOutput, err := vm.RunSmartContractCall(vmInput)
service.wasmVMChangeLocker.RUnlock()
if err != nil {
// Cleaning the current root hash so the real one would be returned further
_ = service.blockChain.SetCurrentBlockHeaderAndRootHash(nil, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can call this on a defer function after L207


// GetCurrentBlockRootHash returns the current block root hash from the main chain handler, if no local root hash is set
// if there is a local root hash, it will be returned until its reset
func (abc *apiBlockchain) GetCurrentBlockRootHash() []byte {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would it need to reset at exit?

I would instead, from the API call prepare the apiBlockchain with either the main blockchain current data, or with the preferred blockchain data given by the developer, and then do the sc query/etc.

In that case there should be no edge cases that need to be taken care of, and will be more straight forward as you will always set the block coordinates you are working with on each of the calls.

index: 0,
processingMode: args.processingMode,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is used for ArgsAccountsDB

}

return smartContract.NewSCQueryService(argsNewSCQueryService)
}

func createNewAccountsAdapterApi(args *scQueryElementArgs, chainHandler data.ChainHandler) (state.AccountsAdapterAPI, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AccountsDBApi is indeed called below, what we were missing in the original create accounts adapter was a separate merkle trie instance for each of the sc query instances, and a separate chain handler.

pcf.txLogsProcessor = txLogsProcessor
genesisBlocks, initialTxs, err := pcf.generateGenesisHeadersAndApplyInitialBalances()
genesisBlocks, initialTxs, err := pcf.generateGenesisHeadersAndApplyInitialBalances(scheduledTxsExecutionHandler)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, I don't see where this will be needed for the VM query

}

func (service *SCQueryService) getBlockRootHash(headerHash []byte, header data.HeaderHandler) []byte {
blockRootHash, err := service.scheduledTxsExecutionHandler.GetScheduledRootHashForHeaderWithEpoch(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I see why you added the scheduled execution handler, but you can use instead getBlockHeaderByNonce and request the header.Nonce+1 and check on that the scheduledRootHash.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can discuss as well, I see that this is similar to how it is done for on nodeBlocks.go
This code appears duplicated, maybe it would be better to extract a component and reuse it in both places instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a TODO for the duplicated code

api/groups/vmValuesGroup_test.go Show resolved Hide resolved
factory/api/apiResolverFactory.go Outdated Show resolved Hide resolved
process/smartContract/scQueryService.go Outdated Show resolved Hide resolved
StorageService: arg.Data.StorageService(),
Marshaller: arg.Core.InternalMarshalizer(),
Hasher: arg.Core.Hasher(),
Uint64ByteSliceConverter: arg.Core.Uint64ByteSliceConverter(),
}
queryService, err := smartContract.NewSCQueryService(argsNewSCQueryService)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really necessary to have a real sc query service on the genesis block creator? @iulianpascalau
I see we execute the genesis node staking transactions and then we use the sc query system to verify that the node is indeed staked via sc query.
Also for the DNS contracts we use it to check the version.

Maybe we should discuss if we really need this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the time it was the easiest way to verify that the genesis was constructed correctly. As seen, during testing, without these checks we can have a malformed genesis state if we got some genesis parameters wrong (like the initial number of staked nodes). I strongly encourage to keep them as they are now in order to safeguard the genesis state creation.

process/interface.go Show resolved Hide resolved
}

header := service.getBlockHeader(blockHeader)
return header, header.GetRootHash(), nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that you extract the roothash from the following block after the 'block by hash'

I think the header to return is exactly the block by hash, but the rootHash to return here should be the ScheduledRootHash from the following block after the block by hash

return blockHeader, header.GetScheduledRootHash, nil

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

}

func (service *SCQueryService) getBlockHeader(currentHeader data.HeaderHandler) data.HeaderHandler {
header, _, err := service.getBlockHeaderByNonce(currentHeader.GetNonce() + 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why nonce +1 here?
Maybe rename the caller method to getNextBlockHeader?

also I don't think we need this method, as we already have getBlockByNonce (current header nonce +1), and an error on this get should not use the current header, but propagate the error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the method and updated as suggested

}

func (service *SCQueryService) checkForRootHashChanges(rootHashBefore []byte) error {
rootHashAfter := service.blockChain.GetCurrentBlockRootHash()
rootHashAfter := service.mainBlockChain.GetCurrentBlockRootHash()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it verify this against the api blockchain block roothash?

I would only use the mainBlockChain to set the apiBlockChain data when the API is called without block parameters (so for current)

return ErrOperationNotPermitted
// RecreateTrie is used to reload the trie based on an existing rootHash
func (accountsDB *accountsDBApi) RecreateTrie(rootHash []byte) error {
_, err := accountsDB.doRecreateTrieWithBlockInfo(holders.NewBlockInfo([]byte{}, 0, rootHash))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you afterwards use the blockInfo set on the accountsDBApi?
if yes, the maybe use instead

accountsDB.innerAccountsAdapter.RecreateTrie(rootHash)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reused doRecreateTrieWithBlockInfo method because the resulted blockInfo is then returned on GetAccountWithBlockInfo and RootHash

@sstanculeanu sstanculeanu changed the base branch from feat/block_coordinates_on_vmquery to feat/vmquery_with_block_coordinates August 16, 2023 10:41
bogdan-rosianu
bogdan-rosianu previously approved these changes Aug 16, 2023
if err != nil {
return nil, nil, err
}

header := service.getBlockHeader(blockHeader)
return header, header.GetRootHash(), nil
blockHeader, _, err := service.getBlockHeaderByNonce(currentHeader.GetNonce() + 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks similar to lines 268-278, can you maybe extract a method (getRootHashForBlock)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pushed

@sstanculeanu sstanculeanu merged commit 59ebb58 into feat/vmquery_with_block_coordinates Aug 22, 2023
5 checks passed
@sstanculeanu sstanculeanu deleted the block_coordinates_on_vm_query branch August 22, 2023 10:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants