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

[Utility] Refactor and rename some existing functions related to handling transactions #771

Merged
merged 35 commits into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
334b8b9
Split anteHandleMessage into multiple messages
Olshansk May 18, 2023
8ea77eb
s/u.anteHandleMessage/u.basicValidateTransaction
Olshansk May 18, 2023
820b18c
Rename processProposalBlockTransactions
Olshansk May 18, 2023
80c8e48
s/HydrateIdxTx/HandleTransaction
Olshansk May 18, 2023
41f4d0f
WIP commit
Olshansk May 18, 2023
784ee24
Added GetIndexedTransaction
Olshansk May 18, 2023
e37daf2
Updated txProtoBytesToRPCIdxTxs to use txProtoBytesToRPCIdxTxs
Olshansk May 18, 2023
7aa9231
Update changelogs
Olshansk May 19, 2023
97d8398
Merge branch 'main' into minor_tx_processing_improvement
Olshansk May 22, 2023
d1ded6f
Attempt with mocking lots of persistence code
Olshansk May 22, 2023
a6d04a9
WIP
Olshansk May 23, 2023
e1f26d7
Added tests for TestGetIndexedTransaction
Olshansk May 23, 2023
bb643d2
Merge branch 'main' into minor_tx_processing_improvement
Olshansk May 23, 2023
6a4330a
Implemented TestHandleTransaction_BasicValidation
Olshansk May 24, 2023
eda7427
Update comments
Olshansk May 24, 2023
5886791
Merge with main
Olshansk May 30, 2023
3f01cae
Reply to comments
Olshansk May 30, 2023
42c384a
Merge with main
Olshansk May 30, 2023
a4338c7
Merge with main
Olshansk May 30, 2023
98649e1
Updated persistence changelog
Olshansk May 30, 2023
3abfd24
Broken test
Olshansk May 30, 2023
45b9517
Comment out a single test
Olshansk May 30, 2023
567cafb
Merge branch 'main' into minor_tx_processing_improvement
Olshansk May 31, 2023
ace8cd6
Merge with main
Olshansk Jun 1, 2023
d388caf
Update changelogs
Olshansk Jun 1, 2023
2f23222
Merge with main
Olshansk Jun 1, 2023
1c47870
Reply to Arash's comments
Olshansk Jun 1, 2023
9d3743a
Fix the error we check for
Olshansk Jun 1, 2023
e19c62d
Change persistence changelog
Olshansk Jun 1, 2023
2fe483a
Tend to a couple linter errors
Olshansk Jun 1, 2023
a68a4f9
Merge with main
Olshansk Jun 1, 2023
7e46bb6
Revert the p2p error
Olshansk Jun 1, 2023
3ea9936
Empty commit
Olshansk Jun 2, 2023
f0ec266
Merge branch 'main' into minor_tx_processing_improvement
Olshansk Jun 2, 2023
12104a8
Update changelogs
Olshansk Jun 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions persistence/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ func (p *persistenceModule) TransactionExists(transactionHash string) (bool, err
res, err := p.txIndexer.GetByHash(hash)
if res == nil {
// check for not found
if err != nil && err.Error() == kvstore.BadgerKeyNotFoundError {
if err != nil && err.Error() == kvstore.KeyNotFoundError {
return false, nil
}
return false, err
}
return true, err
return true, nil
}

func (p *PostgresContext) GetMinimumBlockHeight() (latestHeight uint64, err error) {
Expand Down
5 changes: 5 additions & 0 deletions persistence/docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.0.56] - 2023-05-31

- Renamed an error used by Badger / KVStore
- Improve comments used for mock generation

## [0.0.0.55] - 2023-06-01

- Integrate lazy loading SMT (release v0.5.0) into V1
Expand Down
6 changes: 2 additions & 4 deletions persistence/indexer/indexer.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// TECHDEBT(andrew): Move this out of shared and alongside the mempool.

package indexer

//go:generate mockgen -package=mock_types -destination=../types/mocks/indexer_mock.go github.com/pokt-network/pocket/persistence/indexer TxIndexer

import (
"encoding/hex"
"fmt"
Expand All @@ -11,8 +11,6 @@ import (
coreTypes "github.com/pokt-network/pocket/shared/core/types"
)

// Interface

// `TxIndexer` interface defines methods to index and query transactions.
type TxIndexer interface {
// `Index` analyzes, indexes and stores a single transaction result.
Expand Down
2 changes: 1 addition & 1 deletion persistence/kvstore/kvstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type KVStore interface {
}

const (
BadgerKeyNotFoundError = "Key not found"
KeyNotFoundError = "Key not found"
)

var (
Expand Down
5 changes: 5 additions & 0 deletions rpc/doc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.0.20] - 2023-05-30

- Add a few clarifying comments
- Use `utilityModule.GetIndexedTransaction` instead of `utilityUnitOfWork.HydrateTxId` which only retrieves indexed transactions instead of applying the underlying business logic

## [0.0.0.19] - 2023-05-24

- Updates rpc handlers to use updated BlockStore interface
Expand Down
2 changes: 2 additions & 0 deletions rpc/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ func (s *rpcServer) PostV1ClientBroadcastTxSync(ctx echo.Context) error {
return ctx.String(http.StatusBadRequest, "cannot decode tx bytes")
}

// Validate the transaction and add it to the mempool
if err := s.GetBus().GetUtilityModule().HandleTransaction(txBz); err != nil {
return ctx.String(http.StatusInternalServerError, err.Error())
}

// Broadcast the transaction to the rest of the network if it passed the basic validation above
if err := s.broadcastMessage(txBz); err != nil {
return ctx.String(http.StatusInternalServerError, err.Error())
}
Expand Down
12 changes: 3 additions & 9 deletions rpc/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,21 +360,15 @@ func (s *rpcServer) calculateMessageFeeForActor(actorType coreTypes.ActorType, m
}

// txProtoBytesToRPCIdxTxs converts a slice of serialised Transaction protobufs to a slice of RPC IdxTxs
// ADDTEST: Add an E2E test (using the gherkin suite) that leverages this codepath
func (s *rpcServer) txProtoBytesToRPCIdxTxs(txProtoBytes [][]byte) ([]IndexedTransaction, error) {
currentHeight := s.GetBus().GetConsensusModule().CurrentHeight()
uow, err := s.GetBus().GetUtilityModule().NewUnitOfWork(int64(currentHeight))
if err != nil {
return nil, err
}
defer uow.Release() //nolint:errcheck // We only need to make sure the UOW is released

txs := make([]IndexedTransaction, 0)
for idx, txBz := range txProtoBytes {
for _, txBz := range txProtoBytes {
tx := new(coreTypes.Transaction)
if err := codec.GetCodec().Unmarshal(txBz, tx); err != nil {
return nil, err
}
idxTx, er := uow.HydrateIdxTx(tx, idx)
idxTx, er := s.GetBus().GetUtilityModule().GetIndexedTransaction(txBz)
if er != nil {
return nil, er
}
Expand Down
20 changes: 19 additions & 1 deletion shared/core/types/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func NewError(code Code, msg string) Error {
}
}

// NextCode: 135
// NextCode: 138
type Code float64 // CONSIDERATION: Should these be a proto enum or a golang iota?

//nolint:gosec // G101 - Not hard-coded credentials
Expand All @@ -53,6 +53,7 @@ const (
CodeNewPublicKeyFromBytesError Code = 8
CodeNewAddressFromBytesError Code = 9
CodeSignatureVerificationFailedError Code = 10
CodeRetrievingSignableBytesError Code = 137
CodeHexDecodeFromStringError Code = 11
CodeInvalidHashLengthError Code = 12
CodeEmptyNetworkIDError Code = 13
Expand All @@ -74,6 +75,7 @@ const (
CodeEmptyNonceError Code = 30
CodeEmptyPublicKeyError Code = 31
CodeEmptySignatureError Code = 32
CodeEmptySignatureStructureError Code = 136
CodeDuplicateTransactionError Code = 35
CodeTransactionSignError Code = 36
CodeGetAllValidatorsError Code = 37
Expand Down Expand Up @@ -137,6 +139,7 @@ const (
CodeEmptyParamValueError Code = 93
CodeGetOutputAddressError Code = 94
CodeTransactionAlreadyCommittedError Code = 95
CodeTransactionNotCommittedError Code = 135
CodeInitGenesisParamsError Code = 96
CodeGetAllFishermenError Code = 97
CodeGetAllServicersError Code = 98
Expand Down Expand Up @@ -226,11 +229,13 @@ const (
InvalidNonceError = "the nonce field is invalid; cannot be converted to big.Int"
NewPublicKeyFromBytesError = "unable to convert the raw bytes to a valid public key"
SignatureVerificationFailedError = "the public key / signature combination is not valid for the msg"
RetrievingSignableBytesError = "error retrieving signable bytes"
ProtoFromAnyError = "an error occurred getting the structure from the protobuf any"
NewFeeFromStringError = "the fee string is unable to be converted to a valid base 10 number"
EmptyNonceError = "the nonce in the transaction is empty"
EmptyPublicKeyError = "the public key field is empty"
EmptySignatureError = "the signature field is empty"
EmptySignatureStructureError = "the signature structure is empty"
TransactionSignError = "an error occurred signing the transaction"
InterfaceConversionError = "an error occurred converting the interface to an expected type: "
SetStatusPausedBeforeError = "an error occurred setting the actor status that were paused before"
Expand All @@ -249,6 +254,7 @@ const (
GetOutputAddressError = "an error occurred getting the output address using operator"
GetHeightError = "an error occurred when getting the height from the store"
TransactionAlreadyCommittedError = "the transaction is already committed"
TransactionNotCommittedError = "the transaction is not committed"
NewSavePointError = "an error occurred creating the save point"
RollbackSavePointError = "an error occurred rolling back to save point"
NewPersistenceContextError = "an error occurred creating the persistence context"
Expand Down Expand Up @@ -596,10 +602,18 @@ func ErrEmptySignature() Error {
return NewError(CodeEmptySignatureError, EmptySignatureError)
}

func ErrEmptySignatureStructure() Error {
return NewError(CodeEmptySignatureStructureError, EmptySignatureStructureError)
}

func ErrSignatureVerificationFailed() Error {
return NewError(CodeSignatureVerificationFailedError, SignatureVerificationFailedError)
}

func ErrRetrievingSignableBytes(err error) Error {
return NewError(CodeRetrievingSignableBytesError, fmt.Sprintf("%s: %s", RetrievingSignableBytesError, err.Error()))
Copy link
Contributor

Choose a reason for hiding this comment

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

Wrapping errors in general makes more sense IMO (although I understand it would not be very consistent with current codebase)

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not a fan of error.go myself (didn't create it), but we're just staying consistent with this approach we work on #556 to standardize it.

}

func ErrDecodeMessage(err error) Error {
return NewError(CodeDecodeMessageError, fmt.Sprintf("%s: %s", DecodeMessageError, err.Error()))
}
Expand All @@ -612,6 +626,10 @@ func ErrTransactionAlreadyCommitted() Error {
return NewError(CodeTransactionAlreadyCommittedError, TransactionAlreadyCommittedError)
}

func ErrTransactionNotCommitted() Error {
return NewError(CodeTransactionNotCommittedError, TransactionNotCommittedError)
}

func ErrTransactionSign(err error) Error {
return NewError(CodeTransactionSignError, fmt.Sprintf("%s: %s", TransactionSignError, err.Error()))
}
Expand Down
6 changes: 2 additions & 4 deletions shared/core/types/signature.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package types

import "fmt"

func (s *Signature) ValidateBasic() error {
if s.Signature == nil {
return fmt.Errorf("signature cannot be empty")
return ErrEmptySignature()
}
if s.PublicKey == nil {
return fmt.Errorf("public key cannot be empty")
return ErrEmptyPublicKey()
}
return nil
}
13 changes: 6 additions & 7 deletions shared/core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package types

import (
"bytes"
"fmt"

"github.com/pokt-network/pocket/shared/codec"
"github.com/pokt-network/pocket/shared/crypto"
Expand Down Expand Up @@ -40,12 +39,12 @@ type ITransaction interface {
func (tx *Transaction) ValidateBasic() error {
// Nonce cannot be empty to avoid transaction replays
if tx.Nonce == "" {
return fmt.Errorf("nonce cannot be empty") // ErrEmptyNonce
return ErrEmptyNonce()
}

// Is there a signature we can verify?
if tx.Signature == nil {
return fmt.Errorf("signature cannot be empty") // ErrEmptySignature
return ErrEmptySignatureStructure()
}
if err := tx.Signature.ValidateBasic(); err != nil {
return err
Expand All @@ -54,21 +53,21 @@ func (tx *Transaction) ValidateBasic() error {
// Does the transaction have a valid key?
publicKey, err := crypto.NewPublicKeyFromBytes(tx.Signature.PublicKey)
if err != nil {
return err // ErrEmptyPublicKey or ErrNewPublicKeyFromBytes
return ErrNewPublicKeyFromBytes(err)
}

// Is there a valid msg that can be decoded?
if _, err := tx.GetMessage(); err != nil {
return err // ? ErrBadMessage
return ErrDecodeMessage(err)
}

signBytes, err := tx.SignableBytes()
if err != nil {
return err // ? ErrBadSignature
return ErrRetrievingSignableBytes(err)
}

if ok := publicKey.Verify(signBytes, tx.Signature.Signature); !ok {
return fmt.Errorf("signature verification failed") // ErrSignatureVerificationFailed
return ErrSignatureVerificationFailed()
}

return nil
Expand Down
8 changes: 7 additions & 1 deletion shared/modules/doc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.0.13] - 2023-05-30

- Added `GetIndexedTransaction` to the `UtilityModule` interface to be able to retrieve an indexed transaction without running the underlying business logic
- Renamed `HydrateIdxTx` to `HandleTransaction` in the `UtilityUnitOfWork`interface so its more descriptive of what the function does
- Renamed `anteHandleMessage` to `basicValidateTransaction`
- Split the logic in `basicValidateTransaction` into multiple smaller functions for readability and so adding new business logic will be clearer

## [0.0.0.12] - 2023-05-16

- Updates the PersistenceModule interface to return a BlockStore instead of KVStore directly
Expand All @@ -15,7 +22,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- `Consensus` - Updated debug interface functions: added `PushStateSyncMetadataResponse()`, removed `SetAggregatedStateSyncMetadata()` and `GetAggregatedStateSyncMetadataMaxHeight()`


## [0.0.0.10] - 2023-03-30

- `Consensus` - improved documentation for supporting interfaces
Expand Down
9 changes: 7 additions & 2 deletions shared/modules/utility_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ type UtilityModule interface {
NewUnitOfWork(height int64) (UtilityUnitOfWork, error)

// HandleTransaction does basic `Transaction` validation & adds it to the utility's module mempool if valid
// if it's valid. It does not process the business logic of the underlying message; see UtilityUnitOfWork.HandleTransaction.
HandleTransaction(tx []byte) error

// GetIndexedTransaction returns the indexed transaction if it is available in this node's view of the world state.
GetIndexedTransaction(tx []byte) (*coreTypes.IndexedTransaction, error)

// HandleRelay process the relay to the specified chain if this node is a servicer
HandleRelay(relay *coreTypes.Relay) (*coreTypes.RelayResponse, error) // TODO: Implement this

Expand Down Expand Up @@ -63,8 +67,9 @@ type UtilityUnitOfWork interface {
// TODO: Investigate a way to potentially simplify the interface by removing this function.
SetProposalBlock(blockHash string, proposerAddr []byte, txs [][]byte) error

// HydrateIdxTx hydrates a Transaction structure, with its index in the block returning a IndexedTransaction structure
HydrateIdxTx(tx *coreTypes.Transaction, index int) (*coreTypes.IndexedTransaction, coreTypes.Error)
// HandleTransaction validates the transaction, processes the business logic of the underlying message
// given the index in the current unit of work, and returns the resulting `IndexedTransaction` struct.
HandleTransaction(tx *coreTypes.Transaction, index int) (*coreTypes.IndexedTransaction, coreTypes.Error)

// ApplyBlock applies the context's in-memory proposed state (i.e. the txs in this context).
// Only intended to be used by the block verifiers (i.e. replicas).
Expand Down
8 changes: 7 additions & 1 deletion utility/doc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.0.42] - 2023-05-30

- Implemented `GetIndexedTransaction`
- Add some comments and rename a few functions throughout to have things be more self-explanatory
- Moved over all the business logic related to getting "signer candidates" into its own file for readability purposes

## [0.0.0.41] - 2023-05-30

- Added the E2E Feature Path Template to document, present and explain how to go about defining and scoping utility level features

## [0.0.0.40] - 2023-05-04

- Expose `HydrateIdxTx` for use outside of utility
- Expose `HandleTransaction` for use outside of utility
- Add placeholder functions `HandleRelay()` and `HandleChallenge()`
- Moved `utility/types/errors.go` into `shared/core/types` and updated their usage accordingly

Expand Down
25 changes: 25 additions & 0 deletions utility/transaction.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package utility

import (
"encoding/hex"

"github.com/pokt-network/pocket/persistence/kvstore"
"github.com/pokt-network/pocket/shared/codec"
coreTypes "github.com/pokt-network/pocket/shared/core/types"
)
Expand Down Expand Up @@ -35,3 +38,25 @@ func (u *utilityModule) HandleTransaction(txProtoBytes []byte) error {
// Store the tx in the mempool
return u.mempool.AddTx(txProtoBytes)
}

// GetIndexedTransaction implements the exposed functionality of the shared utilityModule interface.
func (u *utilityModule) GetIndexedTransaction(txProtoBytes []byte) (*coreTypes.IndexedTransaction, error) {
Olshansk marked this conversation as resolved.
Show resolved Hide resolved
h5law marked this conversation as resolved.
Show resolved Hide resolved
txHash := coreTypes.TxHash(txProtoBytes)
persistenceModule := u.GetBus().GetPersistenceModule()
Olshansk marked this conversation as resolved.
Show resolved Hide resolved

// TECHDEBT: Note the inconsistency between referencing tx hash as a string vs. byte slice in different places. Need to pick
// one and consolidate throughout the codebase
hash, err := hex.DecodeString(txHash)
if err != nil {
return nil, err
}
idTx, err := persistenceModule.GetTxIndexer().GetByHash(hash)
if err != nil {
if err.Error() == kvstore.KeyNotFoundError {
Olshansk marked this conversation as resolved.
Show resolved Hide resolved
return nil, coreTypes.ErrTransactionNotCommitted()
}
return nil, err
}

return idTx, nil
}
Loading