Skip to content

Commit

Permalink
POC for side transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
VAIBHAVJINDAL3012 committed Dec 18, 2023
1 parent 2e9f75e commit c4ef54b
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 59 deletions.
192 changes: 159 additions & 33 deletions simapp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package simapp
import (
"bytes"
"encoding/json"
"errors"
"fmt"

abci "github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/proto/tendermint/types"
tmTypes "github.com/cometbft/cometbft/types"

"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

/*
Expand All @@ -28,13 +29,16 @@ type (
//inside the voteEntension function
SideHandler func(ctx sdk.Context, tx sdk.Tx) (bool, error)

PostHandler func(ctx sdk.Context, msg sdk.Msg, res bool) error

// VoteExtensionHandler defines a dummy vote extension handler for SimApp.
//
// NOTE: This implementation is solely used for testing purposes. DO NOT use
// in a production application!
VoteExtensionHandler struct {
app *SimApp
modSideHandler map[string]SideHandler
modPostHandler map[string]PostHandler
}

// VoteExtension defines the structure used to create a dummy vote extension.
Expand All @@ -46,6 +50,20 @@ type (
}
)

// SideTxResponse store the vote on a particular side tx
// TODO:To keep it simple use the bool value in result
type SideTxResponse struct {
TxHash []byte `json:"tx_hash"`
Result bool `json:"result"`
}

// SideTxResult store the votes on a particular sideTx by
// all the validators
type SideTxResult struct {
TxHash []byte `json:"tx_hash"`
Validators []abci.Validator `json:"validators"`
}

const (
Vote_YES = iota
Vote_NO
Expand All @@ -64,14 +82,6 @@ func (v VoteExtensionHandler) SetSideHandler(modSideHandler map[string]SideHandl
// PrepareProposal -> Verify Prev. Block's VEs (check for 2/3+ majority ) -> Encode VEs in tx bytes
func (app *SimApp) NewPrepareProposalHandler() sdk.PrepareProposalHandler {
return func(ctx sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) {

// Log the event record stored in previous height
event, err := app.StakingKeeper.GetEventRecord(ctx)
if err != nil {
fmt.Println("Error occurred while fetching Event Record from DB: ", err)
}
fmt.Println("Event stored in height : ", ctx.BlockHeight()-1, "event ID: ", event.Id, "event block no.: ", event.BlockNumber)

// Validate VE sigs and check whether they have 2/3+ majority
hasTwoThirdsSigs := true
if err := app.StakingKeeper.ValidateVoteExtensions(ctx, ctx.BlockHeight(), ctx.ChainID(), req.LocalLastCommit.Votes, req.LocalLastCommit.Round); err != nil {
Expand All @@ -92,9 +102,7 @@ func (app *SimApp) NewPrepareProposalHandler() sdk.PrepareProposalHandler {
}
txs = append(txs, extVoteInfo)
} else {
//Appending the tx as an empty bytes so that first tx is always spl transaction.
var emptyBytes []byte
txs = append(txs, emptyBytes)
return nil, errors.New("Can't prepare the block without the more than 2/3 majority")
}

// encode the txs
Expand Down Expand Up @@ -140,24 +148,17 @@ func (app *SimApp) NewProcessProposalHandler() sdk.ProcessProposalHandler {

return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
}

}

// ExtendVote -> Fetch data from buffer -> Vote on it accordingly -> Store result.
func (h *VoteExtensionHandler) ExtendVote() sdk.ExtendVoteHandler {
return func(ctx sdk.Context, req *abci.RequestExtendVote) (*abci.ResponseExtendVote, error) {
fmt.Println("EXTENDING VOTE!", "height: ", ctx.BlockHeight())
// ve := VoteExtension{
// Hash: req.Hash,
// Height: req.Height,
// Result: Vote_YES,
// }

var bz []byte
var err error

//For simplicity only using the bool
txResults := make(map[string]bool)
txResults := make([]SideTxResponse, 0)
// txs are encoded after ExtendedVoteInfo
if len(req.Txs) > 1 {
txs := req.Txs[1:]
Expand All @@ -168,6 +169,8 @@ func (h *VoteExtensionHandler) ExtendVote() sdk.ExtendVoteHandler {
msgs := tx.GetMsgs()
msg := msgs[1]

//CHECK THE TX ALSO, IT MIGHT BE INVALID

fn, ok := h.modSideHandler[sdk.MsgTypeURL(msg)]
if ok {
//Can capture the error here, not capturing because of simplicity.
Expand All @@ -176,7 +179,19 @@ func (h *VoteExtensionHandler) ExtendVote() sdk.ExtendVoteHandler {
//We need to do this to get the tx hash
var txBytes tmTypes.Tx = rawTx

txResults[string(txBytes.Hash())] = res
//TODO:Should only choose to vote for those tx for
//which response from fn(ctx, tx) is true
//It can save P2P bandwidth as less data need to
//be propogated

//Make the res as enum
sideTxResult := SideTxResponse{
TxHash: txBytes.Hash(),
Result: res, //TODO:Can also remore this field as it will always be true.
}

txResults = append(txResults, sideTxResult)

}
}
}
Expand Down Expand Up @@ -218,9 +233,29 @@ func (h *VoteExtensionHandler) VerifyVoteExtension() sdk.VerifyVoteExtensionHand
// PreFinalizeBlock -> Extract VEs from previous block and persist in DB.
func (app *SimApp) NewPreBlockHookHandler() sdk.PreBlocker {
return func(ctx sdk.Context, req *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) {
txs := req.Txs

for _, rawTx := range txs {
ctx.WithTxBytes(rawTx)
if tx, err := app.TxDecode(rawTx); err == nil {
msgs := tx.GetMsgs()
msg := msgs[1]

//It checks whether the tx is sideTx for not
_, ok := app.VoteExtHandler.modSideHandler[sdk.MsgTypeURL(msg)]
if ok {
//Need to implement the following function
//sideChannelKeeper.SetSideTx(rawTx)
//Doing this with stakingKeeper for now

var txBytes tmTypes.Tx = rawTx
app.StakingKeeper.SetEventRecord(ctx, txBytes.Hash(), rawTx)

Check warning

Code scanning / gosec

Errors unhandled. Warning

Errors unhandled.

}
}
}

if req.Height > 5 {
var ve VoteExtension
var eventData *stakingtypes.EventRecord
var extVoteInfo []abci.ExtendedVoteInfo

// Extract ExtendedVoteInfo from txs (encoded at the beginning)
Expand All @@ -229,28 +264,43 @@ func (app *SimApp) NewPreBlockHookHandler() sdk.PreBlocker {
fmt.Println("Error occurred while decoding ExtendedVoteInfo: %w", err)
}

for _, vote := range extVoteInfo {
err := json.Unmarshal(vote.VoteExtension, &ve)
//TODO: Check for this parameter 'req.DecidedLastCommit.Round'
if err := app.StakingKeeper.ValidateVoteExtensions(ctx, ctx.BlockHeight(), ctx.ChainID(), extVoteInfo, req.DecidedLastCommit.Round); err != nil {
return &sdk.ResponsePreBlock{}, nil
}

sideTxResult := getBeginSideBlockData(extVoteInfo)

for _, res := range sideTxResult {
rawTx, err := app.StakingKeeper.GetEventRecord(ctx, res.TxHash)
if err != nil {
fmt.Println("Error Occurred while unmarshaling in PreFinalize: ", err)
fmt.Println("Error in getting the tx from the keeper")
continue
}

if ve.Result == Vote_YES {
if err := json.Unmarshal(ve.Data, &eventData); err == nil {
fmt.Println("PERSISTING VE DATA!", "HEIGHT: ", ctx.BlockHeight())
app.StakingKeeper.SetEventRecord(ctx, *eventData)
if tx, err := app.TxDecode(rawTx); err == nil {
msgs := tx.GetMsgs()
msg := msgs[1]

gotMajority := app.StakingKeeper.ValidateSideTxVotesMajority(ctx, res.Validators)

fn, ok := app.VoteExtHandler.modPostHandler[sdk.MsgTypeURL(msg)]
//TODO:Could get eventResponse back also,need to modify the PostHandler
if ok {
err := fn(ctx, msg, gotMajority)
if err != nil {
fmt.Println("Error in calling the postHandler for the particular msg", msg)
}

}
}

}

return &sdk.ResponsePreBlock{}, nil
}

return &sdk.ResponsePreBlock{}, nil

}

}

func (h *VoteExtensionHandler) SetHandlers(bApp *baseapp.BaseApp) {
Expand All @@ -263,3 +313,79 @@ func (app *SimApp) SetProposalHandlers(bApp *baseapp.BaseApp) {
bApp.SetProcessProposal(app.NewProcessProposalHandler())
bApp.SetPreBlocker(app.NewPreBlockHookHandler())
}

func getBeginSideBlockData(extendedVoteInfos []abci.ExtendedVoteInfo) []SideTxResult {
// returns [
// {
// txHash: txHash,
// validators: [
// {
// result: 1,
// validators: []validators{}
// },
// ...
// ]
// },
// ...
// ]

// prepare result
result := make([]SideTxResult, 0)

// iterate all votes
for _, voteInfos := range extendedVoteInfos {

var responses []SideTxResponse

//TODO:Please check for the signature of the vote
//It might be possible that block proposer make the malicious
//changes inside the spl tx
//Use the code from the CometBFT
err := json.Unmarshal(voteInfos.VoteExtension, &responses)
if err != nil {
fmt.Println("Error Occurred while unmarshaling in PreFinalize: ", err)
continue
}

txMapping := make(map[int]bool)

for _, res := range responses {
// find if result object is already created
resultIndex := -1
for i, rr := range result {
if bytes.Equal(rr.TxHash, res.TxHash) {
resultIndex = i
break
}
}

// create tx-hash based object, if not found yet
if resultIndex == -1 {
result = append(result, SideTxResult{
TxHash: res.TxHash,
Validators: make([]abci.Validator, 0),
})

// set new result index
resultIndex = len(result) - 1
}

// if tx is not processed for current vote, add it into sigs for particular side-tx result
if _, ok := txMapping[resultIndex]; !ok {

//If the validator has voted 'YES' for the tx
//then include that particular validator in address
//TODO:Pre recheck this condition
if voteInfos.BlockIdFlag == types.BlockIDFlagCommit && res.Result {
// get result object from result index
result[resultIndex].Validators = append(result[resultIndex].Validators, voteInfos.Validator)
}

// add tx hash for the record for particular vote to avoid duplicate votes
txMapping[resultIndex] = true
}
}
}

return result
}
21 changes: 18 additions & 3 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ type SimApp struct {

// module configurator
configurator module.Configurator

// VoteExtensionHandler
VoteExtHandler *VoteExtensionHandler
}

func init() {
Expand Down Expand Up @@ -269,8 +272,8 @@ func NewSimApp(
}

//Set the VoteExtension Handler
voteExtHandler := NewVoteExtensionHandler(app)
voteExtHandler.SetHandlers(bApp)
app.VoteExtHandler = NewVoteExtensionHandler(app)
app.VoteExtHandler.SetHandlers(bApp)

app.ParamsKeeper = initParamsKeeper(appCodec, legacyAmino, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey])

Expand Down Expand Up @@ -470,7 +473,14 @@ func NewSimApp(
//Register the sideHandler for msg
for _, module := range app.ModuleManager.Modules {
if module, ok := module.(HasSidehandlerServices); ok {
module.RegisterSideHandlerServices(&voteExtHandler.modSideHandler)
module.RegisterSideHandlerServices(&(app.VoteExtHandler.modSideHandler))
}
}

//Register the PostHandler for msg
for _, module := range app.ModuleManager.Modules {
if module, ok := module.(HasPosthandlerServices); ok {
module.RegisterPostHandlerServices(&(app.VoteExtHandler.modPostHandler))
}
}

Expand Down Expand Up @@ -789,3 +799,8 @@ type HasSidehandlerServices interface {
// RegisterServices allows a module to register services.
RegisterSideHandlerServices(*map[string]SideHandler)
}

type HasPosthandlerServices interface {
// RegisterServices allows a module to register services.
RegisterPostHandlerServices(*map[string]PostHandler)
}
8 changes: 8 additions & 0 deletions simapp/helper/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ import sdk "github.com/cosmos/cosmos-sdk/types"
// SideHandler is the msg specific funtion which will be called
// inside the voteEntension function
type SideHandler func(ctx sdk.Context, tx sdk.Tx) (bool, error)

// PostHandler is the msg specific funtion which will be called
// inside the voteEntension function
type PostHandler func(ctx sdk.Context, msg sdk.Msg, res bool) error

// PostHandler is the msg specific funtion which will be called
// inside the voteEntension function
type PostHandlerLogic func(ctx sdk.Context, src interface{}, msg sdk.Msg, res bool) error
Loading

0 comments on commit c4ef54b

Please sign in to comment.