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

Exchanges integration: return invalid transactions as well, refactor status #2237

Merged
merged 25 commits into from
Aug 26, 2020

Conversation

andreibancioiu
Copy link
Collaborator

@andreibancioiu andreibancioiu commented Aug 25, 2020

  • refactor computation of transaction status (encapsulate in StatusComputer)

  • add more tests, refactor tests

  • include invalid transactions in response of /block?withTxs

  • deprecated field Status in MiniblockMetadata storage - will not be used anymore. Though, now, miniblock Type is required to compute status - thus, a breaking change for the blockAPI - but since we will implement replay & index rebuild, this is a non-issue.

  • added new field Type in MiniblockMetadata storage (see above)

  • also return miniblock hash in transaction response, where possible

How to test:

  • send both valid and invalid transactions
  • inspect transactions by hash (valid, invalid, rewards, possibly SCs as well)
  • inspect hyperblock wrt. to hyperblockNonce from step above
  • assert that invalid conditions are included in hyperblocks
  • assert that miniblock type is correctly set on get transaction responses
  • assert that miniblock hash, source shard, destination shard are present on get transaction responses

Get transaction route: http://proxy:8080/transaction/abracadabra

Get hyperblock route: http://proxy:8080/hyperblock/by-nonce/42

@andreibancioiu andreibancioiu self-assigned this Aug 25, 2020
@andreibancioiu andreibancioiu marked this pull request as ready for review August 25, 2020 12:32
// TxStatusExecuted = received and executed
TxStatusExecuted TxStatus = "executed"
// TxStatusNotExecuted = received and executed with error
// Question for review: not executed means "executed with error", correct?
Copy link
Contributor

Choose a reason for hiding this comment

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

yes for transaction that are indexed in elasticseach 'not executed' is status for transaction executed with error.


// ComputeStatusWhenInStorageKnowingMiniblock computes the transaction status for a historical transaction
func (params *StatusComputer) ComputeStatusWhenInStorageKnowingMiniblock() TxStatus {
if params.MiniblockType == block.InvalidBlock {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe you can add a method isMiniblockInvalid

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added.

tx.MiniBlockType = miniblock.Type
tx.MiniBlockHash = hex.EncodeToString([]byte(miniblockHash))

tx.Status = (&transaction.StatusComputer{
Copy link
Contributor

Choose a reason for hiding this comment

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

nice approach

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks!

node/errors.go Outdated
// ErrTransactionNotFound signals that a transaction was not found
var ErrTransactionNotFound = errors.New("transaction not found")

// ErrCannotRetrieveTransaction signals that a transaction was not found
Copy link
Contributor

Choose a reason for hiding this comment

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

update comment

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

}

// merge data about transaction from history storer and transaction storer
tx = putHistoryFieldsInTransaction(tx, miniblockMetadata)
putHistoryFieldsInTransaction(tx, miniblockMetadata)
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe you can add this method inside structure ApiTransactionResult
func (ats *ApiTransactionResult) PutHistoryFieldsInTransaction(miniblockMetadata *fullHistory.MiniblockMetadata) just a suggestion

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That is a good suggestion, indeed, to encapsulate this logic in ApiTransactionResult. The issue is, though, that ApiTransactionResult is defined in transaction package, while miniblockMetadata in fullHistory package - and that would require an additional interface - we can do in the future.

// TxStatusExecuted = received and executed
TxStatusExecuted TxStatus = "executed"
// TxStatusNotExecuted = received and executed with error
// Question for review: not executed means "executed with error", correct?
Copy link
Contributor

Choose a reason for hiding this comment

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

not-executed is confusing. Not-executed would be those transaction which really do not get included in block (lower nonce for example - user sending two transactions with same nonce and only 1 getting executed).
Than we have the scenario when the transaction's status in invalid - in that case the gas was consumed from the account, and the nonce increased.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Currently, also Explorer displays not executed for executed transactions (but, say, with user error). Do we keep it like this for the moment or do we change this?

Copy link
Contributor

Choose a reason for hiding this comment

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

we should change that as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Only now I see that TxStatusNotExecuted is only used in the context of the transactions simulator:

node/txsimulator/txSimulator.go, line 63

And seems to be used well there.

The other occurrence, completely unrelated, is in the indexer component (which, actually, defines a separate set of statuses): txStatusNotExecuted = "Not Executed".

I don't want to change the indexer status in this PR (a bit risky), but we can do that in another PR.

Ok?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok. that is in another PR changing in the indexer.


// ComputeStatusWhenInPool computes the transaction status when transaction is in pool
func (params *StatusComputer) ComputeStatusWhenInPool() TxStatus {
if params.isContractDeploy() {
Copy link
Contributor

Choose a reason for hiding this comment

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

why is this check needed ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

params.isDestinationMe() && params.isCrossShard() would be evaluated to true for deployments, but still we don't want to return TxStatusPartiallyExecuted in that case (seen in previous PR with the status for deployments).

Does it make sense?

Fixed (moved condition).

Copy link
Contributor

Choose a reason for hiding this comment

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

isCrossShard should return false in case of deployment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Currently, isCrossShard() is implemented as follows:

func (params *StatusComputer) isCrossShard() bool {
	return params.SourceShard != params.DestinationShard
}

While ComputeStatusWhenInPool() is fed the parameters as follows - in a generic manner, not knowing anything special about the Zero address:

SourceShard:      n.shardCoordinator.ComputeId(tx.Tx.GetSndAddr()),
DestinationShard: n.shardCoordinator.ComputeId(tx.Tx.GetRcvAddr()),
Receiver:         tx.Tx.GetRcvAddr(),
TransactionData:  tx.Data,
SelfShard:        n.shardCoordinator.SelfId(),

Thus, the isContractDeploy() has to be checked first (because the check isDestinationMe() && isCrossShard() will be fooled otherwise).

We could change isCrossShard() to call isContractDeploy(), but perhaps that would be a bit harder to reason about in the future.

Fixed to be more explicit, as before. Is it all right?

Copy link
Contributor

Choose a reason for hiding this comment

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

in this case shardCoordinator computeID should have returned selfID. for the empty address.

Copy link
Contributor

Choose a reason for hiding this comment

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

in computestatuswheninPool you might use another function a wrapper on top of computeID where you add the empty check.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Let's keep compute ID completely out of the statusComputer, please.

Previous location, previous PR (reviewed):
https://github.com/ElrondNetwork/elrond-go/pull/2234/files

Copy link
Contributor

Choose a reason for hiding this comment

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

Sometimes it happens that these things get approved by a PR and the next one requests changes in the same zone. It would be better to have correct shardId computation at the base. In this case shard coordinator, or computestatus

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Left as it is, as discussed separately.

// Question for review: here we cannot know if the transaction is, actually, "invalid".
// However, when "fullHistory" indexing is enabled, this function is not used.

if params.isDestinationMe() || params.isContractDeploy() {
Copy link
Contributor

Choose a reason for hiding this comment

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

we should attach the receipt / smart contract result to the transaction - as we have in elastic search - than it it simple to decide the status of the transaction. It is not a trivial task, this can stay as it is.

Copy link
Contributor

Choose a reason for hiding this comment

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

delete the comments question for review

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed comment, moved above function.

case block.SmartContractResultBlock:
return bap.getTxsFromMiniblock(miniBlock, epoch, "unsigned", dataRetriever.UnsignedTransactionUnit)
return bap.getTxsFromMiniblock(miniBlock, miniblockHash, epoch, "unsigned", dataRetriever.UnsignedTransactionUnit)
Copy link
Contributor

Choose a reason for hiding this comment

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

make some constants for magic strings

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

// TxStatusExecuted = received and executed
TxStatusExecuted TxStatus = "executed"
// TxStatusNotExecuted = received and executed with error
// Question for review: not executed means "executed with error", correct?
Copy link
Contributor

Choose a reason for hiding this comment

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

we should change that as well.

// Question for review: here we cannot know if the transaction is, actually, "invalid".
// However, when "fullHistory" indexing is enabled, this function is not used.

if params.isDestinationMe() || params.isContractDeploy() {
Copy link
Contributor

Choose a reason for hiding this comment

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

delete the comments question for review

node/nodeTransactions.go Outdated Show resolved Hide resolved
node/nodeTransactions.go Outdated Show resolved Hide resolved
node/nodeTransactions.go Show resolved Hide resolved

// ComputeStatusWhenInPool computes the transaction status when transaction is in pool
func (params *StatusComputer) ComputeStatusWhenInPool() TxStatus {
if params.isContractDeploy() {
Copy link
Contributor

Choose a reason for hiding this comment

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

in computestatuswheninPool you might use another function a wrapper on top of computeID where you add the empty check.

node/errors.go Outdated
@@ -195,3 +190,16 @@ var ErrNilTxSimulatorProcessor = errors.New("nil transaction simulator processor

// ErrNilIntermediateProcessorContainer signals that intermediate processors container is nil
var ErrNilIntermediateProcessorContainer = errors.New("intermediate processor container is nil")

// NewErrTransactionNotFound creates an error which signals that a transaction was not found
func NewErrTransactionNotFound(originalErr error) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

do we really need these ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Let's have them, please.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

}

txBytes, txType, found := n.getTxBytesFromStorageByEpoch(hash, miniblockMetadata.Epoch)
if !found {
log.Warn("getFullHistoryTransaction(): unexpected condition, cannot find transaction in storage")
return nil, ErrCannotRetrieveTransaction
return nil, NewErrCannotRetrieveTransaction(err)
Copy link
Contributor

Choose a reason for hiding this comment

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

error wrapping can happen directly here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Let's use the wrappers defined in errors.go, please (a bit cleaner).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

AdoAdoAdo
AdoAdoAdo previously approved these changes Aug 26, 2020
miiu96
miiu96 previously approved these changes Aug 26, 2020
@andreibancioiu andreibancioiu dismissed stale reviews from miiu96 and AdoAdoAdo via 8bb656c August 26, 2020 14:31
miiu96
miiu96 previously approved these changes Aug 26, 2020
Copy link
Contributor

@LucianMincu LucianMincu left a comment

Choose a reason for hiding this comment

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

System tests passed.

@LucianMincu LucianMincu merged commit c059598 into development Aug 26, 2020
@LucianMincu LucianMincu deleted the exchanges-integration branch August 26, 2020 16:14
@andreibancioiu andreibancioiu restored the exchanges-integration branch August 26, 2020 21:08
@andreibancioiu andreibancioiu deleted the exchanges-integration branch August 28, 2020 12:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants