-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9e7e4f1
commit eff1a9d
Showing
11 changed files
with
643 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
package accounttx | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
tmdb "github.com/cometbft/cometbft-db" | ||
abci "github.com/cometbft/cometbft/abci/types" | ||
tmjson "github.com/cometbft/cometbft/libs/json" | ||
tm "github.com/cometbft/cometbft/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
terra "github.com/terra-money/alliance/app" | ||
"github.com/terra-money/feather-core/app" | ||
|
||
"github.com/terra-money/feather-core/mantlemint/db/safe_batch" | ||
"github.com/terra-money/feather-core/mantlemint/indexer" | ||
"github.com/terra-money/feather-core/mantlemint/indexer/tx" | ||
"github.com/terra-money/feather-core/mantlemint/mantlemint" | ||
) | ||
|
||
var cdc = app.MakeEncodingConfig() | ||
|
||
var IndexTx = indexer.CreateIndexer(func(db safe_batch.SafeBatchDB, block *tm.Block, blockID *tm.BlockID, evc *mantlemint.EventCollector, _ *app.App) error { | ||
for i, txByte := range block.Txs { | ||
txRecord := evc.ResponseDeliverTxs[i] | ||
addrsInTx, err := parseTxBytesForAddresses(txByte) | ||
if err != nil { | ||
return err | ||
} | ||
addrsInEvents, err := parseEventsForAddresses(txRecord.Events) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for k, _ := range addrsInEvents { | ||
addrsInTx[k] = true | ||
} | ||
|
||
for addr, _ := range addrsInTx { | ||
key := GetAccountTxKey(addr, uint64(block.Height), uint64(i)) | ||
accountTx := AccountTx{ | ||
TxHash: fmt.Sprintf("%X", txByte.Hash()), | ||
BlockHeight: uint64(block.Height), | ||
Timestamp: block.Time, | ||
} | ||
b, err := json.Marshal(accountTx) | ||
if err != nil { | ||
return err | ||
} | ||
err = db.Set(key, b) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
return nil | ||
}) | ||
|
||
func parseTxBytesForAddresses(txByte []byte) (addrs map[string]bool, err error) { | ||
// Use a map to collect unique addresses | ||
addrs = make(map[string]bool) | ||
|
||
// Decode to Tx struct | ||
tx, err := cdc.TxConfig.TxDecoder()(txByte) | ||
if err != nil { | ||
return addrs, err | ||
} | ||
|
||
wrappedTx, err := cdc.TxConfig.WrapTxBuilder(tx) | ||
if err != nil { | ||
return addrs, err | ||
} | ||
|
||
signers := wrappedTx.GetTx().GetSigners() | ||
for _, signer := range signers { | ||
addr, err := sdk.Bech32ifyAddressBytes(terra.AccountAddressPrefix, signer) | ||
if err != nil { | ||
return addrs, err | ||
} | ||
addrs[addr] = true | ||
} | ||
|
||
// Encode to JSON | ||
jsonByte, err := cdc.TxConfig.TxJSONEncoder()(tx) | ||
if err != nil { | ||
return addrs, err | ||
} | ||
// Decode to generic interface to find addresses | ||
var txRaw map[string]interface{} | ||
err = json.Unmarshal(jsonByte, &txRaw) | ||
if err != nil { | ||
return addrs, err | ||
} | ||
bodyRaw, found := txRaw["body"] | ||
if !found { | ||
return addrs, nil | ||
} | ||
|
||
body, ok := bodyRaw.(map[string]interface{}) | ||
if !ok { | ||
return addrs, fmt.Errorf("unable to coerce tx body into map") | ||
} | ||
|
||
var findAddresses func(o interface{}) | ||
findAddresses = func(o interface{}) { | ||
stringValue, isString := o.(string) | ||
if isString { | ||
_, err := sdk.GetFromBech32(stringValue, terra.AccountAddressPrefix) | ||
if err == nil { | ||
addrs[stringValue] = true | ||
} | ||
return | ||
} | ||
|
||
mapValue, isMap := o.(map[string]interface{}) | ||
if isMap { | ||
for _, v := range mapValue { | ||
findAddresses(v) | ||
} | ||
return | ||
} | ||
|
||
arrayValue, isArray := o.([]interface{}) | ||
if isArray { | ||
for _, a := range arrayValue { | ||
findAddresses(a) | ||
} | ||
} | ||
} | ||
|
||
msgsRaw, found := body["messages"] | ||
if !found { | ||
return addrs, nil | ||
} | ||
findAddresses(msgsRaw) | ||
return addrs, nil | ||
} | ||
|
||
func parseEventsForAddresses(events []abci.Event) (addrs map[string]bool, err error) { | ||
addrs = make(map[string]bool) | ||
for _, event := range events { | ||
attrs := event.GetAttributes() | ||
for _, attr := range attrs { | ||
addrStr := string(attr.GetValue()) | ||
_, err := sdk.GetFromBech32(addrStr, terra.AccountAddressPrefix) | ||
if err == nil { | ||
addrs[addrStr] = true | ||
} | ||
} | ||
} | ||
return addrs, nil | ||
} | ||
|
||
func getTxnsByAccount(db tmdb.DB, account string, offset uint64, limit uint64) (txs []tx.TxByHeightRecord, err error) { | ||
key := GetAccountTxKeyByAddr(account) | ||
iter, err := db.Iterator(key, sdk.PrefixEndBytes(key)) | ||
if err != nil { | ||
return txs, err | ||
} | ||
currentOffset := uint64(0) | ||
currentLimit := uint64(0) | ||
for iter.Valid() { | ||
if currentOffset < offset { | ||
currentOffset += 1 | ||
continue | ||
} | ||
var accountTx AccountTx | ||
b := iter.Value() | ||
err = json.Unmarshal(b, &accountTx) | ||
if err != nil { | ||
return txs, err | ||
} | ||
txRecord, err := tx.GetTxByHash(db, accountTx.TxHash) | ||
if err != nil { | ||
return txs, err | ||
} | ||
|
||
var txResponse tx.ResponseDeliverTx | ||
err = tmjson.Unmarshal(txRecord.TxResponse, &txResponse) | ||
if err != nil { | ||
return txs, err | ||
} | ||
txRes := tx.TxByHeightRecord{ | ||
Code: txResponse.Code, | ||
Codespace: txResponse.Codespace, | ||
GasUsed: txResponse.GasUsed, | ||
GasWanted: txResponse.GasWanted, | ||
Height: int64(accountTx.BlockHeight), | ||
RawLog: txResponse.Log, | ||
Logs: func() json.RawMessage { | ||
if txResponse.Code == 0 { | ||
return []byte(txResponse.Log) | ||
} else { | ||
out, _ := json.Marshal([]string{}) | ||
return out | ||
} | ||
}(), | ||
TxHash: accountTx.TxHash, | ||
Timestamp: accountTx.Timestamp, | ||
Tx: txRecord.Tx, | ||
} | ||
|
||
txs = append(txs, txRes) | ||
currentLimit += 1 | ||
if currentLimit >= limit { | ||
break | ||
} | ||
iter.Next() | ||
} | ||
return txs, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package accounttx | ||
|
||
import ( | ||
tmdb "github.com/cometbft/cometbft-db" | ||
tmjson "github.com/cometbft/cometbft/libs/json" | ||
"github.com/gorilla/mux" | ||
"github.com/terra-money/feather-core/mantlemint/indexer" | ||
"net/http" | ||
"strconv" | ||
) | ||
|
||
var ( | ||
defaultLimit = uint64(100) | ||
defaultOffset = uint64(0) | ||
EndpointGETAccountTxs = "/index/account/{account}" | ||
) | ||
|
||
var RegisterRESTRoute = indexer.CreateRESTRoute(func(router *mux.Router, indexerDB tmdb.DB) { | ||
router.HandleFunc(EndpointGETAccountTxs, func(w http.ResponseWriter, r *http.Request) { | ||
account, ok := mux.Vars(r)["account"] | ||
if !ok { | ||
http.Error(w, "invalid request: account not found", 400) | ||
return | ||
} | ||
queries := r.URL.Query() | ||
offset, err := strconv.ParseUint(queries.Get("offset"), 10, 64) | ||
if err != nil { | ||
offset = defaultOffset | ||
} | ||
limit, err := strconv.ParseUint(queries.Get("offset"), 10, 64) | ||
if err != nil { | ||
limit = defaultLimit | ||
} | ||
txs, err := getTxnsByAccount(indexerDB, account, offset, limit) | ||
if err != nil { | ||
http.Error(w, err.Error(), 500) | ||
} | ||
|
||
rxRes := &GetAccountTxsResponse{ | ||
Limit: limit, | ||
Offset: offset, | ||
Txs: txs, | ||
} | ||
res, err := tmjson.Marshal(rxRes) | ||
if err != nil { | ||
http.Error(w, err.Error(), 500) | ||
} | ||
w.WriteHeader(200) | ||
w.Write(res) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package accounttx | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/terra-money/feather-core/mantlemint/indexer/tx" | ||
"time" | ||
) | ||
|
||
type AccountTx struct { | ||
TxHash string `json:"txhash"` | ||
BlockHeight uint64 `json:"height"` | ||
Timestamp time.Time `json:"timestamp"` | ||
} | ||
|
||
var ( | ||
AccountTxPrefix = []byte("account_tx/address:") | ||
) | ||
|
||
func GetAccountTxKeyByAddr(addr string) (key []byte) { | ||
key = append(AccountTxPrefix, addr...) | ||
return key | ||
} | ||
|
||
func GetAccountTxKey(addr string, blockHeight uint64, txIndex uint64) (key []byte) { | ||
key = append(GetAccountTxKeyByAddr(addr), sdk.Uint64ToBigEndian(blockHeight)...) | ||
key = append(key, sdk.Uint64ToBigEndian(txIndex)...) | ||
return key | ||
} | ||
|
||
type GetAccountTxsResponse struct { | ||
Limit uint64 `json:"limit"` | ||
Offset uint64 `json:"offset"` | ||
Txs []tx.TxByHeightRecord `json:"txs"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.