Skip to content

Commit

Permalink
fix get transaction endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
miiu96 committed Aug 5, 2024
1 parent 5815d8d commit 89ad27b
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 4 deletions.
3 changes: 3 additions & 0 deletions api/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ var ErrInvalidReceiverAddress = errors.New("invalid hex receiver address provide
// ErrTransactionNotFound signals that a transaction was not found
var ErrTransactionNotFound = errors.New("transaction not found")

// ErrSCRsNoFound signals that smart contract results were not found
var ErrSCRsNoFound = errors.New("smart contract results not found")

// ErrTransactionsNotFoundInPool signals that no transaction was not found in pool
var ErrTransactionsNotFoundInPool = errors.New("transactions not found in pool")

Expand Down
12 changes: 12 additions & 0 deletions data/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ type GetTransactionResponse struct {
Code string `json:"code"`
}

// GetSCRsResponseData follows the format of the data field of get smart contract results response
type GetSCRsResponseData struct {
SCRs []*transaction.ApiSmartContractResult `json:"scrs"`
}

// GetSCRsResponse defines a response from the node holding the smart contract results
type GetSCRsResponse struct {
Data GetSCRsResponseData `json:"data"`
Error string `json:"error"`
Code string `json:"code"`
}

// transactionWrapper is a wrapper over a normal transaction in order to implement the interface needed in mx-chain-go
// for computing gas cost for a transaction
type transactionWrapper struct {
Expand Down
135 changes: 132 additions & 3 deletions process/transactionProcessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ const TransactionSimulatePath = "/transaction/simulate"
// MultipleTransactionsPath defines the multiple transactions send path of the node
const MultipleTransactionsPath = "/transaction/send-multiple"

// SCRsByTxHash defines smart contract results by transaction hash path of the node
const SCRsByTxHash = "/transaction/scrs-by-tx-hash/"

const (
withResultsParam = "?withResults=true"
scrHashParam = "?scrHash=%s"
checkSignatureFalse = "?checkSignature=false"
bySenderParam = "&by-sender="
fieldsParam = "?fields="
Expand Down Expand Up @@ -68,6 +72,11 @@ type erdTransaction struct {
Version uint32 `json:"version"`
}

type tupleHashWasFetched struct {
hash string
fetched bool
}

// TransactionProcessor is able to process transaction requests
type TransactionProcessor struct {
proc Processor
Expand Down Expand Up @@ -723,6 +732,7 @@ func (tp *TransactionProcessor) gatherAllLogsAndScrs(tx *transaction.ApiTransact

func (tp *TransactionProcessor) getTxFromObservers(txHash string, reqType requestType, withResults bool) (*transaction.ApiTransactionResult, error) {
observersShardIDs := tp.proc.GetShardIDs()
shardIDWasFetch := make(map[uint32]*tupleHashWasFetched)
for _, observerShardID := range observersShardIDs {
nodesInShard, err := tp.getNodesInShard(observerShardID, reqType)
if err != nil {
Expand All @@ -749,42 +759,155 @@ func (tp *TransactionProcessor) getTxFromObservers(txHash string, reqType reques
"sender address", getTxResponse.Data.Transaction.Sender,
"error", err.Error())
}
shardIDWasFetch[sndShardID] = &tupleHashWasFetched{
hash: getTxResponse.Data.Transaction.Hash,
fetched: false,
}

rcvShardID, err := tp.getShardByAddress(getTxResponse.Data.Transaction.Receiver)
if err != nil {
log.Warn("cannot compute shard ID from receiver address",
"receiver address", getTxResponse.Data.Transaction.Receiver,
"error", err.Error())
}
shardIDWasFetch[rcvShardID] = &tupleHashWasFetched{
hash: getTxResponse.Data.Transaction.Hash,
fetched: false,
}

isIntraShard := sndShardID == rcvShardID
observerIsInDestShard := rcvShardID == observerShardID
if isIntraShard {
return &getTxResponse.Data.Transaction, nil
shardIDWasFetch[sndShardID].fetched = true
if len(getTxResponse.Data.Transaction.SmartContractResults) == 0 {
return &getTxResponse.Data.Transaction, nil
}

tp.extraShardFromSCRs(getTxResponse.Data.Transaction.SmartContractResults, shardIDWasFetch)
}

if observerIsInDestShard {
// need to get transaction from source shard and merge scResults
// if withEvents is true
return tp.alterTxWithScResultsFromSourceIfNeeded(txHash, &getTxResponse.Data.Transaction, withResults), nil
txFromSource := tp.alterTxWithScResultsFromSourceIfNeeded(txHash, &getTxResponse.Data.Transaction, withResults, shardIDWasFetch)

tp.extraShardFromSCRs(txFromSource.SmartContractResults, shardIDWasFetch)

err = tp.fetchSCRSBasedOnShardMap(txFromSource, shardIDWasFetch)
if err != nil {
return nil, err
}

return txFromSource, nil
}

// get transaction from observer that is in destination shard
txFromDstShard, ok := tp.getTxFromDestShard(txHash, rcvShardID, withResults)
if ok {
tp.extraShardFromSCRs(txFromDstShard.SmartContractResults, shardIDWasFetch)

alteredTxFromDest := tp.mergeScResultsFromSourceAndDestIfNeeded(&getTxResponse.Data.Transaction, txFromDstShard, withResults)

err = tp.fetchSCRSBasedOnShardMap(alteredTxFromDest, shardIDWasFetch)
if err != nil {
return nil, err
}

return alteredTxFromDest, nil
}

// return transaction from observer from source shard
// if did not get ok responses from observers from destination shard

err = tp.fetchSCRSBasedOnShardMap(&getTxResponse.Data.Transaction, shardIDWasFetch)
if err != nil {
return nil, err
}

return &getTxResponse.Data.Transaction, nil
}

return nil, errors.ErrTransactionNotFound
}

func (tp *TransactionProcessor) alterTxWithScResultsFromSourceIfNeeded(txHash string, tx *transaction.ApiTransactionResult, withResults bool) *transaction.ApiTransactionResult {
func (tp *TransactionProcessor) fetchSCRSBasedOnShardMap(tx *transaction.ApiTransactionResult, shardIDWasFetch map[uint32]*tupleHashWasFetched) error {
for shardID, info := range shardIDWasFetch {
scrs, err := tp.fetchSCRs(tx.Hash, info.hash, shardID)
if err != nil {
return err
}

scResults := append(tx.SmartContractResults, scrs...)
scResultsNew := tp.getScResultsUnion(scResults)

tx.SmartContractResults = scResultsNew
info.fetched = true
}

return nil
}

func (tp *TransactionProcessor) fetchSCRs(txHash, scrHash string, shardID uint32) ([]*transaction.ApiSmartContractResult, error) {
observers, err := tp.getNodesInShard(shardID, requestTypeFullHistoryNodes)
if err != nil {
return nil, err
}

apiPath := SCRsByTxHash + txHash + fmt.Sprintf(scrHashParam, scrHash)
for _, observer := range observers {
getTxResponseDst := &data.GetSCRsResponse{}
respCode, errG := tp.proc.CallGetRestEndPoint(observer.Address, apiPath, getTxResponseDst)
if errG != nil {
log.Trace("cannot get smart contract results", "address", observer.Address, "error", errG)
continue
}

if respCode != http.StatusOK {
continue
}

return getTxResponseDst.Data.SCRs, nil
}

return nil, errors.ErrSCRsNoFound

}

func (tp *TransactionProcessor) extraShardFromSCRs(scrs []*transaction.ApiSmartContractResult, shardIDWasFetch map[uint32]*tupleHashWasFetched) {
for _, scr := range scrs {
sndShardID, err := tp.getShardByAddress(scr.SndAddr)
if err != nil {
log.Warn("cannot compute shard ID from sender address",
"sender address", scr.SndAddr,
"error", err.Error())
}

_, found := shardIDWasFetch[sndShardID]
if !found {
shardIDWasFetch[sndShardID] = &tupleHashWasFetched{
hash: scr.Hash,
fetched: false,
}
}

rcvShardID, err := tp.getShardByAddress(scr.SndAddr)
if err != nil {
log.Warn("cannot compute shard ID from receiver address",
"receiver address", scr.RcvAddr,
"error", err.Error())
}

_, found = shardIDWasFetch[rcvShardID]
if !found {
shardIDWasFetch[rcvShardID] = &tupleHashWasFetched{
hash: scr.Hash,
fetched: false,
}
}
}
}

func (tp *TransactionProcessor) alterTxWithScResultsFromSourceIfNeeded(txHash string, tx *transaction.ApiTransactionResult, withResults bool, shardIDWasFetch map[uint32]*tupleHashWasFetched) *transaction.ApiTransactionResult {
if !withResults || len(tx.SmartContractResults) == 0 {
return tx
}
Expand All @@ -801,6 +924,12 @@ func (tp *TransactionProcessor) alterTxWithScResultsFromSourceIfNeeded(txHash st
}

alteredTxFromDest := tp.mergeScResultsFromSourceAndDestIfNeeded(&getTxResponse.Data.Transaction, tx, withResults)

shardIDWasFetch[tx.SourceShard] = &tupleHashWasFetched{
hash: getTxResponse.Data.Transaction.Hash,
fetched: true,
}

return alteredTxFromDest
}

Expand Down
5 changes: 4 additions & 1 deletion process/transactionProcessor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,10 @@ func TestTransactionProcessor_GetTransactionStatusCrossShardTransaction(t *testi
}, nil
},
CallGetRestEndPointCalled: func(address string, path string, value interface{}) (i int, err error) {
responseGetTx := value.(*data.GetTransactionResponse)
responseGetTx, ok := value.(*data.GetTransactionResponse)
if !ok {
return http.StatusOK, nil
}

responseGetTx.Data.Transaction = transaction.ApiTransactionResult{
Receiver: sndrShard1,
Expand Down

0 comments on commit 89ad27b

Please sign in to comment.