├── .gitignore ├── Dockerfile ├── Makefile ├── README.md ├── conf ├── db │ └── types.go └── server │ └── types.go ├── cron ├── cron_task.go └── cron_task_test.go ├── docker └── docker-compose.yml ├── go.mod ├── go.sum ├── logger ├── conf.go ├── logger.go └── zap_logger.go ├── main.go ├── monitor ├── server.go └── status │ └── metrics.go ├── script └── mongodb.js ├── service ├── handler │ ├── account.go │ ├── block.go │ ├── block_test.go │ ├── proposal.go │ ├── proposal_test.go │ ├── tx.go │ ├── tx_test.go │ └── types.go ├── sync.go ├── sync_test.go └── task │ ├── sync_proposal_status_task.go │ ├── task_create.go │ ├── task_create_test.go │ ├── task_execute.go │ ├── task_execute_test.go │ ├── task_statistics.go │ ├── task_statistics_test.go │ └── timer_task.go ├── store ├── document │ ├── account.go │ ├── account_test.go │ ├── block.go │ ├── proposal.go │ ├── sync_conf.go │ ├── sync_task.go │ ├── token_flow.go │ ├── tx.go │ └── validator.go ├── store.go ├── store_test.go └── types.go ├── types ├── msg │ ├── asset.go │ ├── bank.go │ ├── coinswap.go │ ├── distribution.go │ ├── gov.go │ ├── guardian.go │ ├── htlc.go │ ├── rand.go │ └── stake.go ├── msg_test.go ├── types.go └── types_test.go └── util ├── constant └── types.go └── helper ├── account.go ├── account_test.go ├── common.go ├── common_test.go ├── delegator.go ├── delegator_test.go ├── pool_client.go ├── pool_client_test.go ├── pool_factory.go ├── proposal.go ├── proposal_test.go ├── query.go ├── query_test.go ├── tx.go ├── tx_test.go ├── validator.go └── validator_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | vendor/ 3 | .DS_Store 4 | *.out 5 | irishub-sync 6 | irishub-sync-unix 7 | *.iml 8 | *.log 9 | processor/ 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:20190925 2 | 3 | # Set up dependencies 4 | ENV PACKAGES go make git libc-dev bash 5 | 6 | # Set up GOPATH & PATH 7 | 8 | ENV PROJECT_NAME irishub-sync 9 | ENV GOPATH /root/go 10 | ENV BASE_PATH $GOPATH/src/github.com/irisnet 11 | ENV REPO_PATH $BASE_PATH/$PROJECT_NAME 12 | ENV LOG_DIR /$PROJECT_NAME/log 13 | ENV PATH $GOPATH/bin:$PATH 14 | ENV GO111MODULE on 15 | 16 | # Set volumes 17 | 18 | VOLUME $LOG_DIR 19 | 20 | # Link expected Go repo path 21 | 22 | RUN mkdir -p $GOPATH/pkg $GOPATH/bin $BASE_PATH $REPO_PATH $LOG_DIR 23 | 24 | # Add source files 25 | 26 | COPY . $REPO_PATH 27 | 28 | # Install minimum necessary dependencies, build irishub-server 29 | RUN apk add --no-cache $PACKAGES && \ 30 | cd $REPO_PATH && make all && \ 31 | mv $REPO_PATH/$PROJECT_NAME $GOPATH/bin && \ 32 | rm -rf $REPO_PATH/vendor && \ 33 | rm -rf $GOPATH/src/github.com/golang $GOPATH/bin/dep $GOPATH/pkg/* && \ 34 | apk del $PACKAGES 35 | 36 | VOLUME ["$LOG_DIR"] 37 | 38 | 39 | #CMD irishub-sync > $LOG_DIR/debug.log && tail -f $LOG_DIR/debug.log 40 | CMD irishub-sync -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GOCMD=go 2 | GOBUILD=$(GOCMD) build 3 | GOCLEAN=$(GOCMD) clean 4 | GOTEST=$(GOCMD) test 5 | GOGET=$(GOCMD) get 6 | GOMOD=$(GOCMD) mod 7 | BINARY_NAME=irishub-sync 8 | BINARY_UNIX=$(BINARY_NAME)-unix 9 | 10 | all: get_vendor build 11 | 12 | get_vendor: 13 | @rm -rf vendor/ 14 | @echo "--> Running go mod vendor" 15 | $(GOMOD) vendor 16 | 17 | build: 18 | $(GOBUILD) -o $(BINARY_NAME) -v 19 | 20 | clean: 21 | $(GOCLEAN) 22 | rm -f $(BINARY_NAME) 23 | rm -f $(BINARY_UNIX) 24 | 25 | run: 26 | $(GOBUILD) -o $(BINARY_NAME) -v 27 | ./$(BINARY_NAME) 28 | 29 | 30 | # Cross compilation 31 | build-linux: 32 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BINARY_UNIX) -v 33 | 34 | ###################################### 35 | ## Tools -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IRISHUB-SYNC 2 | A server that synchronize IRIS blockChain data into a database 3 | 4 | # Structure 5 | 6 | - `conf`: config of project 7 | - `module`: project module 8 | - `mongodb`: mongodb script to create database 9 | - `service`: main logic of sync-server, sync data from blockChain and write to database 10 | - `store`: database model 11 | - `util`: common constants and helper functions 12 | - `main.go`: bootstrap project 13 | 14 | # SetUp 15 | 16 | ## Create mongodb database 17 | 18 | run script `mongodb.js` in `mongodb` folder to create database before run project 19 | 20 | # Build And Run 21 | 22 | - Build: `make all` 23 | - Run: `make run` 24 | - Cross compilation: `make build-linux` 25 | 26 | ## Env Variables 27 | 28 | ### Db config 29 | 30 | - DB_ADDR: `required` `string` mongodb addrs(example: `127.0.0.1:27017, 127.0.0.2:27017, ...`) 31 | - DB_USER: `required` `string` mongodb user(example: `user`) 32 | - DB_PASSWD: `required` `string` mongodb password(example: `DB_PASSWD`) 33 | - DB_DATABASE:`required` `string` mongodb database name(example:`DB_DATABASE`) 34 | 35 | ### Server config 36 | 37 | - SER_BC_FULL_NODE: `required` `string` full node url(example: `tcp://127.0.0.1:26657, tcp://127.0.0.2:26657, ...`) 38 | - SER_BC_CHAIN_ID: `required` `string` chain id(example: `rainbow-dev`) 39 | - WORKER_NUM_CREATE_TASK: `required` `string` num of worker to create tasks(example: `2`) 40 | - WORKER_NUM_EXECUTE_TASK: `required` `string` num of worker to execute tasks(example: `30`) 41 | 42 | - NETWORK: `option` `string` network type(example: `testnet,mainnet`) 43 | 44 | ## Note 45 | 46 | If you synchronizes irishub data from specify block height(such as:17908 current time:1576208532) 47 | 1. At first, stop the irishub-sync and run follow sql in mongodb 48 | 49 | ``` 50 | db.sync_task.insert({'start_height':NumberLong(17908),'end_height':NumberLong(0),'current_height':NumberLong(0),'status':'unhandled','last_update_time':NumberLong(1576208532)}) 51 | ``` 52 | 53 | 2. Then, start irishub-sync 54 | 55 | -------------------------------------------------------------------------------- /conf/db/types.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/irisnet/irishub-sync/logger" 7 | "github.com/irisnet/irishub-sync/util/constant" 8 | ) 9 | 10 | var ( 11 | Addrs = "192.168.150.7:30000" 12 | User = "iris" 13 | Passwd = "irispassword" 14 | Database = "sync-iris" 15 | ) 16 | 17 | // get value of env var 18 | func init() { 19 | addrs, found := os.LookupEnv(constant.EnvNameDbAddr) 20 | if found { 21 | Addrs = addrs 22 | } 23 | logger.Info("Env Value", logger.String(constant.EnvNameDbAddr, Addrs)) 24 | 25 | user, found := os.LookupEnv(constant.EnvNameDbUser) 26 | if found { 27 | User = user 28 | } 29 | 30 | passwd, found := os.LookupEnv(constant.EnvNameDbPassWd) 31 | if found { 32 | Passwd = passwd 33 | } 34 | 35 | database, found := os.LookupEnv(constant.EnvNameDbDataBase) 36 | if found { 37 | Database = database 38 | } 39 | logger.Info("Env Value", logger.String(constant.EnvNameDbDataBase, Database)) 40 | } 41 | -------------------------------------------------------------------------------- /conf/server/types.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/irisnet/irishub-sync/logger" 9 | "github.com/irisnet/irishub-sync/util/constant" 10 | ) 11 | 12 | var ( 13 | BlockChainMonitorUrl = []string{"http://192.168.150.32:26657"} 14 | 15 | WorkerNumCreateTask = 1 16 | WorkerNumExecuteTask = 60 17 | 18 | InitConnectionNum = 50 // fast init num of tendermint client pool 19 | MaxConnectionNum = 100 // max size of tendermint client pool 20 | SyncProposalStatus = "0 */1 * * * *" // every minute 21 | 22 | Network = "testnet" 23 | ) 24 | 25 | // get value of env var 26 | func init() { 27 | nodeUrl, found := os.LookupEnv(constant.EnvNameSerNetworkFullNode) 28 | if found { 29 | BlockChainMonitorUrl = strings.Split(nodeUrl, ",") 30 | } 31 | 32 | logger.Info("Env Value", logger.Any(constant.EnvNameSerNetworkFullNode, BlockChainMonitorUrl)) 33 | 34 | workerNumCreateTask, found := os.LookupEnv(constant.EnvNameWorkerNumCreateTask) 35 | if found { 36 | //var err error 37 | //WorkerNumCreateTask, err = strconv.Atoi(workerNumCreateTask) 38 | //if err != nil { 39 | // logger.Fatal("Can't convert str to int", logger.String(constant.EnvNameWorkerNumCreateTask, workerNumCreateTask)) 40 | //} 41 | // worker num for create task no longer set by env value 42 | WorkerNumCreateTask = 1 43 | } 44 | logger.Info("Env Value", logger.Int(constant.EnvNameWorkerNumCreateTask, WorkerNumCreateTask)) 45 | 46 | workerNumExecuteTask, found := os.LookupEnv(constant.EnvNameWorkerNumExecuteTask) 47 | if found { 48 | var err error 49 | WorkerNumExecuteTask, err = strconv.Atoi(workerNumExecuteTask) 50 | if err != nil { 51 | logger.Fatal("Can't convert str to int", logger.String(constant.EnvNameWorkerNumExecuteTask, workerNumCreateTask)) 52 | } 53 | } 54 | logger.Info("Env Value", logger.Int(constant.EnvNameWorkerNumExecuteTask, WorkerNumExecuteTask)) 55 | 56 | network, found := os.LookupEnv(constant.EnvNameNetwork) 57 | if found { 58 | Network = network 59 | } 60 | logger.Info("Env Value", logger.String(constant.EnvNameNetwork, Network)) 61 | } 62 | -------------------------------------------------------------------------------- /cron/cron_task.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/logger" 5 | "github.com/irisnet/irishub-sync/store" 6 | "github.com/irisnet/irishub-sync/store/document" 7 | "github.com/irisnet/irishub-sync/util/helper" 8 | "gopkg.in/mgo.v2" 9 | "gopkg.in/mgo.v2/bson" 10 | "os" 11 | "os/signal" 12 | "time" 13 | ) 14 | 15 | type CronService struct{} 16 | 17 | func (s *CronService) StartCronService() { 18 | 19 | logger.Info("Start Update Txs CronService ...") 20 | ticker := time.NewTicker(24 * time.Hour) 21 | defer ticker.Stop() 22 | stop := make(chan os.Signal) 23 | signal.Notify(stop, os.Interrupt) 24 | 25 | fnUpdate := func() { 26 | defer func() { 27 | if r := recover(); r != nil { 28 | logger.Error("CronService have error", logger.Any("err", r)) 29 | } 30 | }() 31 | 32 | runValue := true 33 | skip := 0 34 | for runValue { 35 | total, err := UpdateUnknownOrEmptyTypeTxsByPage(skip, 20) 36 | if err != nil { 37 | logger.Error("getCommonTx have error", logger.String("err", err.Error())) 38 | } 39 | if total < 20 { 40 | runValue = false 41 | logger.Info("Finish UpdateUnknownTxsByPage.", logger.Int("total", total)) 42 | } else { 43 | skip = skip + total 44 | logger.Info("Continue UpdateUnknownTxsByPage", logger.Int("skip", skip)) 45 | } 46 | } 47 | 48 | logger.Info("Finish Update Txs.") 49 | } 50 | fnUpdate() 51 | for { 52 | select { 53 | case <-ticker.C: 54 | fnUpdate() 55 | case <-stop: 56 | close(stop) 57 | logger.Info("Update Txs CronService Quit...") 58 | return 59 | } 60 | 61 | } 62 | 63 | } 64 | 65 | func UpdateUnknownOrEmptyTypeTxsByPage(skip, limit int) (int, error) { 66 | 67 | res, err := new(document.CommonTx).GetUnknownOrEmptyTypeTxs(skip, limit) 68 | if err != nil { 69 | return 0, err 70 | } 71 | 72 | if len(res) > 0 { 73 | doWork(res, UpdateUnknowOrEmptyTypeTxs) 74 | } 75 | 76 | return len(res), nil 77 | } 78 | 79 | func doWork(commonTxs []document.CommonTx, fn func([]*document.CommonTx) error) { 80 | client := helper.GetClient() 81 | defer func() { 82 | client.Release() 83 | }() 84 | 85 | for _, val := range commonTxs { 86 | txs, err := ParseUnknownOrEmptyTypeTxs(val.Height, client) 87 | if err != nil { 88 | logger.Error("ParseUnknownTxs have error", logger.String("error", err.Error())) 89 | continue 90 | } 91 | if err := fn(txs); err != nil { 92 | logger.Warn("UpdateUnknowTxs have error", logger.String("error", err.Error())) 93 | } 94 | } 95 | 96 | } 97 | 98 | func ParseUnknownOrEmptyTypeTxs(b int64, client *helper.Client) (commontx []*document.CommonTx, err error) { 99 | 100 | defer func() { 101 | if err := recover(); err != nil { 102 | logger.Error("parse block fail", logger.Int64("blockHeight", b), 103 | logger.Any("err", err)) 104 | } 105 | }() 106 | 107 | block, err := client.Block(&b) 108 | if err != nil { 109 | // there is possible parse block fail when in iterator 110 | var err2 error 111 | client2 := helper.GetClient() 112 | block, err2 = client2.Block(&b) 113 | client2.Release() 114 | if err2 != nil { 115 | return nil, err2 116 | } 117 | } 118 | 119 | commontx = make([]*document.CommonTx, 0, len(block.Block.Txs)) 120 | 121 | for _, txByte := range block.Block.Txs { 122 | tx := helper.ParseTx(txByte, block.Block) 123 | if tx.Status != document.Unknow_Status { 124 | commontx = append(commontx, &tx) 125 | } 126 | 127 | } 128 | return 129 | } 130 | 131 | func UpdateUnknowOrEmptyTypeTxs(commontx []*document.CommonTx) error { 132 | 133 | update_fn := func(tx *document.CommonTx) error { 134 | fn := func(c *mgo.Collection) error { 135 | return c.Update(bson.M{"tx_hash": tx.TxHash}, 136 | bson.M{"$set": bson.M{"from": tx.From, "to": tx.To, "type": tx.Type, "amount": tx.Amount, 137 | "actual_fee": tx.ActualFee, "status": tx.Status, "tags": tx.Tags, "msgs": tx.Msgs, 138 | "code": tx.Code, "log": tx.Log, "gas_wanted": tx.GasWanted}}) 139 | } 140 | 141 | if err := store.ExecCollection(document.CollectionNmCommonTx, fn); err != nil { 142 | return err 143 | } 144 | return nil 145 | } 146 | 147 | for _, dbval := range commontx { 148 | update_fn(dbval) 149 | } 150 | 151 | return nil 152 | } 153 | -------------------------------------------------------------------------------- /cron/cron_task_test.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestUpdateUnknownOrEmptyTypeTxsByPage(t *testing.T) { 10 | store.Start() 11 | defer func() { 12 | store.Stop() 13 | }() 14 | num, err := UpdateUnknownOrEmptyTypeTxsByPage(0, 20) 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | 19 | t.Log(num) 20 | 21 | } 22 | 23 | func TestCronService_StartCronService(t *testing.T) { 24 | store.Start() 25 | defer func() { 26 | store.Stop() 27 | }() 28 | new(CronService).StartCronService() 29 | time.Sleep(1 * time.Minute) 30 | } 31 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | irishub-server: 4 | image: irisnet/irishub-sync:develop 5 | container_name: c-irishub-sync-develop 6 | volumes: 7 | - /mnt/data/irishub-sync/log:/irishub-sync/log 8 | environment: 9 | ENV: dev 10 | DB_ADDR: 192.168.150.7:30000 11 | DB_USER: iris 12 | DB_PASSWD: irispassword 13 | DB_DATABASE: sync-iris 14 | 15 | 16 | SER_BC_NODE_URL: tcp://192.168.150.7:26657 17 | SER_BC_CHAIN_ID: rainbow-dev 18 | SER_BC_TOKEN: iris 19 | SER_MAX_GOROUTINE: 60 20 | SER_SYNC_BLOCK_NUM: 8000 21 | 22 | LOG_FILE_PATH: /irishub-sync/log/sync_server.log 23 | LOG_FILE_MAX_SIZE: 200 24 | LOG_FILE_MAX_AGE: 7 25 | ENABLE_ATOMIC_LEVEL: true 26 | LOG_COMPRESS: true 27 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/irisnet/irishub-sync 2 | 3 | require ( 4 | github.com/go-kit/kit v0.9.0 5 | github.com/irisnet/irishub v0.16.0 6 | github.com/jolestar/go-commons-pool v2.0.0+incompatible 7 | github.com/pkg/errors v0.9.1 8 | github.com/prometheus/client_golang v1.11.1 9 | github.com/robfig/cron v1.2.0 10 | github.com/stretchr/testify v1.4.0 11 | github.com/tendermint/tendermint v0.32.7 12 | go.uber.org/zap v1.12.0 13 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 14 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 15 | gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect 16 | ) 17 | 18 | replace ( 19 | github.com/tendermint/iavl => github.com/irisnet/iavl v0.12.3 20 | github.com/tendermint/tendermint => github.com/irisnet/tendermint v0.32.0 21 | golang.org/x/crypto => github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5 22 | ) 23 | -------------------------------------------------------------------------------- /logger/conf.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/util/constant" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | type Config struct { 10 | Filename string 11 | MaxSize int 12 | MaxAge int 13 | Compress bool 14 | EnableAtomicLevel bool 15 | } 16 | 17 | var ( 18 | conf = Config{ 19 | Filename: os.ExpandEnv("$HOME/irishub-sync/sync_server.log"), 20 | MaxSize: 20, 21 | MaxAge: 7, 22 | Compress: true, 23 | EnableAtomicLevel: true, 24 | } 25 | ) 26 | 27 | func init() { 28 | fileName, found := os.LookupEnv(constant.EnvLogFileName) 29 | if found { 30 | conf.Filename = fileName 31 | } 32 | 33 | maxSize, found := os.LookupEnv(constant.EnvLogFileMaxSize) 34 | if found { 35 | if size, err := strconv.Atoi(maxSize); err == nil { 36 | conf.MaxSize = size 37 | } 38 | } 39 | 40 | maxAge, found := os.LookupEnv(constant.EnvLogFileMaxAge) 41 | if found { 42 | if age, err := strconv.Atoi(maxAge); err == nil { 43 | conf.MaxAge = age 44 | } 45 | } 46 | 47 | compress, found := os.LookupEnv(constant.EnvLogCompress) 48 | if found { 49 | if compre, err := strconv.ParseBool(compress); err == nil { 50 | conf.Compress = compre 51 | } 52 | } 53 | 54 | enableAtomicLevel, found := os.LookupEnv(constant.EnableAtomicLevel) 55 | if found { 56 | if atomicLevel, err := strconv.ParseBool(enableAtomicLevel); err == nil { 57 | conf.EnableAtomicLevel = atomicLevel 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import "go.uber.org/zap" 4 | 5 | type Logger struct { 6 | *zap.Logger 7 | module string 8 | } 9 | 10 | func GetLogger(module string) Logger { 11 | l := Logger{ 12 | Logger: zapLogger, 13 | module: module, 14 | } 15 | return l 16 | } 17 | 18 | func (l Logger) appendModule(fields []Field) []Field { 19 | var f []Field 20 | if len(l.module) != 0 { 21 | f = append(f, String("module", l.module)) 22 | f = append(f, fields...) 23 | } 24 | return f 25 | } 26 | 27 | func (l Logger) Info(msg string, fields ...Field) { 28 | defer Sync() 29 | Info(msg, l.appendModule(fields)...) 30 | } 31 | 32 | func (l Logger) Debug(msg string, fields ...Field) { 33 | defer Sync() 34 | Debug(msg, l.appendModule(fields)...) 35 | } 36 | 37 | func (l Logger) Warn(msg string, fields ...Field) { 38 | defer Sync() 39 | Warn(msg, l.appendModule(fields)...) 40 | } 41 | 42 | func (l Logger) Error(msg string, fields ...Field) { 43 | defer Sync() 44 | Error(msg, l.appendModule(fields)...) 45 | } 46 | 47 | func (l Logger) Panic(msg string, fields ...Field) { 48 | defer Sync() 49 | Panic(msg, l.appendModule(fields)...) 50 | } 51 | 52 | func (l Logger) Fatal(msg string, fields ...Field) { 53 | defer Sync() 54 | Fatal(msg, l.appendModule(fields)...) 55 | } 56 | -------------------------------------------------------------------------------- /logger/zap_logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | "go.uber.org/zap/zapcore" 6 | "gopkg.in/natefinch/lumberjack.v2" 7 | "net/http" 8 | "os" 9 | ) 10 | 11 | var ( 12 | zapLogger *zap.Logger 13 | 14 | //zap method 15 | Binary = zap.Binary 16 | Bool = zap.Bool 17 | Complex128 = zap.Complex128 18 | Complex64 = zap.Complex64 19 | Float64 = zap.Float64 20 | Float32 = zap.Float32 21 | Int = zap.Int 22 | Int64 = zap.Int64 23 | Int32 = zap.Int32 24 | Int16 = zap.Int16 25 | Int8 = zap.Int8 26 | String = zap.String 27 | Uint = zap.Uint 28 | Uint64 = zap.Uint64 29 | Uint32 = zap.Uint32 30 | Uint16 = zap.Uint16 31 | Uint8 = zap.Uint8 32 | Time = zap.Time 33 | Any = zap.Any 34 | Duration = zap.Duration 35 | ) 36 | 37 | type Field = zap.Field 38 | 39 | func Info(msg string, fields ...Field) { 40 | defer Sync() 41 | zapLogger.Info(msg, fields...) 42 | } 43 | 44 | func Debug(msg string, fields ...Field) { 45 | defer Sync() 46 | zapLogger.Debug(msg, fields...) 47 | } 48 | 49 | func Warn(msg string, fields ...Field) { 50 | defer Sync() 51 | zapLogger.Warn(msg, fields...) 52 | } 53 | 54 | func Error(msg string, fields ...Field) { 55 | defer Sync() 56 | zapLogger.Error(msg, fields...) 57 | } 58 | 59 | func Panic(msg string, fields ...Field) { 60 | defer Sync() 61 | zapLogger.Panic(msg, fields...) 62 | } 63 | 64 | func Fatal(msg string, fields ...Field) { 65 | defer Sync() 66 | zapLogger.Fatal(msg, fields...) 67 | } 68 | 69 | func With(fields ...Field) { 70 | defer Sync() 71 | zapLogger.With(fields...) 72 | } 73 | 74 | func Sync() { 75 | zapLogger.Sync() 76 | } 77 | 78 | func init() { 79 | 80 | hook := lumberjack.Logger{ 81 | Filename: conf.Filename, 82 | MaxSize: conf.MaxSize, // megabytes 83 | MaxBackups: 3, 84 | MaxAge: conf.MaxAge, //days 85 | Compress: conf.Compress, // disabled by default 86 | LocalTime: true, 87 | } 88 | 89 | fileWriter := zapcore.AddSync(&hook) 90 | 91 | consoleDebugging := zapcore.Lock(os.Stdout) 92 | 93 | // Optimize the Kafka output for machine consumption and the console output 94 | // for human operators. 95 | encoderConfig := zap.NewProductionEncoderConfig() 96 | encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder 97 | encoder := zapcore.NewJSONEncoder(encoderConfig) 98 | // Join the outputs, encoders, and level-handling functions into 99 | // zapcore.Cores, then tee the four cores together. 100 | level := zap.NewAtomicLevelAt(zap.InfoLevel) 101 | var core zapcore.Core 102 | if conf.EnableAtomicLevel { 103 | core = zapcore.NewTee( 104 | // 打印在控制台 105 | zapcore.NewCore(encoder, consoleDebugging, level), 106 | // 打印在文件中 107 | zapcore.NewCore(encoder, fileWriter, level), 108 | ) 109 | } else { 110 | // 仅打印Info级别以上的日志 111 | highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 112 | return lvl >= zapcore.InfoLevel 113 | }) 114 | // 打印所有级别的日志 115 | lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 116 | return lvl >= zapcore.DebugLevel 117 | }) 118 | 119 | core = zapcore.NewTee( 120 | // 打印在控制台 121 | zapcore.NewCore(encoder, consoleDebugging, lowPriority), 122 | // 打印在文件中 123 | zapcore.NewCore(encoder, fileWriter, highPriority), 124 | ) 125 | } 126 | caller := zap.AddCaller() 127 | callerSkipOpt := zap.AddCallerSkip(1) 128 | // From a zapcore.Core, it's easy to construct a Logger. 129 | zapLogger = zap.New(core, caller, callerSkipOpt, zap.AddStacktrace(zap.ErrorLevel)) 130 | 131 | if conf.EnableAtomicLevel { 132 | go func() { 133 | // curl -X PUT -H "Content-Type:application/json" -d '{"level":"info"}' localhost:9090 134 | http.ListenAndServe(":9090", &level) 135 | }() 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/cron" 5 | "github.com/irisnet/irishub-sync/logger" 6 | "github.com/irisnet/irishub-sync/monitor" 7 | "github.com/irisnet/irishub-sync/service" 8 | "github.com/irisnet/irishub-sync/store" 9 | "github.com/irisnet/irishub-sync/util/helper" 10 | "os" 11 | "os/signal" 12 | "syscall" 13 | ) 14 | 15 | func main() { 16 | c := make(chan os.Signal) 17 | engine := service.New() 18 | 19 | defer func() { 20 | logger.Info("Irishub Sync Service Exit...") 21 | engine.Stop() 22 | helper.ClosePool() 23 | store.Stop() 24 | logger.Sync() 25 | if err := recover(); err != nil { 26 | logger.Error(err.(string)) 27 | os.Exit(1) 28 | } 29 | }() 30 | //monitor system signal 31 | signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) 32 | // start monitor 33 | go monitor.NewMonitor().Start() 34 | //start databases service 35 | logger.Info("Databases Service Start...") 36 | store.Start() 37 | //start sync task service 38 | logger.Info("Irishub Sync Service Start...") 39 | go new(cron.CronService).StartCronService() 40 | engine.Start() 41 | //paused until the signal have received 42 | <-c 43 | } 44 | -------------------------------------------------------------------------------- /monitor/server.go: -------------------------------------------------------------------------------- 1 | package monitor 2 | 3 | import ( 4 | "fmt" 5 | "github.com/irisnet/irishub-sync/logger" 6 | "github.com/irisnet/irishub-sync/monitor/status" 7 | "github.com/prometheus/client_golang/prometheus/promhttp" 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | type Monitor struct { 13 | providers []MetricsProvider 14 | } 15 | 16 | func NewMonitor() *Monitor { 17 | var providers []MetricsProvider 18 | monitor := &Monitor{ 19 | providers: providers, 20 | } 21 | monitor.AddMetricsProvider(status.PrometheusMetrics()) 22 | return monitor 23 | } 24 | 25 | func (m *Monitor) AddMetricsProvider(provider MetricsProvider) *Monitor { 26 | m.providers = append(m.providers, provider) 27 | return m 28 | } 29 | 30 | func (m *Monitor) Start() { 31 | var startMetris = func() { 32 | for { 33 | t := time.NewTimer(time.Duration(5) * time.Second) 34 | select { 35 | case <-t.C: 36 | for _, provider := range m.providers { 37 | go provider.Report() 38 | } 39 | } 40 | } 41 | } 42 | 43 | go startMetris() 44 | srv := &http.Server{ 45 | Addr: fmt.Sprintf(":%d", 8080), 46 | Handler: promhttp.Handler(), 47 | } 48 | go func() { 49 | if err := srv.ListenAndServe(); err == nil { 50 | logger.Error("start monitor error", logger.String("error", err.Error())) 51 | } 52 | }() 53 | } 54 | 55 | type MetricsProvider interface { 56 | Report() 57 | } 58 | -------------------------------------------------------------------------------- /monitor/status/metrics.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "github.com/go-kit/kit/metrics" 5 | "github.com/go-kit/kit/metrics/prometheus" 6 | "github.com/irisnet/irishub-sync/logger" 7 | "github.com/irisnet/irishub-sync/store/document" 8 | "github.com/irisnet/irishub-sync/util/helper" 9 | prom "github.com/prometheus/client_golang/prometheus" 10 | "time" 11 | ) 12 | 13 | const ( 14 | NodeStatusNotReachable = 1 15 | NodeStatusCatchingUp = 2 16 | NodeStatusSyncing = 3 17 | ) 18 | 19 | type Metrics struct { 20 | NodeHeight metrics.Gauge 21 | DbHeight metrics.Gauge 22 | NodeStatus metrics.Gauge 23 | } 24 | 25 | func PrometheusMetrics() *Metrics { 26 | return &Metrics{ 27 | NodeHeight: prometheus.NewGaugeFrom(prom.GaugeOpts{ 28 | Namespace: "sync", 29 | Subsystem: "status", 30 | Name: "node_height", 31 | Help: "full node latest block height", 32 | }, []string{}), 33 | DbHeight: prometheus.NewGaugeFrom(prom.GaugeOpts{ 34 | Namespace: "sync", 35 | Subsystem: "status", 36 | Name: "db_height", 37 | Help: "sync system database max block height", 38 | }, []string{}), 39 | NodeStatus: prometheus.NewGaugeFrom(prom.GaugeOpts{ 40 | Namespace: "sync", 41 | Subsystem: "status", 42 | Name: "node_status", 43 | Help: "full node status(1:NotReachable,2:CatchingUp,3:Syncing)", 44 | }, []string{}), 45 | } 46 | } 47 | 48 | func (cs *Metrics) Report() { 49 | client, err := helper.GetClientWithTimeout(10 * time.Second) 50 | if err != nil { 51 | logger.Error("rpc node connection exception", logger.String("error", err.Error())) 52 | cs.NodeStatus.Set(float64(NodeStatusNotReachable)) 53 | return 54 | } 55 | defer func() { 56 | client.Release() 57 | }() 58 | status, err := client.Status() 59 | if err != nil { 60 | logger.Error("rpc node connection exception", logger.String("error", err.Error())) 61 | cs.NodeStatus.Set(float64(NodeStatusNotReachable)) 62 | return 63 | } 64 | // node height 65 | cs.NodeHeight.Set(float64(status.SyncInfo.LatestBlockHeight)) 66 | // db height 67 | height, err := document.Block{}.GetMaxBlockHeight() 68 | if err != nil { 69 | logger.Error("query block exception", logger.String("error", err.Error())) 70 | } 71 | cs.DbHeight.Set(float64(height)) 72 | if status.SyncInfo.CatchingUp { 73 | cs.NodeStatus.Set(float64(NodeStatusCatchingUp)) 74 | } else { 75 | cs.NodeStatus.Set(float64(NodeStatusSyncing)) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /script/mongodb.js: -------------------------------------------------------------------------------- 1 | //create database and user 2 | // use sync-iris 3 | // db.createUser( 4 | // { 5 | // user:"iris", 6 | // pwd:"irispassword", 7 | // roles:[{role:"root",db:"admin"}] 8 | // } 9 | // ) 10 | 11 | // create collections 12 | db.createCollection("block"); 13 | db.createCollection("sync_task"); 14 | db.createCollection("tx_common"); 15 | db.createCollection("proposal"); 16 | // db.createCollection("tx_msg"); 17 | db.createCollection("sync_conf"); 18 | db.createCollection("mgo_txn"); 19 | db.createCollection("mgo_txn.stash"); 20 | 21 | 22 | // create index 23 | db.account.createIndex({"address": 1}, {"unique": true}); 24 | db.block.createIndex({"height": -1}, {"unique": true}); 25 | 26 | db.sync_task.createIndex({"start_height": 1, "end_height": 1}, {"unique": true}); 27 | db.sync_task.createIndex({"status": 1}, {"background": true}); 28 | 29 | db.tx_common.createIndex({"height": -1}); 30 | db.tx_common.createIndex({"time": -1}); 31 | db.tx_common.createIndex({"tx_hash": 1}, {"unique": true}); 32 | // db.tx_common.createIndex({"from": 1}); 33 | // db.tx_common.createIndex({"to": 1}); 34 | db.tx_common.createIndex({"type": 1}); 35 | db.tx_common.createIndex({"status": 1}); 36 | db.tx_common.createIndex({"proposal_id": 1}, {"background": true}); 37 | db.tx_common.createIndex({"type": -1, "time": -1, "height": -1}, {"background": true}); 38 | 39 | db.proposal.createIndex({"proposal_id": 1}, {"unique": true}); 40 | db.proposal.createIndex({"status": 1}, {"background": true}); 41 | db.proposal.createIndex({"voting_end_time": 1, "deposit_end_time": 1, "status": 1}, {"background": true}); 42 | 43 | // db.tx_msg.createIndex({"hash": 1}, {"unique": true}); 44 | 45 | // init data 46 | db.sync_conf.insert({"block_num_per_worker_handle": 100, "max_worker_sleep_time": 120}); 47 | 48 | // drop collection 49 | // db.account.drop(); 50 | // db.block.drop(); 51 | // db.proposal.drop(); 52 | // db.sync_task.drop(); 53 | // db.tx_common.drop(); 54 | // db.tx_msg.drop(); 55 | // db.mgo_txn.drop(); 56 | // db.mgo_txn.stash.drop(); 57 | 58 | // remove collection data 59 | // db.account.remove({}); 60 | // db.block.remove({}); 61 | // db.proposal.remove({}); 62 | // db.sync_task.remove({}); 63 | // db.tx_common.remove({}); 64 | // db.tx_msg.remove({}); 65 | // db.mgo_txn.remove({}); 66 | // db.mgo_txn.stash.remove({}); 67 | -------------------------------------------------------------------------------- /service/handler/account.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store/document" 5 | "github.com/irisnet/irishub-sync/util/constant" 6 | "github.com/irisnet/irishub-sync/logger" 7 | "github.com/irisnet/irishub-sync/types/msg" 8 | "github.com/irisnet/irishub-sync/util/helper" 9 | "encoding/json" 10 | ) 11 | 12 | //when new address not found or the tx is not success,this address will not be collected 13 | func saveNewAccount(tx *document.CommonTx) { 14 | var accountModel document.Account 15 | if document.TxStatusSuccess != tx.Status { 16 | return 17 | } 18 | switch tx.Type { 19 | case constant.TxTypeTransfer: 20 | accountModel.Address = tx.To 21 | case constant.TxTypeAddTrustee: 22 | if len(tx.Msgs) > 0 { 23 | msgData := msg.DocTxMsgAddTrustee{} 24 | if err := json.Unmarshal([]byte(helper.ToJson(tx.Msgs[0].Msg)), &msgData); err == nil { 25 | accountModel.Address = msgData.Address 26 | } 27 | } 28 | 29 | case constant.TxTypeSetWithdrawAddress: 30 | if len(tx.Msgs) > 0 { 31 | msgData := msg.DocTxMsgSetWithdrawAddress{} 32 | if err := json.Unmarshal([]byte(helper.ToJson(tx.Msgs[0].Msg)), &msgData); err == nil { 33 | accountModel.Address = msgData.WithdrawAddr 34 | } 35 | } 36 | } 37 | if accountModel.Address == "" { 38 | return 39 | } 40 | if err := accountModel.SaveAddress(accountModel.Address); err != nil { 41 | logger.Warn("Save new account address failed", logger.String("err", err.Error())) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /service/handler/block.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/irisnet/irishub-sync/logger" 6 | "github.com/irisnet/irishub-sync/store/document" 7 | "github.com/irisnet/irishub-sync/types" 8 | itypes "github.com/irisnet/irishub-sync/types" 9 | "github.com/irisnet/irishub-sync/util/helper" 10 | "strings" 11 | "github.com/irisnet/irishub-sync/store" 12 | ) 13 | 14 | var ( 15 | assetDetailTriggers = map[string]bool{ 16 | "stakeEndBlocker": true, 17 | "slashBeginBlocker": true, 18 | "slashEndBlocker": true, 19 | "govEndBlocker": true, 20 | } 21 | 22 | bech32AccountAddrPrefix = itypes.Bech32AccountAddrPrefix 23 | ) 24 | 25 | const ( 26 | triggerTxHashLength = 64 27 | separator = "::" // tag value separator 28 | unDelegationSubject = "Undelegation" 29 | ) 30 | 31 | func ParseBlock(meta *types.BlockMeta, block *types.Block, validators []*types.Validator) document.Block { 32 | cdc := types.GetCodec() 33 | 34 | hexFunc := func(bytes []byte) string { 35 | return helper.BuildHex(bytes) 36 | } 37 | 38 | docBlock := document.Block{ 39 | Height: meta.Header.Height, 40 | Hash: hexFunc(meta.BlockID.Hash), 41 | Time: meta.Header.Time, 42 | NumTxs: meta.Header.NumTxs, 43 | ProposalAddress: block.Header.ProposerAddress.String(), 44 | } 45 | 46 | lastBlockId := document.BlockID{ 47 | Hash: hexFunc(meta.Header.LastBlockID.Hash), 48 | PartsHeader: document.PartSetHeader{ 49 | Total: meta.Header.LastBlockID.PartsHeader.Total, 50 | Hash: hexFunc(meta.Header.LastBlockID.PartsHeader.Hash), 51 | }, 52 | } 53 | 54 | // blockMeta 55 | blockMeta := document.BlockMeta{ 56 | BlockID: document.BlockID{ 57 | Hash: hexFunc(meta.BlockID.Hash), 58 | PartsHeader: document.PartSetHeader{ 59 | Total: meta.BlockID.PartsHeader.Total, 60 | Hash: hexFunc(meta.BlockID.PartsHeader.Hash), 61 | }, 62 | }, 63 | Header: document.Header{ 64 | ChainID: meta.Header.ChainID, 65 | Height: meta.Header.Height, 66 | Time: meta.Header.Time, 67 | NumTxs: meta.Header.NumTxs, 68 | LastBlockID: lastBlockId, 69 | TotalTxs: meta.Header.TotalTxs, 70 | LastCommitHash: hexFunc(meta.Header.LastCommitHash), 71 | DataHash: hexFunc(meta.Header.DataHash), 72 | ValidatorsHash: hexFunc(meta.Header.ValidatorsHash), 73 | ConsensusHash: hexFunc(meta.Header.ConsensusHash), 74 | AppHash: hexFunc(meta.Header.AppHash), 75 | LastResultsHash: hexFunc(meta.Header.LastResultsHash), 76 | EvidenceHash: hexFunc(meta.Header.EvidenceHash), 77 | }, 78 | } 79 | 80 | // block 81 | var ( 82 | preCommits []document.Vote 83 | ) 84 | 85 | if len(block.LastCommit.Precommits) > 0 { 86 | for _, v := range block.LastCommit.Precommits { 87 | if v != nil { 88 | var sig document.Signature 89 | out, _ := cdc.MarshalJSON(v.Signature) 90 | json.Unmarshal(out, &sig) 91 | preCommit := document.Vote{ 92 | ValidatorAddress: v.ValidatorAddress.String(), 93 | ValidatorIndex: v.ValidatorIndex, 94 | Height: v.Height, 95 | Round: v.Round, 96 | Timestamp: v.Timestamp, 97 | Type: byte(v.Type), 98 | BlockID: lastBlockId, 99 | Signature: sig, 100 | } 101 | preCommits = append(preCommits, preCommit) 102 | } 103 | } 104 | } 105 | 106 | blockContent := document.BlockContent{ 107 | LastCommit: document.Commit{ 108 | BlockID: lastBlockId, 109 | Precommits: preCommits, 110 | }, 111 | } 112 | 113 | // validators 114 | var vals []document.Validator 115 | if len(validators) > 0 { 116 | for _, v := range validators { 117 | validator := document.Validator{ 118 | Address: v.Address.String(), 119 | VotingPower: v.VotingPower, 120 | PubKey: hexFunc(v.PubKey.Bytes()), 121 | } 122 | vals = append(vals, validator) 123 | } 124 | } 125 | 126 | docBlock.Meta = blockMeta 127 | docBlock.Block = blockContent 128 | docBlock.Validators = vals 129 | docBlock.Result = parseBlockResult(docBlock.Height) 130 | 131 | if proposalId,ok := IsContainVotingEndTag(docBlock.Result.EndBlock);ok { 132 | if proposal,err := document.QueryProposal(proposalId);err == nil { 133 | proposal.VotingEndHeight = docBlock.Height 134 | store.SaveOrUpdate(proposal) 135 | }else{ 136 | logger.Error("QueryProposal fail", logger.Int64("block", docBlock.Height), 137 | logger.String("err", err.Error())) 138 | } 139 | } 140 | 141 | return docBlock 142 | } 143 | 144 | func parseBlockResult(height int64) (res document.BlockResults) { 145 | client := helper.GetClient() 146 | defer client.Release() 147 | 148 | result, err := client.BlockResults(&height) 149 | if err != nil { 150 | // try again 151 | var err2 error 152 | client2 := helper.GetClient() 153 | result, err2 = client2.BlockResults(&height) 154 | client2.Release() 155 | if err2 != nil { 156 | logger.Error("parse block result fail", logger.Int64("block", height), 157 | logger.String("err", err.Error())) 158 | return document.BlockResults{} 159 | } 160 | } 161 | 162 | var deliverTxRes []document.ResponseDeliverTx 163 | for _, tx := range result.Results.DeliverTx { 164 | deliverTxRes = append(deliverTxRes, document.ResponseDeliverTx{ 165 | Code: tx.Code, 166 | Data: string(tx.Data), 167 | Log: tx.Log, 168 | GasWanted: tx.GasWanted, 169 | GasUsed: tx.GasUsed, 170 | Tags: parseTags(tx.Tags), 171 | }) 172 | } 173 | 174 | res.DeliverTx = deliverTxRes 175 | 176 | var validatorUpdates []document.ValidatorUpdate 177 | for _, tx := range result.Results.EndBlock.ValidatorUpdates { 178 | validatorUpdates = append(validatorUpdates, document.ValidatorUpdate{ 179 | PubKey: tx.PubKey.String(), 180 | Power: tx.Power, 181 | }) 182 | } 183 | 184 | var consensusParamUpdates document.ConsensusParams 185 | var tmConsensusParamUpdates = result.Results.EndBlock.ConsensusParamUpdates 186 | if tmConsensusParamUpdates != nil { 187 | if tmConsensusParamUpdates.Validator != nil { 188 | consensusParamUpdates.Validator = document.ValidatorParams{ 189 | PubKeyTypes: tmConsensusParamUpdates.Validator.PubKeyTypes, 190 | } 191 | } 192 | if tmConsensusParamUpdates.BlockSize != nil { 193 | consensusParamUpdates.BlockSize = document.BlockSizeParams{ 194 | MaxBytes: tmConsensusParamUpdates.BlockSize.MaxBytes, 195 | MaxGas: tmConsensusParamUpdates.BlockSize.MaxGas, 196 | } 197 | } 198 | 199 | if tmConsensusParamUpdates.Evidence != nil { 200 | consensusParamUpdates.Evidence = document.EvidenceParams{ 201 | MaxAge: tmConsensusParamUpdates.Evidence.MaxAge, 202 | } 203 | } 204 | } 205 | 206 | res.EndBlock = document.ResponseEndBlock{ 207 | ValidatorUpdates: validatorUpdates, 208 | ConsensusParamUpdates: consensusParamUpdates, 209 | Tags: parseTags(result.Results.EndBlock.Tags), 210 | } 211 | 212 | res.BeginBlock = document.ResponseBeginBlock{ 213 | Tags: parseTags(result.Results.BeginBlock.Tags), 214 | } 215 | 216 | return res 217 | } 218 | 219 | func parseTags(tags []types.TmKVPair) (response []document.KvPair) { 220 | for _, tag := range tags { 221 | key := string(tag.Key) 222 | value := string(tag.Value) 223 | response = append(response, document.KvPair{Key: key, Value: value}) 224 | } 225 | return response 226 | } 227 | 228 | // parse accounts from coin flow which in block result 229 | // return two kind accounts 230 | // 1. accounts which balance info need updated 231 | // 2. accounts which unbondingDelegation info need updated 232 | func getAccountsFromCoinFlow(endBlockTags []document.KvPair, height int64) ([]string, []string) { 233 | var ( 234 | accsBalanceNeedUpdated, accsUnbondingDelegationNeedUpdated []string 235 | ) 236 | balanceAccountExistMap := make(map[string]bool) 237 | unbondingDelegationAccountExistMap := make(map[string]bool) 238 | 239 | getDistinctAccsBalanceNeedUpdated := func(address string) { 240 | if strings.HasPrefix(address, bech32AccountAddrPrefix) && !balanceAccountExistMap[address] { 241 | balanceAccountExistMap[address] = true 242 | accsBalanceNeedUpdated = append(accsBalanceNeedUpdated, address) 243 | } 244 | } 245 | getDistinctAccsUnbondingDelegationNeedUpdated := func(address string) { 246 | if strings.HasPrefix(address, bech32AccountAddrPrefix) && !unbondingDelegationAccountExistMap[address] { 247 | unbondingDelegationAccountExistMap[address] = true 248 | accsUnbondingDelegationNeedUpdated = append(accsUnbondingDelegationNeedUpdated, address) 249 | } 250 | } 251 | 252 | for _, t := range endBlockTags { 253 | tagKey := string(t.Key) 254 | tagValue := string(t.Value) 255 | 256 | if assetDetailTriggers[tagKey] || len(tagKey) == triggerTxHashLength { 257 | values := strings.Split(tagValue, separator) 258 | if len(values) != 6 { 259 | logger.Warn("struct of iris coin flow changed in block result, skip parse this block coin flow", 260 | logger.Int64("height", height), logger.String("tagKey", tagKey)) 261 | continue 262 | } 263 | 264 | // parse coin flow address from and to, from: value[0], to: value[1] 265 | from := values[0] 266 | to := values[1] 267 | getDistinctAccsBalanceNeedUpdated(from) 268 | getDistinctAccsBalanceNeedUpdated(to) 269 | 270 | // unbondingDelegation tx complete, need to update account unbondingDelegation info 271 | if values[3] == unDelegationSubject { 272 | getDistinctAccsUnbondingDelegationNeedUpdated(from) 273 | getDistinctAccsUnbondingDelegationNeedUpdated(to) 274 | } 275 | } 276 | } 277 | 278 | return accsBalanceNeedUpdated, accsUnbondingDelegationNeedUpdated 279 | } 280 | -------------------------------------------------------------------------------- /service/handler/block_test.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/irisnet/irishub-sync/logger" 9 | "github.com/irisnet/irishub-sync/types" 10 | "github.com/irisnet/irishub-sync/util/helper" 11 | ) 12 | 13 | func buildBlock(blockHeight int64) (*types.BlockMeta, *types.Block, []*types.Validator) { 14 | 15 | client := helper.GetClient() 16 | // release client 17 | defer client.Release() 18 | 19 | block, err := client.Client.Block(&blockHeight) 20 | 21 | if err != nil { 22 | logger.Error(err.Error()) 23 | } 24 | 25 | validators, err := client.Client.Validators(&blockHeight) 26 | if err != nil { 27 | logger.Error(err.Error()) 28 | } 29 | 30 | return block.BlockMeta, block.Block, validators.Validators 31 | } 32 | 33 | func TestForEach(t *testing.T) { 34 | var i int 35 | var arr = []string{"1", "2", "3"} 36 | for i = range arr { 37 | fmt.Println(fmt.Sprintf("a[%d] = %s", i, arr[i])) 38 | if arr[i] == "2" { 39 | break 40 | } 41 | 42 | } 43 | fmt.Println(fmt.Sprintf("a[%d]", i)) 44 | } 45 | 46 | func TestParseBlockResult(t *testing.T) { 47 | v := parseBlockResult(213637) 48 | bz, _ := json.Marshal(v) 49 | fmt.Println(string(bz)) 50 | } 51 | 52 | func TestParseBlock(t *testing.T) { 53 | blockHeight := int64(88) 54 | 55 | client := helper.GetClient() 56 | defer client.Release() 57 | 58 | if res, err := client.Block(&blockHeight); err != nil { 59 | t.Fatal(err) 60 | } else { 61 | var validators []*types.Validator 62 | valRes, err := client.Validators(&blockHeight) 63 | if err != nil { 64 | t.Error(err) 65 | } else { 66 | validators = valRes.Validators 67 | } 68 | blockDoc := ParseBlock(res.BlockMeta, res.Block, validators) 69 | 70 | resBytes, _ := json.MarshalIndent(blockDoc, "", "\t") 71 | t.Log(string(resBytes)) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /service/handler/proposal.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "github.com/irisnet/irishub-sync/store/document" 6 | "github.com/irisnet/irishub-sync/util/constant" 7 | "github.com/irisnet/irishub-sync/util/helper" 8 | "strconv" 9 | ) 10 | 11 | func handleProposal(docTx document.CommonTx) { 12 | switch docTx.Type { 13 | case constant.TxTypeSubmitProposal: 14 | if proposal, err := helper.GetProposal(docTx.ProposalId); err == nil { 15 | if isContainVotingPeriodStartTag(docTx) { 16 | proposal.VotingPeriodStartHeight = docTx.Height 17 | } 18 | store.SaveOrUpdate(proposal) 19 | } 20 | case constant.TxTypeDeposit: 21 | if proposal, err := document.QueryProposal(docTx.ProposalId); err == nil { 22 | propo, _ := helper.GetProposal(docTx.ProposalId) 23 | if isContainVotingPeriodStartTag(docTx) { 24 | proposal.VotingPeriodStartHeight = docTx.Height 25 | } 26 | proposal.TotalDeposit = propo.TotalDeposit 27 | proposal.Status = propo.Status 28 | proposal.VotingStartTime = propo.VotingStartTime 29 | proposal.VotingEndTime = propo.VotingEndTime 30 | store.SaveOrUpdate(proposal) 31 | } 32 | //case constant.TxTypeVote: 33 | // //失败的投票不计入统计 34 | // if docTx.Status == document.TxStatusFail { 35 | // return 36 | // } 37 | // if proposal, err := document.QueryProposal(docTx.ProposalId); err == nil { 38 | // msgVote := msg.DocTxMsgVote{} 39 | // err := msgVote.BuildMsgByUnmarshalJson(utils.MarshalJsonIgnoreErr(docTx.Msgs[0].Msg)) 40 | // if err != nil { 41 | // logger.Warn("BuildMsgByUnmarshalJson DocTxMsgVote have fail", logger.String("err", err.Error())) 42 | // } 43 | // vote := document.PVote{ 44 | // Voter: msgVote.Voter, 45 | // Option: msgVote.Option, 46 | // TxHash: docTx.TxHash, 47 | // Time: docTx.Time, 48 | // } 49 | // var i int 50 | // var hasVote = false 51 | // for i = range proposal.Votes { 52 | // if proposal.Votes[i].Voter == vote.Voter { 53 | // hasVote = true 54 | // break 55 | // } 56 | // } 57 | // if hasVote { 58 | // proposal.Votes[i] = vote 59 | // } else { 60 | // proposal.Votes = append(proposal.Votes, vote) 61 | // } 62 | // store.SaveOrUpdate(proposal) 63 | // } 64 | } 65 | } 66 | 67 | func isContainVotingPeriodStartTag(docTx document.CommonTx) bool { 68 | tags := docTx.Tags 69 | if len(tags) > 0 { 70 | for k, _ := range tags { 71 | if k == constant.TxTagVotingPeriodStart { 72 | return true 73 | } 74 | } 75 | } 76 | 77 | return false 78 | } 79 | 80 | func IsContainVotingEndTag(blockresult document.ResponseEndBlock) (uint64, bool) { 81 | tags := blockresult.Tags 82 | if len(tags) > 0 { 83 | for _, tag := range tags { 84 | if tag.Key == constant.BlockTagProposalId { 85 | proposalid, _ := strconv.ParseUint(tag.Value, 10, 64) 86 | return proposalid, true 87 | } 88 | } 89 | } 90 | 91 | return 0, false 92 | } 93 | -------------------------------------------------------------------------------- /service/handler/proposal_test.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "github.com/irisnet/irishub-sync/store/document" 6 | "gopkg.in/mgo.v2" 7 | "gopkg.in/mgo.v2/bson" 8 | "testing" 9 | ) 10 | 11 | func TestIsContainVotingPeriodStartTag(t *testing.T) { 12 | txHash := "37A0127A87AA68BFE73D03C2B9A2A6A3D8E51DF242D86C845DB2D158B1617502" 13 | 14 | var tx document.CommonTx 15 | fn := func(c *mgo.Collection) error { 16 | q := bson.M{"tx_hash": txHash} 17 | return c.Find(q).One(&tx) 18 | } 19 | 20 | if err := store.ExecCollection(tx.Name(), fn); err != nil { 21 | t.Fatal(err) 22 | } else { 23 | res := isContainVotingPeriodStartTag(tx) 24 | t.Log(res) 25 | } 26 | } 27 | 28 | func TestHandleProposal(t *testing.T) { 29 | txHash := "37A0127A87AA68BFE73D03C2B9A2A6A3D8E51DF242D86C845DB2D158B1617502" 30 | 31 | var tx document.CommonTx 32 | fn := func(c *mgo.Collection) error { 33 | q := bson.M{"tx_hash": txHash} 34 | return c.Find(q).One(&tx) 35 | } 36 | 37 | if err := store.ExecCollection(tx.Name(), fn); err != nil { 38 | t.Fatal(err) 39 | } else { 40 | handleProposal(tx) 41 | t.Log("success") 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /service/handler/tx.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "github.com/irisnet/irishub-sync/store/document" 6 | "github.com/irisnet/irishub-sync/types" 7 | "github.com/irisnet/irishub-sync/util/helper" 8 | "gopkg.in/mgo.v2/bson" 9 | "gopkg.in/mgo.v2/txn" 10 | ) 11 | 12 | func HandleTx(block *types.Block) (error) { 13 | var ( 14 | batch []txn.Op 15 | ) 16 | 17 | for _, txByte := range block.Txs { 18 | tx := helper.ParseTx(txByte, block) 19 | 20 | // batch insert tx 21 | txOp := txn.Op{ 22 | C: document.CollectionNmCommonTx, 23 | Id: bson.NewObjectId(), 24 | Insert: tx, 25 | } 26 | batch = append(batch, txOp) 27 | 28 | // save or update proposal 29 | handleProposal(tx) 30 | //handleTokenFlow(blockWithTags, tx, &batch) 31 | 32 | // save new account address 33 | saveNewAccount(&tx) 34 | } 35 | 36 | if len(batch) > 0 { 37 | err := store.Txn(batch) 38 | if err != nil { 39 | return err 40 | } 41 | } 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /service/handler/tx_test.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/irisnet/irishub-sync/logger" 7 | "github.com/irisnet/irishub-sync/store" 8 | "github.com/irisnet/irishub-sync/store/document" 9 | "github.com/irisnet/irishub-sync/util/helper" 10 | "os" 11 | ) 12 | 13 | const ( 14 | BankHeight = 17694 15 | StakeCreateHeight = 28848 16 | StakeEditHeight = 28581 17 | StakeDelegateHeight = 79026 18 | StakeBeginUnbondingHeight = 79063 19 | StakeCompleteUnbondingHeight = 79177 20 | ) 21 | 22 | func TestMain(m *testing.M) { 23 | // setup 24 | store.Start() 25 | 26 | code := m.Run() 27 | 28 | // shutdown 29 | os.Exit(code) 30 | } 31 | 32 | func buildDocData(blockHeight int64) document.CommonTx { 33 | client := helper.GetClient() 34 | // release client 35 | defer client.Release() 36 | 37 | block, err := client.Client.Block(&blockHeight) 38 | 39 | if err != nil { 40 | logger.Panic(err.Error()) 41 | } 42 | 43 | if block.BlockMeta.Header.NumTxs > 0 { 44 | txs := block.Block.Data.Txs 45 | txByte := txs[0] 46 | docTx := helper.ParseTx(txByte, block.Block) 47 | 48 | return docTx 49 | 50 | } 51 | return document.CommonTx{} 52 | } 53 | -------------------------------------------------------------------------------- /service/handler/types.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/logger" 5 | "github.com/irisnet/irishub-sync/store/document" 6 | "sync" 7 | ) 8 | 9 | // get tx type 10 | func GetTxType(docTx document.CommonTx) string { 11 | if docTx.TxHash == "" { 12 | return "" 13 | } 14 | return docTx.Type 15 | } 16 | 17 | type Action = func(tx document.CommonTx, mutex sync.Mutex) 18 | 19 | func Handle(docTx document.CommonTx, mutex sync.Mutex, actions []Action) { 20 | defer func() { 21 | if err := recover(); err != nil { 22 | logger.Error("Parse Tx failed", logger.Int64("height", docTx.Height), 23 | logger.String("txHash", docTx.TxHash), logger.Any("err", err)) 24 | } 25 | }() 26 | 27 | for _, action := range actions { 28 | if docTx.TxHash != "" { 29 | action(docTx, mutex) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /service/sync.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/logger" 5 | "github.com/irisnet/irishub-sync/service/task" 6 | "github.com/robfig/cron" 7 | "time" 8 | ) 9 | 10 | var ( 11 | engine *SyncEngine 12 | ) 13 | 14 | func init() { 15 | engine = &SyncEngine{ 16 | cron: cron.New(), 17 | tasks: []task.Task{}, 18 | initFuncs: []func(){}, 19 | } 20 | 21 | engine.AddTask(task.MakeSyncProposalStatusTask()) 22 | } 23 | 24 | type SyncEngine struct { 25 | cron *cron.Cron //cron 26 | tasks []task.Task // my timer task 27 | initFuncs []func() // module init fun 28 | } 29 | 30 | func (engine *SyncEngine) AddTask(task task.Task) { 31 | engine.tasks = append(engine.tasks, task) 32 | engine.cron.AddFunc(task.GetCron(), task.GetCommand()) 33 | } 34 | 35 | func (engine *SyncEngine) Start() { 36 | // init module info 37 | for _, init := range engine.initFuncs { 38 | init() 39 | } 40 | go task.StartCreateTask() 41 | go task.StartExecuteTask() 42 | 43 | // cron task should start after fast sync finished 44 | fastSyncChan := make(chan bool, 1) 45 | ticker := time.NewTicker(1 * time.Minute) 46 | go func() { 47 | for { 48 | <-ticker.C 49 | flag, err := task.AssertFastSyncFinished() 50 | if err != nil { 51 | logger.Error("assert fast sync finished failed", logger.String("err", err.Error())) 52 | } 53 | if flag { 54 | close(fastSyncChan) 55 | return 56 | } 57 | } 58 | }() 59 | <-fastSyncChan 60 | logger.Info("fast sync finished, now cron task can start") 61 | 62 | engine.cron.Start() 63 | } 64 | 65 | func (engine *SyncEngine) Stop() { 66 | logger.Info("release resource :SyncEngine") 67 | engine.cron.Stop() 68 | } 69 | 70 | func New() *SyncEngine { 71 | return engine 72 | } 73 | -------------------------------------------------------------------------------- /service/sync_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/robfig/cron" 5 | "testing" 6 | 7 | conf "github.com/irisnet/irishub-sync/conf/server" 8 | 9 | "github.com/irisnet/irishub-sync/logger" 10 | "sync" 11 | ) 12 | 13 | func TestStart(t *testing.T) { 14 | var ( 15 | limitChan chan int 16 | unBufferChan chan int 17 | ) 18 | limitChan = make(chan int, 3) 19 | unBufferChan = make(chan int) 20 | goroutineNum := 5 21 | activeGoroutineNum := goroutineNum 22 | for i := 1; i <= goroutineNum; i++ { 23 | limitChan <- i 24 | go func(goroutineNum int, ch chan int) { 25 | logger.Info("release limitChan") 26 | <-limitChan 27 | defer func() { 28 | logger.Info("%v goroutine send data to channel") 29 | ch <- goroutineNum 30 | }() 31 | 32 | }(i, nil) 33 | } 34 | 35 | for { 36 | select { 37 | case <-unBufferChan: 38 | activeGoroutineNum = activeGoroutineNum - 1 39 | logger.Info("active goroutine num is %v") 40 | if activeGoroutineNum == 0 { 41 | logger.Info("All goroutine complete") 42 | break 43 | } 44 | } 45 | } 46 | 47 | } 48 | 49 | func Test_startCron(t *testing.T) { 50 | var wg sync.WaitGroup 51 | wg.Add(2) 52 | 53 | c := cron.New() 54 | c.AddFunc(conf.CronCalculateUpTime, func() { 55 | logger.Info("every one minute execute code") 56 | }) 57 | c.AddFunc(conf.CronCalculateTxGas, func() { 58 | logger.Info("every five minute execute code") 59 | }) 60 | go c.Start() 61 | 62 | wg.Wait() 63 | } 64 | -------------------------------------------------------------------------------- /service/task/sync_proposal_status_task.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | conf "github.com/irisnet/irishub-sync/conf/server" 5 | "github.com/irisnet/irishub-sync/logger" 6 | "github.com/irisnet/irishub-sync/store" 7 | "github.com/irisnet/irishub-sync/store/document" 8 | "github.com/irisnet/irishub-sync/util/constant" 9 | "github.com/irisnet/irishub-sync/util/helper" 10 | ) 11 | 12 | func syncProposalStatus() { 13 | var status = []string{constant.StatusDepositPeriod, constant.StatusVotingPeriod} 14 | if proposals, err := document.QueryByStatus(status); err == nil { 15 | for _, proposal := range proposals { 16 | propo, err := helper.GetProposal(proposal.ProposalId) 17 | if err != nil { 18 | store.Delete(proposal) 19 | return 20 | } 21 | if propo.Status != proposal.Status { 22 | propo.SubmitTime = proposal.SubmitTime 23 | propo.Votes = proposal.Votes 24 | propo.VotingPeriodStartHeight = proposal.VotingPeriodStartHeight 25 | store.SaveOrUpdate(propo) 26 | } 27 | } 28 | } 29 | } 30 | 31 | func MakeSyncProposalStatusTask() Task { 32 | return NewLockTaskFromEnv(conf.SyncProposalStatus, func() { 33 | logger.Debug("========================task's trigger [SyncProposalStatus] begin===================") 34 | syncProposalStatus() 35 | logger.Debug("========================task's trigger [SyncProposalStatus] end===================") 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /service/task/task_create.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "fmt" 5 | serverConf "github.com/irisnet/irishub-sync/conf/server" 6 | "github.com/irisnet/irishub-sync/logger" 7 | "github.com/irisnet/irishub-sync/store" 8 | "github.com/irisnet/irishub-sync/store/document" 9 | "github.com/irisnet/irishub-sync/util/helper" 10 | "gopkg.in/mgo.v2/bson" 11 | "gopkg.in/mgo.v2/txn" 12 | "time" 13 | ) 14 | 15 | const maxRecordNumForBatchInsert = 1000 16 | 17 | func StartCreateTask() { 18 | log := logger.GetLogger("StartCreateTask") 19 | var ( 20 | syncConfModel document.SyncConf 21 | blockNumPerWorkerHandle int64 22 | ) 23 | 24 | // get sync conf 25 | syncConf, err := syncConfModel.GetConf() 26 | if err != nil { 27 | log.Fatal("Get sync conf failed", logger.String("err", err.Error())) 28 | } 29 | blockNumPerWorkerHandle = syncConf.BlockNumPerWorkerHandle 30 | if blockNumPerWorkerHandle <= 0 { 31 | log.Fatal("blockNumPerWorkerHandle should greater than 0") 32 | } 33 | 34 | log.Info("Start create task", logger.Any("sync conf", syncConf)) 35 | 36 | // buffer channel to limit goroutine num 37 | chanLimit := make(chan bool, serverConf.WorkerNumCreateTask) 38 | 39 | for { 40 | chanLimit <- true 41 | go createTask(blockNumPerWorkerHandle, chanLimit) 42 | time.Sleep(time.Duration(1) * time.Minute) 43 | } 44 | } 45 | 46 | func createTask(blockNumPerWorker int64, chanLimit chan bool) { 47 | var ( 48 | syncTaskModel document.SyncTask 49 | syncTasks []document.SyncTask 50 | ops []txn.Op 51 | invalidFollowTask document.SyncTask 52 | logMsg string 53 | ) 54 | log := logger.GetLogger("CreateTask") 55 | 56 | defer func() { 57 | if err := recover(); err != nil { 58 | log.Error("Create task failed", logger.Any("err", err)) 59 | } 60 | <-chanLimit 61 | }() 62 | 63 | // check valid follow task if exist 64 | // status of valid follow task is unhandled or underway 65 | validFollowTasks, err := syncTaskModel.QueryAll( 66 | []string{ 67 | document.SyncTaskStatusUnHandled, 68 | document.SyncTaskStatusUnderway, 69 | }, document.SyncTaskTypeFollow) 70 | if err != nil { 71 | log.Error("Query task failed", logger.String("err", err.Error())) 72 | return 73 | } 74 | if len(validFollowTasks) == 0 { 75 | // get max end_height from sync_task 76 | maxEndHeight, err := syncTaskModel.GetMaxBlockHeight() 77 | if err != nil { 78 | log.Error("Get task max endBlock failed", logger.String("err", err.Error())) 79 | return 80 | } 81 | 82 | blockChainLatestHeight, err := getBlockChainLatestHeight() 83 | if err != nil { 84 | log.Error("Get blockchain latest height failed", logger.String("err", err.Error())) 85 | return 86 | } 87 | 88 | if maxEndHeight+blockNumPerWorker <= blockChainLatestHeight { 89 | syncTasks = createCatchUpTask(maxEndHeight, blockNumPerWorker, blockChainLatestHeight) 90 | logMsg = fmt.Sprintf("Create catch up task during follow task not exist, from: %v, to: %v", 91 | maxEndHeight+1, blockChainLatestHeight) 92 | } else { 93 | finished, err := assertAllCatchUpTaskFinished() 94 | if err != nil { 95 | log.Error("AssertAllCatchUpTaskFinished failed", logger.String("err", err.Error())) 96 | return 97 | } 98 | if finished { 99 | syncTasks = createFollowTask(maxEndHeight, blockNumPerWorker, blockChainLatestHeight) 100 | logMsg = fmt.Sprintf("Create follow task during follow task not exist, from: %v, blockChainLatestHeight: %v", 101 | maxEndHeight+1, blockChainLatestHeight) 102 | } 103 | } 104 | } else { 105 | followTask := validFollowTasks[0] 106 | followedHeight := followTask.CurrentHeight 107 | if followedHeight == 0 { 108 | followedHeight = followTask.StartHeight - 1 109 | } 110 | 111 | blockChainLatestHeight, err := getBlockChainLatestHeight() 112 | if err != nil { 113 | log.Error("Get blockchain latest height failed", logger.String("err", err.Error())) 114 | return 115 | } 116 | 117 | if followedHeight+blockNumPerWorker <= blockChainLatestHeight { 118 | syncTasks = createCatchUpTask(followedHeight, blockNumPerWorker, blockChainLatestHeight) 119 | invalidFollowTask = followTask 120 | logMsg = fmt.Sprintf("Create catch up task during follow task exis, "+ 121 | "from: %v, to: %v, invalidFollowTaskId: %v, invalidFollowTaskCurrentHeight: %v", 122 | followedHeight+1, blockChainLatestHeight, invalidFollowTask.ID.Hex(), invalidFollowTask.CurrentHeight) 123 | } 124 | } 125 | 126 | // bulk insert or remove use transaction 127 | if len(syncTasks) > 0 { 128 | for _, v := range syncTasks { 129 | objectId := bson.NewObjectId() 130 | v.ID = objectId 131 | op := txn.Op{ 132 | C: document.CollectionNameSyncTask, 133 | Id: objectId, 134 | Assert: nil, 135 | Insert: v, 136 | } 137 | 138 | ops = append(ops, op) 139 | } 140 | } 141 | 142 | if invalidFollowTask.ID.Valid() { 143 | op := txn.Op{ 144 | C: document.CollectionNameSyncTask, 145 | Id: invalidFollowTask.ID, 146 | Assert: bson.M{ 147 | "current_height": invalidFollowTask.CurrentHeight, 148 | "last_update_time": invalidFollowTask.LastUpdateTime, 149 | }, 150 | Update: bson.M{ 151 | "$set": bson.M{ 152 | "status": document.FollowTaskStatusInvalid, 153 | "last_update_time": time.Now().Unix(), 154 | }, 155 | }, 156 | } 157 | ops = append(ops, op) 158 | } 159 | 160 | if len(ops) > 0 { 161 | err := store.Txn(ops) 162 | if err != nil { 163 | log.Warn("Create task fail", logger.String("err", err.Error())) 164 | } else { 165 | log.Info(fmt.Sprintf("Create task success, %v", logMsg)) 166 | } 167 | } 168 | } 169 | 170 | // get blockchain latest height 171 | func getBlockChainLatestHeight() (int64, error) { 172 | client := helper.GetClient() 173 | defer func() { 174 | client.Release() 175 | }() 176 | status, err := client.Status() 177 | if err != nil { 178 | return 0, err 179 | } 180 | currentBlockHeight := status.SyncInfo.LatestBlockHeight 181 | 182 | return currentBlockHeight, nil 183 | } 184 | 185 | // limit max num for batch insert 186 | func createCatchUpTask(maxEndHeight, blockNumPerWorker, blockChainLatestHeight int64) []document.SyncTask { 187 | var ( 188 | syncTasks []document.SyncTask 189 | ) 190 | 191 | for maxEndHeight+blockNumPerWorker <= blockChainLatestHeight { 192 | if len(syncTasks) >= maxRecordNumForBatchInsert { 193 | break 194 | } 195 | syncTask := document.SyncTask{ 196 | StartHeight: maxEndHeight + 1, 197 | EndHeight: maxEndHeight + blockNumPerWorker, 198 | Status: document.SyncTaskStatusUnHandled, 199 | LastUpdateTime: time.Now().Unix(), 200 | } 201 | syncTasks = append(syncTasks, syncTask) 202 | 203 | maxEndHeight += blockNumPerWorker 204 | } 205 | 206 | return syncTasks 207 | } 208 | 209 | func assertAllCatchUpTaskFinished() (bool, error) { 210 | var ( 211 | syncTaskModel document.SyncTask 212 | allCatchUpTaskFinished = false 213 | ) 214 | 215 | // assert all catch up task whether finished 216 | tasks, err := syncTaskModel.QueryAll( 217 | []string{ 218 | document.SyncTaskStatusUnHandled, 219 | document.SyncTaskStatusUnderway, 220 | }, 221 | document.SyncTaskTypeCatchUp) 222 | if err != nil { 223 | return false, err 224 | } 225 | 226 | if len(tasks) == 0 { 227 | allCatchUpTaskFinished = true 228 | } 229 | 230 | return allCatchUpTaskFinished, nil 231 | } 232 | 233 | func createFollowTask(maxEndHeight, blockNumPerWorker, currentBlockHeight int64) []document.SyncTask { 234 | var ( 235 | syncTasks []document.SyncTask 236 | ) 237 | 238 | if maxEndHeight+blockNumPerWorker > currentBlockHeight { 239 | syncTask := document.SyncTask{ 240 | StartHeight: maxEndHeight + 1, 241 | EndHeight: 0, 242 | Status: document.SyncTaskStatusUnHandled, 243 | LastUpdateTime: time.Now().Unix(), 244 | } 245 | 246 | syncTasks = append(syncTasks, syncTask) 247 | } 248 | 249 | return syncTasks 250 | } 251 | -------------------------------------------------------------------------------- /service/task/task_create_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "os" 6 | "sync" 7 | "testing" 8 | ) 9 | 10 | func TestMain(m *testing.M) { 11 | store.Start() 12 | 13 | code := m.Run() 14 | 15 | os.Exit(code) 16 | } 17 | 18 | func Test_createTask(t *testing.T) { 19 | type args struct { 20 | blockNumPerWorker int64 21 | chanLimit chan bool 22 | } 23 | 24 | chanLimit := make(chan bool, 2) 25 | var wg sync.WaitGroup 26 | 27 | tests := []struct { 28 | name string 29 | args args 30 | }{ 31 | { 32 | name: "test create task", 33 | args: args{ 34 | blockNumPerWorker: 100, 35 | chanLimit: chanLimit, 36 | }, 37 | }, 38 | } 39 | for _, tt := range tests { 40 | t.Run(tt.name, func(t *testing.T) { 41 | wg.Add(1) 42 | chanLimit <- true 43 | go createTask(tt.args.blockNumPerWorker, tt.args.chanLimit) 44 | wg.Wait() 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /service/task/task_execute.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | serverConf "github.com/irisnet/irishub-sync/conf/server" 9 | "github.com/irisnet/irishub-sync/logger" 10 | "github.com/irisnet/irishub-sync/service/handler" 11 | "github.com/irisnet/irishub-sync/store" 12 | "github.com/irisnet/irishub-sync/store/document" 13 | "github.com/irisnet/irishub-sync/types" 14 | "github.com/irisnet/irishub-sync/util/helper" 15 | "gopkg.in/mgo.v2" 16 | "gopkg.in/mgo.v2/bson" 17 | "gopkg.in/mgo.v2/txn" 18 | ) 19 | 20 | func StartExecuteTask() { 21 | var ( 22 | syncConfModel document.SyncConf 23 | blockNumPerWorkerHandle int64 24 | maxWorkerSleepTime int64 25 | ) 26 | log := logger.GetLogger("TaskExecutor") 27 | 28 | // get sync conf 29 | syncConf, err := syncConfModel.GetConf() 30 | if err != nil { 31 | log.Fatal("Get sync conf failed", logger.String("err", err.Error())) 32 | } 33 | blockNumPerWorkerHandle = syncConf.BlockNumPerWorkerHandle 34 | if blockNumPerWorkerHandle <= 0 { 35 | log.Fatal("blockNumPerWorkerHandle should greater than 0") 36 | } 37 | maxWorkerSleepTime = syncConf.MaxWorkerSleepTime 38 | if maxWorkerSleepTime <= 60 { 39 | log.Fatal("maxWorkerSleepTime should greater than 60 second") 40 | } 41 | 42 | log.Info("Start execute task", logger.Any("sync conf", syncConf)) 43 | 44 | // buffer channel to limit goroutine num 45 | chanLimit := make(chan bool, serverConf.WorkerNumExecuteTask) 46 | 47 | for { 48 | time.Sleep(time.Duration(1) * time.Second) 49 | chanLimit <- true 50 | go executeTask(blockNumPerWorkerHandle, maxWorkerSleepTime, chanLimit) 51 | } 52 | } 53 | 54 | func executeTask(blockNumPerWorkerHandle, maxWorkerSleepTime int64, chanLimit chan bool) { 55 | var ( 56 | syncTaskModel document.SyncTask 57 | workerId, taskType string 58 | blockChainLatestHeight int64 59 | ) 60 | log := logger.GetLogger("TaskExecutor") 61 | genWorkerId := func() string { 62 | // generate worker id use hostname@xxx 63 | hostname, _ := os.Hostname() 64 | return fmt.Sprintf("%v@%v", hostname, bson.NewObjectId().Hex()) 65 | } 66 | 67 | healthCheckQuit := make(chan bool) 68 | workerId = genWorkerId() 69 | client := helper.GetClient() 70 | 71 | defer func() { 72 | if r := recover(); r != nil { 73 | log.Error("execute task fail", logger.Any("err", r)) 74 | } 75 | close(healthCheckQuit) 76 | <-chanLimit 77 | client.Release() 78 | }() 79 | 80 | // check whether exist executable task 81 | // status = unhandled or 82 | // status = underway and now - lastUpdateTime > confTime 83 | tasks, err := syncTaskModel.GetExecutableTask(maxWorkerSleepTime) 84 | if err != nil { 85 | log.Error("Get executable task fail", logger.String("err", err.Error())) 86 | } 87 | if len(tasks) == 0 { 88 | // there is no executable tasks 89 | return 90 | } 91 | 92 | // take over sync task 93 | // attempt to update status, worker_id and worker_logs 94 | task := tasks[0] 95 | err = syncTaskModel.TakeOverTask(task, workerId) 96 | if err != nil { 97 | if err == mgo.ErrNotFound { 98 | log.Info("Task has been take over by other goroutine") 99 | } else { 100 | log.Error("Take over task fail", logger.String("err", err.Error())) 101 | } 102 | return 103 | } else { 104 | // task over task success, update task worker to current worker 105 | task.WorkerId = workerId 106 | } 107 | 108 | if task.EndHeight != 0 { 109 | taskType = document.SyncTaskTypeCatchUp 110 | } else { 111 | taskType = document.SyncTaskTypeFollow 112 | } 113 | log.Info("worker begin execute task", 114 | logger.String("cur_worker", workerId), logger.String("task_id", task.ID.Hex()), 115 | logger.String("from-to", fmt.Sprintf("%v-%v", task.StartHeight, task.EndHeight))) 116 | 117 | // worker health check, if worker is alive, then update last update time every minute. 118 | // health check will exit in follow conditions: 119 | // 1. task is not owned by current worker 120 | // 2. task is invalid 121 | workerHealthCheck := func(taskId bson.ObjectId, currentWorker string) { 122 | defer func() { 123 | if r := recover(); r != nil { 124 | log.Error("worker health check err", logger.Any("err", r)) 125 | } 126 | }() 127 | 128 | for { 129 | select { 130 | case <-healthCheckQuit: 131 | logger.Info("get health check quit signal, now exit health check") 132 | return 133 | default: 134 | task, err := syncTaskModel.GetTaskByIdAndWorker(taskId, workerId) 135 | if err == nil { 136 | blockChainLatestHeight, err := getBlockChainLatestHeight() 137 | if err == nil { 138 | if assertTaskValid(task, blockNumPerWorkerHandle, blockChainLatestHeight) { 139 | // update task last update time 140 | if err := syncTaskModel.UpdateLastUpdateTime(task); err != nil { 141 | log.Error("update last update time fail", logger.String("err", err.Error()), 142 | logger.String("task_id", task.ID.Hex())) 143 | } 144 | logger.Info("health check success, now sleep one minute", 145 | logger.String("task_id", task.ID.Hex()), 146 | logger.String("task_current_worker", task.WorkerId)) 147 | } else { 148 | log.Info("task is invalid, exit health check", logger.String("task_id", taskId.Hex())) 149 | return 150 | } 151 | } else { 152 | log.Error("get block chain latest height fail", logger.String("err", err.Error())) 153 | } 154 | } else { 155 | if err == mgo.ErrNotFound { 156 | log.Info("task may be task over by other goroutine, exit health check", 157 | logger.String("task_id", taskId.Hex()), logger.String("current_worker", workerId)) 158 | return 159 | } else { 160 | log.Error("get task by id and worker fail", logger.String("task_id", taskId.Hex()), 161 | logger.String("current_worker", workerId)) 162 | } 163 | } 164 | time.Sleep(1 * time.Minute) 165 | } 166 | } 167 | } 168 | go workerHealthCheck(task.ID, workerId) 169 | 170 | // check task is valid 171 | // valid catch up task: current_height < end_height 172 | // valid follow task: current_height + blockNumPerWorkerHandle > blockChainLatestHeight 173 | blockChainLatestHeight, err = getBlockChainLatestHeight() 174 | if err != nil { 175 | log.Error("get block chain latest height fail", logger.String("err", err.Error())) 176 | return 177 | } 178 | for assertTaskValid(task, blockNumPerWorkerHandle, blockChainLatestHeight) { 179 | var inProcessBlock int64 180 | if task.CurrentHeight == 0 { 181 | inProcessBlock = task.StartHeight 182 | } else { 183 | inProcessBlock = task.CurrentHeight + 1 184 | } 185 | 186 | // if task is follow task, 187 | // wait value of blockChainLatestHeight updated when inProcessBlock >= blockChainLatestHeight 188 | if taskType == document.SyncTaskTypeFollow { 189 | blockChainLatestHeight, err = getBlockChainLatestHeight() 190 | if err != nil { 191 | log.Error("get block chain latest height fail", logger.String("err", err.Error())) 192 | return 193 | } 194 | 195 | if task.CurrentHeight+2 >= blockChainLatestHeight { 196 | // wait block chain latest block height updated, must interval two block 197 | log.Info("wait block chain latest block height updated, must interval two block", 198 | logger.String("taskId", task.ID.String()), 199 | logger.String("workerId", task.WorkerId), 200 | logger.Int64("taskCurrentHeight", task.CurrentHeight), 201 | logger.Int64("blockChainLatestHeight", blockChainLatestHeight)) 202 | time.Sleep(2 * time.Second) 203 | continue 204 | } 205 | } 206 | 207 | // parse block and tx 208 | blockDoc, err := parseBlock(inProcessBlock, client) 209 | if err != nil { 210 | log.Error("Parse block fail", logger.Int64("block", inProcessBlock), 211 | logger.String("err", err.Error())) 212 | } 213 | 214 | // check task owner 215 | workerUnchanged, err := assertTaskWorkerUnchanged(task.ID, task.WorkerId) 216 | if err != nil { 217 | log.Error("assert task worker is unchanged fail", logger.String("err", err.Error())) 218 | } 219 | if workerUnchanged { 220 | // save data and update sync task 221 | taskDoc := task 222 | taskDoc.CurrentHeight = inProcessBlock 223 | taskDoc.LastUpdateTime = time.Now().Unix() 224 | taskDoc.Status = document.SyncTaskStatusUnderway 225 | if inProcessBlock == task.EndHeight { 226 | taskDoc.Status = document.SyncTaskStatusCompleted 227 | } 228 | 229 | err := saveDocs(blockDoc, taskDoc) 230 | if err != nil { 231 | log.Error("save docs fail", logger.String("err", err.Error())) 232 | } else { 233 | task.CurrentHeight = inProcessBlock 234 | } 235 | } else { 236 | log.Info("task worker changed", logger.Any("task_id", task.ID), 237 | logger.String("origin worker", workerId), logger.String("current worker", task.WorkerId)) 238 | return 239 | } 240 | } 241 | 242 | log.Info("worker finish execute task", 243 | logger.String("task_worker", task.WorkerId), logger.Any("task_id", task.ID), 244 | logger.String("from-to-current", fmt.Sprintf("%v-%v-%v", task.StartHeight, task.EndHeight, task.CurrentHeight))) 245 | } 246 | 247 | // assert task is valid 248 | // valid catch up task: current_height < end_height 249 | // valid follow task: current_height + blockNumPerWorkerHandle > blockChainLatestHeight 250 | func assertTaskValid(task document.SyncTask, blockNumPerWorkerHandle, blockChainLatestHeight int64) bool { 251 | var ( 252 | taskType string 253 | flag = false 254 | ) 255 | if task.EndHeight != 0 { 256 | taskType = document.SyncTaskTypeCatchUp 257 | } else { 258 | taskType = document.SyncTaskTypeFollow 259 | } 260 | currentHeight := task.CurrentHeight 261 | if currentHeight == 0 { 262 | currentHeight = task.StartHeight - 1 263 | } 264 | 265 | switch taskType { 266 | case document.SyncTaskTypeCatchUp: 267 | if currentHeight < task.EndHeight { 268 | flag = true 269 | } 270 | break 271 | case document.SyncTaskTypeFollow: 272 | if currentHeight+blockNumPerWorkerHandle > blockChainLatestHeight { 273 | flag = true 274 | } 275 | break 276 | } 277 | return flag 278 | } 279 | 280 | func parseBlock(b int64, client *helper.Client) (document.Block, error) { 281 | var blockDoc document.Block 282 | 283 | defer func() { 284 | if err := recover(); err != nil { 285 | logger.Error("parse block fail", logger.Int64("blockHeight", b), 286 | logger.Any("err", err)) 287 | } 288 | }() 289 | 290 | block, err := client.Block(&b) 291 | if err != nil { 292 | // there is possible parse block fail when in iterator 293 | var err2 error 294 | client2 := helper.GetClient() 295 | block, err2 = client2.Block(&b) 296 | client2.Release() 297 | if err2 != nil { 298 | return blockDoc, err2 299 | } 300 | } 301 | 302 | if err := handler.HandleTx(block.Block); err != nil { 303 | return blockDoc, err 304 | } 305 | 306 | // get validatorSet at given height 307 | var validators []*types.Validator 308 | res, err := client.Validators(&b) 309 | if err != nil { 310 | logger.Error("Can't get validatorSet at height", logger.Int64("Height", b)) 311 | } else { 312 | validators = res.Validators 313 | } 314 | 315 | return handler.ParseBlock(block.BlockMeta, block.Block, validators), nil 316 | } 317 | 318 | // assert task worker unchanged 319 | func assertTaskWorkerUnchanged(taskId bson.ObjectId, workerId string) (bool, error) { 320 | var ( 321 | syncTaskModel document.SyncTask 322 | ) 323 | // check task owner 324 | task, err := syncTaskModel.GetTaskById(taskId) 325 | if err != nil { 326 | return false, err 327 | } 328 | 329 | if task.WorkerId == workerId { 330 | return true, nil 331 | } else { 332 | return false, nil 333 | } 334 | } 335 | 336 | func saveDocs(blockDoc document.Block, taskDoc document.SyncTask) error { 337 | var ( 338 | ops []txn.Op 339 | ) 340 | 341 | if blockDoc.Hash == "" { 342 | return fmt.Errorf("block document is empty") 343 | } 344 | 345 | insertOp := txn.Op{ 346 | C: document.CollectionNmBlock, 347 | Id: bson.NewObjectId(), 348 | Insert: blockDoc, 349 | } 350 | 351 | updateOp := txn.Op{ 352 | C: document.CollectionNameSyncTask, 353 | Id: taskDoc.ID, 354 | Assert: txn.DocExists, 355 | Update: bson.M{ 356 | "$set": bson.M{ 357 | "current_height": taskDoc.CurrentHeight, 358 | "status": taskDoc.Status, 359 | "last_update_time": taskDoc.LastUpdateTime, 360 | }, 361 | }, 362 | } 363 | 364 | ops = append(ops, insertOp, updateOp) 365 | 366 | if len(ops) > 0 { 367 | err := store.Txn(ops) 368 | if err != nil { 369 | return err 370 | } 371 | } 372 | 373 | return nil 374 | } 375 | -------------------------------------------------------------------------------- /service/task/task_execute_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | 7 | "encoding/json" 8 | "github.com/irisnet/irishub-sync/logger" 9 | "github.com/irisnet/irishub-sync/store/document" 10 | "github.com/irisnet/irishub-sync/util/helper" 11 | "gopkg.in/mgo.v2/bson" 12 | "time" 13 | ) 14 | 15 | func Test_executeTask(t *testing.T) { 16 | type args struct { 17 | blockNumPerWorkerHandle int64 18 | maxWorkerSleepTime int64 19 | chanLimit chan bool 20 | } 21 | 22 | limitChan := make(chan bool, 2) 23 | 24 | tests := []struct { 25 | name string 26 | args args 27 | }{ 28 | { 29 | name: "test execute task", 30 | args: args{ 31 | blockNumPerWorkerHandle: 100, 32 | maxWorkerSleepTime: 10 * 60, 33 | chanLimit: limitChan, 34 | }, 35 | }, 36 | } 37 | for _, tt := range tests { 38 | t.Run(tt.name, func(t *testing.T) { 39 | var wg sync.WaitGroup 40 | wg.Add(1) 41 | 42 | tt.args.chanLimit <- true 43 | go executeTask(tt.args.blockNumPerWorkerHandle, tt.args.maxWorkerSleepTime, tt.args.chanLimit) 44 | 45 | wg.Wait() 46 | }) 47 | } 48 | } 49 | 50 | func Test_assertTaskValid(t *testing.T) { 51 | var ( 52 | syncTaskModel document.SyncTask 53 | ) 54 | 55 | catchUpTask, _ := syncTaskModel.GetTaskById(bson.ObjectIdHex("5c176b243b6c5c3ff62deaea")) 56 | followTask, _ := syncTaskModel.GetTaskById(bson.ObjectIdHex("5c176dc63b6c5c4027b8fb92")) 57 | 58 | type args struct { 59 | task document.SyncTask 60 | blockNumPerWorkerHandle int64 61 | blockChainLatestHeight int64 62 | } 63 | tests := []struct { 64 | name string 65 | args args 66 | }{ 67 | { 68 | name: "test assert catch up task valid", 69 | args: args{ 70 | task: catchUpTask, 71 | blockNumPerWorkerHandle: 100, 72 | blockChainLatestHeight: 100, 73 | }, 74 | }, 75 | { 76 | name: "test assert follow task valid", 77 | args: args{ 78 | task: followTask, 79 | blockNumPerWorkerHandle: 200, 80 | blockChainLatestHeight: 800, 81 | }, 82 | }, 83 | } 84 | for _, tt := range tests { 85 | t.Run(tt.name, func(t *testing.T) { 86 | got := assertTaskValid(tt.args.task, tt.args.blockNumPerWorkerHandle, tt.args.blockChainLatestHeight) 87 | t.Log(got) 88 | }) 89 | } 90 | } 91 | 92 | func Test_parseBlock(t *testing.T) { 93 | client := helper.GetClient() 94 | 95 | defer func() { 96 | logger.Info("release client") 97 | client.Release() 98 | }() 99 | 100 | type args struct { 101 | b int64 102 | client *helper.Client 103 | } 104 | tests := []struct { 105 | name string 106 | args args 107 | }{ 108 | { 109 | name: "test parse block", 110 | args: args{ 111 | client: client, 112 | b: 4379192, 113 | }, 114 | }, 115 | } 116 | for _, tt := range tests { 117 | t.Run(tt.name, func(t *testing.T) { 118 | res, err := parseBlock(tt.args.b, tt.args.client) 119 | if err != nil { 120 | t.Fatal(err) 121 | } 122 | resBytes, err := json.MarshalIndent(res, "", "\t") 123 | if err != nil { 124 | t.Error(err) 125 | } 126 | t.Log(string(resBytes)) 127 | }) 128 | } 129 | } 130 | 131 | func Test_assertTaskWorkerUnchanged(t *testing.T) { 132 | type args struct { 133 | taskId bson.ObjectId 134 | workerId string 135 | } 136 | tests := []struct { 137 | name string 138 | args args 139 | }{ 140 | { 141 | name: "test assert test worker unchanged", 142 | args: args{ 143 | taskId: bson.ObjectIdHex("5b176dc63b6c5c4027b8fb92"), 144 | workerId: "ObjectIdHex(\"5c19cec10000000000000000\")", 145 | }, 146 | }, 147 | } 148 | for _, tt := range tests { 149 | t.Run(tt.name, func(t *testing.T) { 150 | res, err := assertTaskWorkerUnchanged(tt.args.taskId, tt.args.workerId) 151 | if err != nil { 152 | t.Fatal(err) 153 | } 154 | t.Log(res) 155 | }) 156 | } 157 | } 158 | 159 | func Test_saveDocs(t *testing.T) { 160 | var ( 161 | syncTaskModel document.SyncTask 162 | ) 163 | 164 | block := document.Block{ 165 | Height: 480, 166 | Hash: bson.NewObjectId().Hex(), 167 | } 168 | task, _ := syncTaskModel.GetTaskById(bson.ObjectIdHex("5c19e9e03b6c5ca9a96fdf62")) 169 | task.CurrentHeight = block.Height 170 | task.LastUpdateTime = time.Now().Unix() 171 | 172 | type args struct { 173 | blockDoc document.Block 174 | taskDoc document.SyncTask 175 | } 176 | tests := []struct { 177 | name string 178 | args args 179 | }{ 180 | { 181 | name: "test save docs", 182 | args: args{ 183 | blockDoc: block, 184 | taskDoc: task, 185 | }, 186 | }, 187 | } 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | if err := saveDocs(tt.args.blockDoc, tt.args.taskDoc); err != nil { 191 | t.Fatal(err) 192 | } 193 | t.Log("save success") 194 | }) 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /service/task/task_statistics.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store/document" 5 | ) 6 | 7 | func AssertFastSyncFinished() (bool, error) { 8 | var ( 9 | syncTaskModel document.SyncTask 10 | syncConfModel document.SyncConf 11 | ) 12 | 13 | status := []string{document.SyncTaskStatusUnderway} 14 | tasks, err := syncTaskModel.QueryAll(status, document.SyncTaskTypeFollow) 15 | 16 | if err != nil { 17 | return false, err 18 | } 19 | 20 | if len(tasks) != 0 { 21 | blockChainLatestHeight, err := getBlockChainLatestHeight() 22 | if err != nil { 23 | return false, err 24 | } 25 | syncConf, err := syncConfModel.GetConf() 26 | if err != nil { 27 | return false, err 28 | } 29 | 30 | task := tasks[0] 31 | if task.CurrentHeight+syncConf.BlockNumPerWorkerHandle > blockChainLatestHeight { 32 | return true, err 33 | } 34 | } 35 | 36 | return false, nil 37 | } 38 | -------------------------------------------------------------------------------- /service/task/task_statistics_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import "testing" 4 | 5 | func Test_assertFastSyncFinished(t *testing.T) { 6 | tests := []struct { 7 | name string 8 | }{ 9 | { 10 | name: "assert fast sync finished", 11 | }, 12 | } 13 | for _, tt := range tests { 14 | t.Run(tt.name, func(t *testing.T) { 15 | res, err := AssertFastSyncFinished() 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | t.Log(res) 20 | }) 21 | } 22 | } 23 | 24 | //func TestMakeUpdateDelegatorTask(t *testing.T) { 25 | // updateDelegator() 26 | //} 27 | -------------------------------------------------------------------------------- /service/task/timer_task.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | type Command = func() 4 | type Task interface { 5 | GetCommand() Command 6 | GetCron() string 7 | } 8 | 9 | type LockTask struct { 10 | Spec string 11 | cmd Command 12 | withLock bool 13 | } 14 | 15 | func NewTask(spec string, cmd Command) Task { 16 | return &LockTask{ 17 | Spec: spec, 18 | cmd: cmd, 19 | } 20 | } 21 | 22 | func NewLockTaskFromEnv(spec string, cmd Command) Task { 23 | return NewTask(spec, cmd) 24 | } 25 | 26 | func (task *LockTask) GetCommand() Command { 27 | lockCmd := func() { 28 | task.cmd() 29 | } 30 | return lockCmd 31 | } 32 | 33 | func (task *LockTask) GetCron() string { 34 | return task.Spec 35 | } 36 | -------------------------------------------------------------------------------- /store/document/account.go: -------------------------------------------------------------------------------- 1 | package document 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "gopkg.in/mgo.v2" 6 | "gopkg.in/mgo.v2/bson" 7 | "time" 8 | ) 9 | 10 | const ( 11 | CollectionNmAccount = "account" 12 | AccountFieldAddress = "address" 13 | ) 14 | 15 | type Account struct { 16 | Address string `bson:"address"` 17 | AccountNumber uint64 `bson:"account_number"` 18 | Total store.Coin `bson:"total"` 19 | CoinIris store.Coin `bson:"coin_iris"` 20 | Delegation store.Coin `bson:"delegation"` 21 | UnbondingDelegation store.Coin `bson:"unbonding_delegation"` 22 | Rewards store.Coin `bson:"rewards"` 23 | UpdateAt int64 `bson:"update_at"` 24 | CreateAt int64 `bson:"create_at"` 25 | } 26 | 27 | func (d Account) Name() string { 28 | return CollectionNmAccount 29 | } 30 | 31 | func (d Account) PkKvPair() map[string]interface{} { 32 | return bson.M{AccountFieldAddress: d.Address} 33 | } 34 | 35 | // override store.Save() 36 | // not to check record if exist before save document 37 | func (d Account) Save(account Account) error { 38 | account.CreateAt = time.Now().Unix() 39 | fn := func(c *mgo.Collection) error { 40 | return c.Insert(account) 41 | } 42 | 43 | return store.ExecCollection(d.Name(), fn) 44 | } 45 | 46 | // get account by primary key 47 | // return a empty struct when record is not exists 48 | func (d Account) getAccountByPK() (Account, error) { 49 | var ( 50 | res Account 51 | ) 52 | find := func(c *mgo.Collection) error { 53 | return c.Find(d.PkKvPair()).One(&res) 54 | } 55 | 56 | if err := store.ExecCollection(d.Name(), find); err != nil { 57 | if err == mgo.ErrNotFound { 58 | return res, nil 59 | } else { 60 | return res, err 61 | } 62 | } 63 | 64 | return res, nil 65 | } 66 | 67 | // save account address 68 | func (d Account) SaveAddress(address string) error { 69 | d.Address = address 70 | if account, err := d.getAccountByPK(); err != nil { 71 | return err 72 | } else { 73 | if account.Address != "" { 74 | return nil 75 | } 76 | account.Address = address 77 | return d.Save(account) 78 | 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /store/document/account_test.go: -------------------------------------------------------------------------------- 1 | package document 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | func TestGetAccountByPK(t *testing.T) { 9 | doc := Account{ 10 | Address: "123", 11 | } 12 | res, err := doc.getAccountByPK() 13 | if err != nil { 14 | t.Fatal(err) 15 | } 16 | resBytes, _ := json.MarshalIndent(res, "", "\t") 17 | t.Log(string(resBytes)) 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /store/document/block.go: -------------------------------------------------------------------------------- 1 | package document 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "gopkg.in/mgo.v2" 6 | "gopkg.in/mgo.v2/bson" 7 | "time" 8 | ) 9 | 10 | const ( 11 | CollectionNmBlock = "block" 12 | 13 | Block_Field_Height = "height" 14 | ) 15 | 16 | type Block struct { 17 | Height int64 `bson:"height"` 18 | Hash string `bson:"hash"` 19 | Time time.Time `bson:"time"` 20 | NumTxs int64 `bson:"num_txs"` 21 | ProposalAddress string `bson:"proposal_address"` 22 | Meta BlockMeta `bson:"meta"` 23 | Block BlockContent `bson:"block"` 24 | Validators []Validator `bson:"validators"` 25 | Result BlockResults `bson:"results"` 26 | } 27 | 28 | type BlockMeta struct { 29 | BlockID BlockID `bson:"block_id"` 30 | Header Header `bson:"header"` 31 | } 32 | 33 | type BlockID struct { 34 | Hash string `bson:"hash"` 35 | PartsHeader PartSetHeader `bson:"parts"` 36 | } 37 | 38 | type PartSetHeader struct { 39 | Total int `bson:"total"` 40 | Hash string `bson:"hash"` 41 | } 42 | 43 | type Header struct { 44 | // basic block info 45 | ChainID string `bson:"chain_id"` 46 | Height int64 `bson:"height"` 47 | Time time.Time `bson:"time"` 48 | NumTxs int64 `bson:"num_txs"` 49 | 50 | // prev block info 51 | LastBlockID BlockID `bson:"last_block_id"` 52 | TotalTxs int64 `bson:"total_txs"` 53 | 54 | // hashes of block data 55 | LastCommitHash string `bson:"last_commit_hash"` // commit from validators from the last block 56 | DataHash string `bson:"data_hash"` // transactions 57 | 58 | // hashes from the app output from the prev block 59 | ValidatorsHash string `bson:"validators_hash"` // validators for the current block 60 | ConsensusHash string `bson:"consensus_hash"` // consensus params for current block 61 | AppHash string `bson:"app_hash"` // state after txs from the previous block 62 | LastResultsHash string `bson:"last_results_hash"` // root hash of all results from the txs from the previous block 63 | 64 | // consensus info 65 | EvidenceHash string `bson:"evidence_hash"` // evidence included in the block 66 | } 67 | 68 | type BlockContent struct { 69 | LastCommit Commit `bson:"last_commit"` 70 | } 71 | 72 | type Commit struct { 73 | // NOTE: The Precommits are in order of address to preserve the bonded ValidatorSet order. 74 | // Any peer with a block can gossip precommits by index with a peer without recalculating the 75 | // active ValidatorSet. 76 | BlockID BlockID `bson:"block_id"` 77 | Precommits []Vote `bson:"precommits"` 78 | } 79 | 80 | // Represents a prevote, precommit, or commit vote from validators for consensus. 81 | type Vote struct { 82 | ValidatorAddress string `bson:"validator_address"` 83 | ValidatorIndex int `bson:"validator_index"` 84 | Height int64 `bson:"height"` 85 | Round int `bson:"round"` 86 | Timestamp time.Time `bson:"timestamp"` 87 | Type byte `bson:"type"` 88 | BlockID BlockID `bson:"block_id"` // zero if vote is nil. 89 | Signature Signature `bson:"signature"` 90 | } 91 | 92 | type Signature struct { 93 | Type string `bson:"type"` 94 | Value string `bson:"value"` 95 | } 96 | 97 | type Validator struct { 98 | Address string `bson:"address"` 99 | PubKey string `bson:"pub_key"` 100 | VotingPower int64 `bson:"voting_power"` 101 | Accum int64 `bson:"accum"` 102 | } 103 | 104 | type BlockResults struct { 105 | DeliverTx []ResponseDeliverTx `bson:"deliver_tx"` 106 | EndBlock ResponseEndBlock `bson:""end_block""` 107 | BeginBlock ResponseBeginBlock `bson:""begin_block""` 108 | } 109 | 110 | type ResponseDeliverTx struct { 111 | Code uint32 `bson:"code"` 112 | Data string `bson:"data"` 113 | Log string `bson:"log"` 114 | Info string `bson:"info"` 115 | GasWanted int64 `bson:"gas_wanted"` 116 | GasUsed int64 `bson:"gas_used"` 117 | Tags []KvPair `bson:"tags"` 118 | } 119 | 120 | type ResponseEndBlock struct { 121 | ValidatorUpdates []ValidatorUpdate `bson:"validator_updates"` 122 | ConsensusParamUpdates ConsensusParams `bson:"consensus_param_updates"` 123 | Tags []KvPair `bson:"tags"` 124 | } 125 | 126 | type ValidatorUpdate struct { 127 | PubKey string `bson:"pub_key"` 128 | Power int64 `bson:"power"` 129 | } 130 | 131 | type ConsensusParams struct { 132 | BlockSize BlockSizeParams `bson:"block_size"` 133 | Evidence EvidenceParams `bson:"evidence"` 134 | Validator ValidatorParams `bson:"validator"` 135 | } 136 | 137 | type ValidatorParams struct { 138 | PubKeyTypes []string `bson:"pub_key_types` 139 | } 140 | 141 | type BlockSizeParams struct { 142 | MaxBytes int64 `bson:"max_bytes"` 143 | MaxGas int64 `bson:"max_gas"` 144 | } 145 | 146 | type EvidenceParams struct { 147 | MaxAge int64 `bson:"max_age"` 148 | } 149 | 150 | type BlockGossip struct { 151 | BlockPartSizeBytes int32 `bson:"block_part_size_bytes"` 152 | } 153 | 154 | type ResponseBeginBlock struct { 155 | Tags []KvPair `bson:"tags"` 156 | } 157 | 158 | type KvPair struct { 159 | Key string `bson:"key"` 160 | Value string `bson:"value"` 161 | } 162 | 163 | func (d Block) Name() string { 164 | return CollectionNmBlock 165 | } 166 | 167 | func (d Block) PkKvPair() map[string]interface{} { 168 | return bson.M{Block_Field_Height: d.Height} 169 | } 170 | 171 | type ResValidatorPreCommits struct { 172 | Address string `bson:"_id"` 173 | PreCommitsNum int64 `bson:"num"` 174 | } 175 | 176 | func (d Block) CalculateValidatorPreCommit(startBlock, endBlock int64) ([]ResValidatorPreCommits, error) { 177 | 178 | var res []ResValidatorPreCommits 179 | query := []bson.M{ 180 | { 181 | "$match": bson.M{ 182 | Block_Field_Height: bson.M{"$gt": startBlock, "$lte": endBlock}, 183 | }, 184 | }, 185 | { 186 | "$unwind": "$block.last_commit.precommits", 187 | }, 188 | { 189 | "$group": bson.M{ 190 | "_id": "$block.last_commit.precommits.validator_address", 191 | "num": bson.M{ 192 | "$sum": 1, 193 | }, 194 | }, 195 | }, 196 | { 197 | "$sort": bson.M{"num": -1}, 198 | }, 199 | } 200 | 201 | fun := func(c *mgo.Collection) error { 202 | return c.Pipe(query).All(&res) 203 | } 204 | 205 | err := store.ExecCollection(d.Name(), fun) 206 | 207 | if err != nil { 208 | return nil, err 209 | } 210 | 211 | return res, nil 212 | } 213 | 214 | func (d Block) GetMaxBlockHeight() (int64, error) { 215 | var result struct { 216 | Height int64 `bson:"height` 217 | } 218 | 219 | getMaxBlockHeightFn := func(c *mgo.Collection) error { 220 | return c.Find(nil).Select(bson.M{"height": 1}).Sort("-height").Limit(1).One(&result) 221 | } 222 | 223 | if err := store.ExecCollection(d.Name(), getMaxBlockHeightFn); err != nil { 224 | return result.Height, err 225 | } 226 | 227 | return result.Height, nil 228 | } 229 | -------------------------------------------------------------------------------- /store/document/proposal.go: -------------------------------------------------------------------------------- 1 | package document 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "gopkg.in/mgo.v2" 6 | "gopkg.in/mgo.v2/bson" 7 | "time" 8 | ) 9 | 10 | const ( 11 | CollectionNmProposal = "proposal" 12 | 13 | Proposal_Field_ProposalId = "proposal_id" 14 | Proposal_Field_Status = "status" 15 | ) 16 | 17 | type Proposal struct { 18 | ProposalId uint64 `bson:"proposal_id"` 19 | Title string `bson:"title"` 20 | Type string `bson:"type"` 21 | Description string `bson:"description"` 22 | Status string `bson:"status"` 23 | SubmitTime time.Time `bson:"submit_time"` 24 | DepositEndTime time.Time `bson:"deposit_end_time"` 25 | VotingStartTime time.Time `bson:"voting_start_time"` 26 | VotingEndTime time.Time `bson:"voting_end_time"` 27 | VotingPeriodStartHeight int64 `bson:"voting_start_height"` 28 | VotingEndHeight int64 `bson:"voting_end_height"` 29 | TotalDeposit store.Coins `bson:"total_deposit"` 30 | Votes []PVote `bson:"votes"` 31 | TallyResult PTallyResult `bson:"tally_result"` 32 | } 33 | 34 | type PVote struct { 35 | Voter string `json:"voter" bson:"voter"` 36 | Option string `json:"option" bson:"option"` 37 | TxHash string `json:"tx_hash" bson:"txhash"` 38 | Time time.Time `json:"time" bson:"time"` 39 | } 40 | 41 | //----------------------------------------------------------- 42 | // Tally Results 43 | type PTallyResult struct { 44 | Yes string `json:"yes" bson:"yes"` 45 | Abstain string `json:"abstain" bson:"abstain"` 46 | No string `json:"no" bson:"no"` 47 | NoWithVeto string `json:"no_with_veto" bson:"nowithveto"` 48 | SystemVotingPower string `json:"system_voting_power" bson:"system_voting_power"` 49 | } 50 | 51 | func (m Proposal) Name() string { 52 | return CollectionNmProposal 53 | } 54 | 55 | func (m Proposal) PkKvPair() map[string]interface{} { 56 | return bson.M{Proposal_Field_ProposalId: m.ProposalId} 57 | } 58 | 59 | func QueryProposal(proposalId uint64) (Proposal, error) { 60 | var result Proposal 61 | query := func(c *mgo.Collection) error { 62 | err := c.Find(bson.M{Proposal_Field_ProposalId: proposalId}).Sort("-submit_block").One(&result) 63 | return err 64 | } 65 | 66 | err := store.ExecCollection(CollectionNmProposal, query) 67 | 68 | if err != nil { 69 | return result, err 70 | } 71 | 72 | return result, nil 73 | } 74 | func QueryByStatus(status []string) ([]Proposal, error) { 75 | var result []Proposal 76 | query := func(c *mgo.Collection) error { 77 | err := c.Find(bson.M{Proposal_Field_Status: bson.M{"$in": status}}).All(&result) 78 | return err 79 | } 80 | err := store.ExecCollection(CollectionNmProposal, query) 81 | 82 | if err != nil { 83 | return result, err 84 | } 85 | return result, nil 86 | } 87 | -------------------------------------------------------------------------------- /store/document/sync_conf.go: -------------------------------------------------------------------------------- 1 | package document 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "gopkg.in/mgo.v2" 6 | "gopkg.in/mgo.v2/bson" 7 | ) 8 | 9 | const ( 10 | CollectionNameSyncConf = "sync_conf" 11 | ) 12 | 13 | type SyncConf struct { 14 | BlockNumPerWorkerHandle int64 `bson:"block_num_per_worker_handle"` 15 | MaxWorkerSleepTime int64 `bson:"max_worker_sleep_time"` 16 | } 17 | 18 | func (d SyncConf) Name() string { 19 | return CollectionNameSyncConf 20 | } 21 | 22 | func (d SyncConf) PkKvPair() map[string]interface{} { 23 | return bson.M{} 24 | } 25 | 26 | func (d SyncConf) GetConf() (SyncConf, error) { 27 | var syncConf SyncConf 28 | 29 | q := bson.M{} 30 | fn := func(c *mgo.Collection) error { 31 | return c.Find(q).One(&syncConf) 32 | } 33 | 34 | err := store.ExecCollection(d.Name(), fn) 35 | 36 | if err != nil { 37 | return syncConf, err 38 | } 39 | 40 | return syncConf, nil 41 | } 42 | -------------------------------------------------------------------------------- /store/document/sync_task.go: -------------------------------------------------------------------------------- 1 | package document 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "gopkg.in/mgo.v2" 6 | "gopkg.in/mgo.v2/bson" 7 | "time" 8 | ) 9 | 10 | const ( 11 | CollectionNameSyncTask = "sync_task" 12 | 13 | // value of status 14 | SyncTaskStatusUnHandled = "unhandled" 15 | SyncTaskStatusUnderway = "underway" 16 | SyncTaskStatusCompleted = "completed" 17 | // only for follow task 18 | // when current_height of follow task add blockNumPerWorkerHandle 19 | // less than blockchain current_height, this follow task's status should be set invalid 20 | FollowTaskStatusInvalid = "invalid" 21 | 22 | // taskType 23 | SyncTaskTypeCatchUp = "catch_up" 24 | SyncTaskTypeFollow = "follow" 25 | ) 26 | 27 | type WorkerLog struct { 28 | WorkerId string `bson:"worker_id"` // worker id 29 | BeginTime time.Time `bson:"begin_time"` // time which worker begin handle this task 30 | } 31 | 32 | type SyncTask struct { 33 | ID bson.ObjectId `bson:"_id"` 34 | StartHeight int64 `bson:"start_height"` // task start height 35 | EndHeight int64 `bson:"end_height"` // task end height 36 | CurrentHeight int64 `bson:"current_height"` // task current height 37 | Status string `bson:"status"` // task status 38 | WorkerId string `bson:"worker_id"` // worker id 39 | WorkerLogs []WorkerLog `bson:"worker_logs"` // worker logs 40 | LastUpdateTime int64 `bson:"last_update_time"` // unix timestamp 41 | } 42 | 43 | func (d SyncTask) Name() string { 44 | return CollectionNameSyncTask 45 | } 46 | 47 | func (d SyncTask) PkKvPair() map[string]interface{} { 48 | return bson.M{"start_height": d.CurrentHeight, "end_height": d.EndHeight} 49 | } 50 | 51 | // get max block height in sync task 52 | func (d SyncTask) GetMaxBlockHeight() (int64, error) { 53 | type maxHeightRes struct { 54 | MaxHeight int64 `bson:"max"` 55 | } 56 | var res []maxHeightRes 57 | 58 | q := []bson.M{ 59 | { 60 | "$group": bson.M{ 61 | "_id": nil, 62 | "max": bson.M{"$max": "$end_height"}, 63 | }, 64 | }, 65 | } 66 | 67 | getMaxBlockHeightFn := func(c *mgo.Collection) error { 68 | return c.Pipe(q).All(&res) 69 | } 70 | err := store.ExecCollection(d.Name(), getMaxBlockHeightFn) 71 | 72 | if err != nil { 73 | return 0, err 74 | } 75 | if len(res) > 0 { 76 | return res[0].MaxHeight, nil 77 | } 78 | 79 | return 0, nil 80 | } 81 | 82 | // query record by status 83 | func (d SyncTask) QueryAll(status []string, taskType string) ([]SyncTask, error) { 84 | var syncTasks []SyncTask 85 | q := bson.M{} 86 | 87 | if len(status) > 0 { 88 | q["status"] = bson.M{ 89 | "$in": status, 90 | } 91 | } 92 | 93 | switch taskType { 94 | case SyncTaskTypeCatchUp: 95 | q["end_height"] = bson.M{ 96 | "$ne": 0, 97 | } 98 | break 99 | case SyncTaskTypeFollow: 100 | q["end_height"] = bson.M{ 101 | "$eq": 0, 102 | } 103 | break 104 | } 105 | 106 | fn := func(c *mgo.Collection) error { 107 | return c.Find(q).All(&syncTasks) 108 | } 109 | 110 | err := store.ExecCollection(d.Name(), fn) 111 | 112 | if err != nil { 113 | return syncTasks, err 114 | } 115 | 116 | return syncTasks, nil 117 | } 118 | 119 | // get executable task 120 | // 1. tasks which status eq unhandled 121 | // 2. tasks which status eq underway and lastUpdateTime 0 { 158 | for _, v := range tasks { 159 | if v.Status == SyncTaskStatusUnderway && v.LastUpdateTime >= t { 160 | continue 161 | } else { 162 | executableTasks = append(executableTasks, v) 163 | } 164 | } 165 | } 166 | return executableTasks, nil 167 | } 168 | } 169 | 170 | func (d SyncTask) GetTaskById(id bson.ObjectId) (SyncTask, error) { 171 | var task SyncTask 172 | 173 | fn := func(c *mgo.Collection) error { 174 | return c.FindId(id).One(&task) 175 | } 176 | 177 | err := store.ExecCollection(d.Name(), fn) 178 | if err != nil { 179 | return task, err 180 | } 181 | return task, nil 182 | } 183 | 184 | func (d SyncTask) GetTaskByIdAndWorker(id bson.ObjectId, worker string) (SyncTask, error) { 185 | var task SyncTask 186 | 187 | fn := func(c *mgo.Collection) error { 188 | q := bson.M{ 189 | "_id": id, 190 | "worker_id": worker, 191 | } 192 | 193 | return c.Find(q).One(&task) 194 | } 195 | 196 | err := store.ExecCollection(d.Name(), fn) 197 | if err != nil { 198 | return task, err 199 | } 200 | return task, nil 201 | } 202 | 203 | // take over a task 204 | // update status, worker_id, worker_logs and last_update_time 205 | func (d SyncTask) TakeOverTask(task SyncTask, workerId string) error { 206 | // multiple goroutine attempt to update same record, 207 | // use this selector to ensure only one goroutine can update success at same time 208 | fn := func(c *mgo.Collection) error { 209 | selector := bson.M{ 210 | "_id": task.ID, 211 | "last_update_time": task.LastUpdateTime, 212 | } 213 | 214 | task.Status = SyncTaskStatusUnderway 215 | task.WorkerId = workerId 216 | task.LastUpdateTime = time.Now().Unix() 217 | task.WorkerLogs = append(task.WorkerLogs, WorkerLog{ 218 | WorkerId: workerId, 219 | BeginTime: time.Now(), 220 | }) 221 | 222 | return c.Update(selector, task) 223 | } 224 | 225 | return store.ExecCollection(d.Name(), fn) 226 | } 227 | 228 | // update task last update time 229 | func (d SyncTask) UpdateLastUpdateTime(task SyncTask) error { 230 | fn := func(c *mgo.Collection) error { 231 | selector := bson.M{ 232 | "_id": task.ID, 233 | "worker_id": task.WorkerId, 234 | } 235 | 236 | task.LastUpdateTime = time.Now().Unix() 237 | 238 | return c.Update(selector, task) 239 | } 240 | 241 | return store.ExecCollection(d.Name(), fn) 242 | } 243 | -------------------------------------------------------------------------------- /store/document/token_flow.go: -------------------------------------------------------------------------------- 1 | package document 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/irisnet/irishub-sync/store" 7 | ) 8 | 9 | const ( 10 | CollectionNmTokenFlow = "token_flow" 11 | ) 12 | 13 | type TokenFlow struct { 14 | BlockHeight int64 `bson:"block_height"` 15 | BlockHash string `bson:"block_hash"` 16 | TxHash string `bson:"tx_hash"` 17 | From string `bson:"from"` 18 | To string `bson:"to"` 19 | Amount store.Coin `bson:"amount"` 20 | Fee store.Fee `bson:"fee"` 21 | TxInitiator string `bson:"tx_initiator"` 22 | TxType string `bson:"tx_type"` 23 | FlowType string `bson:"flow_type"` 24 | Status string `bson:"status"` 25 | Timestamp time.Time `bson:"timestamp"` 26 | } 27 | -------------------------------------------------------------------------------- /store/document/tx.go: -------------------------------------------------------------------------------- 1 | package document 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "gopkg.in/mgo.v2" 6 | "gopkg.in/mgo.v2/bson" 7 | "time" 8 | ) 9 | 10 | const ( 11 | CollectionNmCommonTx = "tx_common" 12 | TxStatusSuccess = "success" 13 | TxStatusFail = "fail" 14 | Unknow_Status = "unknown" 15 | 16 | Tx_Field_Hash = "tx_hash" 17 | Tx_Field_Type = "type" 18 | Tx_Field_Status = "status" 19 | Tx_Field_Height = "height" 20 | ) 21 | 22 | type CommonTx struct { 23 | Time time.Time `bson:"time"` 24 | Height int64 `bson:"height"` 25 | TxHash string `bson:"tx_hash"` 26 | From string `bson:"from"` 27 | To string `bson:"to"` 28 | Amount store.Coins `bson:"amount"` 29 | Type string `bson:"type"` 30 | Fee store.Fee `bson:"fee"` 31 | Memo string `bson:"memo"` 32 | Status string `bson:"status"` 33 | Code uint32 `bson:"code"` 34 | Log string `bson:"log"` 35 | GasUsed int64 `bson:"gas_used"` 36 | GasWanted int64 `bson:"gas_wanted"` 37 | GasPrice float64 `bson:"gas_price"` 38 | ActualFee store.ActualFee `bson:"actual_fee"` 39 | ProposalId uint64 `bson:"proposal_id"` 40 | Tags map[string]string `bson:"tags"` 41 | 42 | //StakeCreateValidator StakeCreateValidator `bson:"stake_create_validator"` 43 | //StakeEditValidator StakeEditValidator `bson:"stake_edit_validator"` 44 | //Msg store.Msg `bson:"-"` 45 | Signers []Signer `bson:"signers"` 46 | 47 | Msgs []DocTxMsg `bson:"msgs"` 48 | } 49 | 50 | type DocTxMsg struct { 51 | Type string `bson:"type"` 52 | Msg Msg `bson:"msg"` 53 | } 54 | 55 | type Msg interface { 56 | Type() string 57 | BuildMsg(msg interface{}) 58 | } 59 | 60 | // Description 61 | type ValDescription struct { 62 | Moniker string `bson:"moniker"` 63 | Identity string `bson:"identity"` 64 | Website string `bson:"website"` 65 | Details string `bson:"details"` 66 | } 67 | 68 | //type StakeCreateValidator struct { 69 | // PubKey string `bson:"pub_key"` 70 | // Description ValDescription `bson:"description"` 71 | // Commission CommissionMsg `bson:"commission"` 72 | //} 73 | 74 | type CommissionMsg struct { 75 | Rate string `bson:"rate"` // the commission rate charged to delegators 76 | MaxRate string `bson:"max_rate"` // maximum commission rate which validator can ever charge 77 | MaxChangeRate string `bson:"max_change_rate"` // maximum daily increase of the validator commission 78 | } 79 | 80 | //type StakeEditValidator struct { 81 | // CommissionRate string `bson:"commission_rate"` 82 | // Description ValDescription `bson:"description"` 83 | //} 84 | 85 | type Signer struct { 86 | AddrHex string `bson:"addr_hex"` 87 | AddrBech32 string `bson:"addr_bech32"` 88 | } 89 | 90 | func (d CommonTx) Name() string { 91 | return CollectionNmCommonTx 92 | } 93 | 94 | func (d CommonTx) PkKvPair() map[string]interface{} { 95 | return bson.M{Tx_Field_Hash: d.TxHash} 96 | } 97 | 98 | func (d CommonTx) Query(query, fields bson.M, sort []string, skip, limit int) ( 99 | results []CommonTx, err error) { 100 | exop := func(c *mgo.Collection) error { 101 | return c.Find(query).Sort(sort...).Select(fields).Skip(skip).Limit(limit).All(&results) 102 | } 103 | return results, store.ExecCollection(d.Name(), exop) 104 | } 105 | 106 | func (d CommonTx) CalculateTxGasAndGasPrice(txType string, limit int) ( 107 | []CommonTx, error) { 108 | query := bson.M{ 109 | Tx_Field_Type: txType, 110 | Tx_Field_Status: TxStatusSuccess, 111 | } 112 | fields := bson.M{} 113 | sort := []string{"-height"} 114 | skip := 0 115 | 116 | return d.Query(query, fields, sort, skip, limit) 117 | } 118 | 119 | func (d CommonTx) GetUnknownOrEmptyTypeTxs(skip, limit int) (res []CommonTx, err error) { 120 | q := bson.M{"$or": []bson.M{ 121 | {Tx_Field_Status: Unknow_Status}, 122 | {Tx_Field_Type: ""}, 123 | }} 124 | sorts := []string{"-height"} 125 | selector := bson.M{ 126 | Tx_Field_Hash: 1, 127 | Tx_Field_Height: 1, 128 | } 129 | 130 | fn := func(c *mgo.Collection) error { 131 | return c.Find(q).Select(selector).Sort(sorts...).Skip(skip).Limit(limit).All(&res) 132 | } 133 | 134 | err = store.ExecCollection(CollectionNmCommonTx, fn) 135 | if err != nil { 136 | return nil, err 137 | } 138 | return 139 | } 140 | -------------------------------------------------------------------------------- /store/document/validator.go: -------------------------------------------------------------------------------- 1 | package document 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/logger" 5 | "github.com/irisnet/irishub-sync/store" 6 | "gopkg.in/mgo.v2" 7 | "gopkg.in/mgo.v2/bson" 8 | ) 9 | 10 | const ( 11 | CollectionNmStakeRoleCandidate = "stake_role_candidate" 12 | 13 | Candidate_Field_Address = "address" 14 | Candidate_Field_Tokens = "tokens" 15 | ) 16 | 17 | type ( 18 | Candidate struct { 19 | Address string `bson:"address"` // owner, identity key 20 | PubKey string `bson:"pub_key"` 21 | PubKeyAddr string `bson:"pub_key_addr"` 22 | Jailed bool `bson:"jailed"` // has the validator been revoked from bonded status 23 | Tokens float64 `bson:"tokens"` 24 | OriginalTokens string `bson:"original_tokens"` 25 | DelegatorShares float64 `bson:"delegator_shares"` 26 | VotingPower float64 `bson:"voting_power"` // Voting power if pubKey is a considered a validator 27 | Description ValDescription `bson:"description"` // Description terms for the candidate 28 | BondHeight int64 `bson:"bond_height"` 29 | Status string `bson:"status"` 30 | } 31 | ) 32 | 33 | func (d Candidate) Name() string { 34 | return CollectionNmStakeRoleCandidate 35 | } 36 | 37 | func (d Candidate) PkKvPair() map[string]interface{} { 38 | return bson.M{Candidate_Field_Address: d.Address} 39 | } 40 | 41 | func (d Candidate) Query(query bson.M, selector interface{}, sorts ...string) ( 42 | results []Candidate, err error) { 43 | exop := func(c *mgo.Collection) error { 44 | if sorts[0] == "" { 45 | return c.Find(query).Select(selector).All(&results) 46 | } else { 47 | return c.Find(query).Select(selector).Sort(sorts...).All(&results) 48 | } 49 | } 50 | return results, store.ExecCollection(d.Name(), exop) 51 | } 52 | 53 | func (d Candidate) Remove(query bson.M) error { 54 | remove := func(c *mgo.Collection) error { 55 | changeInfo, err := c.RemoveAll(query) 56 | logger.Info("Remove candidates", logger.Any("changeInfo", changeInfo)) 57 | return err 58 | } 59 | return store.ExecCollection(d.Name(), remove) 60 | } 61 | 62 | func (d Candidate) QueryAll() (candidates []Candidate) { 63 | selector := bson.M{ 64 | "address": 1, 65 | "pub_key_addr": 1, 66 | "tokens": 1, 67 | "jailed": 1, 68 | "status": 1, 69 | } 70 | candidates, err := d.Query(nil, selector, "") 71 | 72 | if err != nil { 73 | logger.Error("candidate collection is empty") 74 | } 75 | return candidates 76 | } 77 | 78 | func (d Candidate) RemoveCandidates() error { 79 | query := bson.M{} 80 | return d.Remove(query) 81 | } 82 | 83 | func (d Candidate) SaveAll(candidates []Candidate) error { 84 | var docs []interface{} 85 | 86 | if len(candidates) == 0 { 87 | return nil 88 | } 89 | 90 | for _, v := range candidates { 91 | docs = append(docs, v) 92 | } 93 | 94 | err := store.SaveAll(d.Name(), docs) 95 | 96 | return err 97 | } 98 | -------------------------------------------------------------------------------- /store/store.go: -------------------------------------------------------------------------------- 1 | // init mongodb session and provide common functions 2 | 3 | package store 4 | 5 | import ( 6 | "strings" 7 | "time" 8 | 9 | conf "github.com/irisnet/irishub-sync/conf/db" 10 | "github.com/irisnet/irishub-sync/logger" 11 | 12 | "errors" 13 | "fmt" 14 | "gopkg.in/mgo.v2" 15 | "gopkg.in/mgo.v2/bson" 16 | "gopkg.in/mgo.v2/txn" 17 | ) 18 | 19 | var session *mgo.Session 20 | 21 | func Start() { 22 | addrs := strings.Split(conf.Addrs, ",") 23 | dialInfo := &mgo.DialInfo{ 24 | Addrs: addrs, 25 | Database: conf.Database, 26 | Username: conf.User, 27 | Password: conf.Passwd, 28 | Direct: true, 29 | Timeout: time.Second * 10, 30 | PoolLimit: 4096, // Session.SetPoolLimit 31 | } 32 | 33 | var err error 34 | session, err = mgo.DialWithInfo(dialInfo) 35 | if err != nil { 36 | logger.Error(err.Error()) 37 | } 38 | session.SetMode(mgo.PrimaryPreferred, true) 39 | } 40 | 41 | func Stop() { 42 | logger.Info("release resource :mongoDb") 43 | session.Close() 44 | } 45 | 46 | func getSession() *mgo.Session { 47 | // max session num is 4096 48 | return session.Clone() 49 | } 50 | 51 | // get collection object 52 | func ExecCollection(collection string, s func(*mgo.Collection) error) error { 53 | session := getSession() 54 | defer session.Close() 55 | c := session.DB(conf.Database).C(collection) 56 | return s(c) 57 | } 58 | 59 | func Find(collection string, query interface{}) *mgo.Query { 60 | session := getSession() 61 | defer session.Close() 62 | c := session.DB(conf.Database).C(collection) 63 | return c.Find(query) 64 | } 65 | 66 | func Save(h Docs) error { 67 | save := func(c *mgo.Collection) error { 68 | pk := h.PkKvPair() 69 | n, _ := c.Find(pk).Count() 70 | if n >= 1 { 71 | errMsg := fmt.Sprintf("Record exists") 72 | return errors.New(errMsg) 73 | } 74 | logger.Debug("Save document", logger.String("table", h.Name()), logger.Any("content", h)) 75 | return c.Insert(h) 76 | } 77 | return ExecCollection(h.Name(), save) 78 | } 79 | 80 | func SaveAll(collectionName string, docs []interface{}) error { 81 | session := getSession() 82 | defer session.Close() 83 | 84 | c := session.DB(conf.Database).C(collectionName) 85 | return c.Insert(docs...) 86 | } 87 | 88 | func SaveOrUpdate(h Docs) error { 89 | save := func(c *mgo.Collection) error { 90 | n, err := c.Find(h.PkKvPair()).Count() 91 | if err != nil { 92 | logger.Error("Store Find error", logger.String("err", err.Error())) 93 | } 94 | 95 | if n >= 1 { 96 | return Update(h) 97 | } 98 | logger.Debug("Save document", logger.String("table", h.Name()), logger.Any("content", h)) 99 | return c.Insert(h) 100 | } 101 | 102 | return ExecCollection(h.Name(), save) 103 | } 104 | 105 | func Update(h Docs) error { 106 | update := func(c *mgo.Collection) error { 107 | key := h.PkKvPair() 108 | logger.Debug("update document", logger.String("table", h.Name()), logger.Any("conditions", h.PkKvPair())) 109 | return c.Update(key, h) 110 | } 111 | return ExecCollection(h.Name(), update) 112 | } 113 | 114 | func Delete(h Docs) error { 115 | remove := func(c *mgo.Collection) error { 116 | key := h.PkKvPair() 117 | logger.Debug("delete document", logger.String("table", h.Name()), logger.Any("conditions", h.PkKvPair())) 118 | return c.Remove(key) 119 | } 120 | return ExecCollection(h.Name(), remove) 121 | } 122 | 123 | func Query(collectionName string, query bson.M, sort string, fields bson.M, skip int, limit int) (results []interface{}, err error) { 124 | callback := func(c *mgo.Collection) error { 125 | logger.Debug("query document", logger.String("table", collectionName), logger.Any("conditions", query)) 126 | return c.Find(query).Sort(sort).Select(fields).Skip(skip).Limit(limit).All(&results) 127 | } 128 | return results, ExecCollection(collectionName, callback) 129 | } 130 | 131 | // mgo transaction method 132 | // detail to see: https://godoc.org/gopkg.in/mgo.v2/txn 133 | func Txn(ops []txn.Op) error { 134 | session := getSession() 135 | defer session.Close() 136 | 137 | c := session.DB(conf.Database).C(CollectionNameTxn) 138 | runner := txn.NewRunner(c) 139 | 140 | txObjectId := bson.NewObjectId() 141 | err := runner.Run(ops, txObjectId, nil) 142 | if err != nil { 143 | if err == txn.ErrAborted { 144 | err = runner.Resume(txObjectId) 145 | if err != nil { 146 | return err 147 | } 148 | } else { 149 | return err 150 | } 151 | } 152 | 153 | return nil 154 | } 155 | -------------------------------------------------------------------------------- /store/store_test.go: -------------------------------------------------------------------------------- 1 | // init mongodb session and provide common functions 2 | 3 | package store 4 | 5 | import ( 6 | "fmt" 7 | "github.com/pkg/errors" 8 | "gopkg.in/mgo.v2" 9 | "gopkg.in/mgo.v2/bson" 10 | "gopkg.in/mgo.v2/txn" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | func TestInitWithAuth(t *testing.T) { 16 | tests := []struct { 17 | name string 18 | }{ 19 | { 20 | name: "tets initWithAuth", 21 | }, 22 | } 23 | for _, tt := range tests { 24 | t.Run(tt.name, func(t *testing.T) { 25 | Start() 26 | }) 27 | } 28 | } 29 | 30 | type I interface { 31 | Test() 32 | } 33 | 34 | type Block struct { 35 | Height int64 `bson:"height"` 36 | Hash string `bson:"hash"` 37 | Time time.Time `bson:"time"` 38 | NumTxs int64 `bson:"num_txs"` 39 | I map[string]interface{} `bson:"i"` 40 | } 41 | 42 | type I1 struct { 43 | F1 string 44 | F2 string 45 | } 46 | 47 | func (I1) Test() { 48 | } 49 | 50 | func TestInterface(t *testing.T) { 51 | Start() 52 | c := session.Copy().DB("").C("user1") 53 | var r []Block 54 | c.Find(bson.M{"i.f1": "f1"}).All(&r) 55 | fmt.Println(r) 56 | } 57 | 58 | func TestTransaction(t *testing.T) { 59 | Start() 60 | c := session.Copy().DB("").C("transaction") 61 | //txn.SetLogger(logger.GetLogger()) 62 | //txn.SetDebug(true) 63 | runner := txn.NewRunner(c) 64 | ops := []txn.Op{ 65 | { 66 | C: "user1", 67 | Id: bson.NewObjectId(), 68 | //Insert: Block{Height: 1, I: I1{F1: "f1"}}, 69 | }, 70 | { 71 | C: "user1", 72 | Id: bson.NewObjectId(), 73 | Insert: Block{Height: 2, Hash: "xxxxxx"}, 74 | }, 75 | { 76 | C: "user1", 77 | Id: bson.NewObjectId(), 78 | Insert: Block{Height: 2}, 79 | }, 80 | { 81 | C: "user1", 82 | Id: bson.NewObjectId(), 83 | Insert: Block{Height: 3}, 84 | }, 85 | //{ 86 | // C: "user2", 87 | // Id: "5bfcbb90914120370830b8ad", 88 | // Assert: bson.M{"balance": bson.M{"$gte": 100}}, 89 | // Update: bson.M{"$set": bson.M{"height": 4}}, 90 | //}, 91 | } 92 | id := bson.NewObjectId() // Optional 93 | err := runner.Run(ops, id, nil) 94 | fmt.Println(err) 95 | } 96 | 97 | type Task struct { 98 | From int64 `bson:"from"` 99 | To int64 `bson:"to"` 100 | Current int64 `bson:"current"` 101 | Status string `bson:"status"` 102 | WorkLocker string `bson:"work_locker"` 103 | Role string `bson:"role"` 104 | } 105 | 106 | func TestApply(t *testing.T) { 107 | prepare() 108 | for i := 1; i <= 10; i++ { 109 | go func(i int) { 110 | worker := fmt.Sprintf("thread[%d]", i) 111 | task, err := fetchTask(worker) 112 | if err != nil { 113 | fmt.Println("worker:", worker, " do not take task:", err.Error()) 114 | } else { 115 | fmt.Println("worker:", worker, " take task:", task) 116 | } 117 | }(i) 118 | } 119 | time.Sleep(5 * time.Second) 120 | 121 | } 122 | 123 | func fetchTask(worker string) (Task, error) { 124 | var task Task 125 | userDao := session.Copy().DB("").C("task") 126 | for j := 1; j <= 5; j++ { 127 | change := mgo.Change{ 128 | Update: bson.M{"$set": bson.M{"status": "processing", "work_locker": worker}}, 129 | ReturnNew: true, 130 | } 131 | 132 | _, err := userDao.Find(bson.M{"status": "ready", "work_locker": "", "role": "catch-up"}).Limit(1).Apply(change, &task) 133 | if err == nil { 134 | return task, nil 135 | } 136 | } 137 | return task, errors.New("fail") 138 | } 139 | 140 | func prepare() { 141 | Start() 142 | 143 | var current = 100000 144 | var syncHeight = 0 145 | 146 | var goroutine_num = 5 147 | 148 | syncBlockNumFastSync := (current - syncHeight) / goroutine_num 149 | 150 | var batch []txn.Op 151 | for i := 1; i <= goroutine_num; i++ { 152 | var ( 153 | start = syncHeight + (i-1)*syncBlockNumFastSync + 1 154 | end = syncHeight + i*syncBlockNumFastSync 155 | ) 156 | 157 | task := Task{ 158 | From: int64(start), 159 | To: int64(end), 160 | Current: -1, 161 | Status: "ready", 162 | Role: "catch-up", 163 | } 164 | 165 | batch = append(batch, txn.Op{ 166 | C: "task", 167 | Id: bson.NewObjectId(), 168 | //Assert: txn.DocExists, 169 | Insert: task, 170 | }) 171 | } 172 | 173 | //添加一条follow记录 174 | batch = append(batch, txn.Op{ 175 | C: "task", 176 | Id: bson.NewObjectId(), 177 | //Assert: txn.DocExists, 178 | Insert: Task{ 179 | From: int64(current + 1), 180 | Current: int64(current), 181 | Status: "ready", 182 | Role: "follow", 183 | }, 184 | }) 185 | 186 | c := session.Copy().DB("").C("transaction") 187 | runner := txn.NewRunner(c) 188 | 189 | id := bson.NewObjectId() // Optional 190 | runner.Run(batch, id, nil) 191 | } 192 | 193 | //func get_external() string { 194 | // resp, err := http.Get("http://myexternalip.com/raw") 195 | // if err != nil { 196 | // return "" 197 | // } 198 | // defer resp.Body.Close() 199 | // content, _ := ioutil.ReadAll(resp.Body) 200 | // buf := new(bytes.Buffer) 201 | // buf.ReadFrom(resp.Body) //s := buf.String() 202 | // return string(content) 203 | //} 204 | -------------------------------------------------------------------------------- /store/types.go: -------------------------------------------------------------------------------- 1 | // interface for a document 2 | 3 | package store 4 | 5 | const ( 6 | CollectionNameTxn = "mgo_txn" 7 | ) 8 | 9 | type Docs interface { 10 | // collection name 11 | Name() string 12 | // primary key pair(used to find a unique record) 13 | PkKvPair() map[string]interface{} 14 | } 15 | 16 | type Coin struct { 17 | Denom string `json:"denom" bson:"denom"` 18 | Amount float64 `json:"amount" bson:"amount"` 19 | } 20 | 21 | type Coins []Coin 22 | 23 | type Fee struct { 24 | Amount Coins `json:"amount"` 25 | Gas int64 `json:"gas"` 26 | } 27 | 28 | type ActualFee struct { 29 | Denom string `json:"denom"` 30 | Amount float64 `json:"amount"` 31 | } 32 | 33 | type Msg interface { 34 | Type() string 35 | String() string 36 | } 37 | -------------------------------------------------------------------------------- /types/msg/asset.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/logger" 5 | itypes "github.com/irisnet/irishub-sync/types" 6 | "github.com/irisnet/irishub-sync/util/constant" 7 | "strings" 8 | ) 9 | 10 | type ( 11 | DocTxMsgIssueToken struct { 12 | Family string `bson:"family"` 13 | Source string `bson:"source"` 14 | Gateway string `bson:"gateway"` 15 | Symbol string `bson:"symbol"` 16 | CanonicalSymbol string `bson:"canonical_symbol"` 17 | Name string `bson:"name"` 18 | Decimal uint8 `bson:"decimal"` 19 | MinUnitAlias string `bson:"min_unit_alias"` 20 | InitialSupply uint64 `bson:"initial_supply"` 21 | MaxSupply uint64 `bson:"max_supply"` 22 | Mintable bool `bson:"mintable"` 23 | Owner string `bson:"owner"` 24 | UdInfo assetTokenUdInfo `bson:"ud_info"` 25 | } 26 | 27 | DocTxMsgEditToken struct { 28 | TokenId string `bson:"token_id"` // id of token 29 | Owner string `bson:"owner"` // owner of token 30 | CanonicalSymbol string `bson:"canonical_symbol"` // canonical_symbol of token 31 | MinUnitAlias string `bson:"min_unit_alias"` // min_unit_alias of token 32 | MaxSupply uint64 `bson:"max_supply"` 33 | Mintable bool `bson:"mintable"` // mintable of token 34 | Name string `bson:"name"` 35 | UdInfo assetTokenUdInfo `bson:"ud_info"` 36 | } 37 | 38 | DocTxMsgMintToken struct { 39 | TokenId string `bson:"token_id"` // the unique id of the token 40 | Owner string `bson:"owner"` // the current owner address of the token 41 | To string `bson:"to"` // address of mint token to 42 | Amount uint64 `bson:"amount"` // amount of mint token 43 | UdInfo assetTokenUdInfo `bson:"ud_info"` 44 | } 45 | 46 | DocTxMsgTransferTokenOwner struct { 47 | SrcOwner string `bson:"src_owner"` // the current owner address of the token 48 | DstOwner string `bson:"dst_owner"` // the new owner 49 | TokenId string `bson:"token_id"` 50 | UdInfo assetTokenUdInfo `bson:"ud_info"` 51 | } 52 | 53 | DocTxMsgCreateGateway struct { 54 | Owner string `bson:"owner"` // the owner address of the gateway 55 | Moniker string `bson:"moniker"` // the globally unique name of the gateway 56 | Identity string `bson:"identity"` // the identity of the gateway 57 | Details string `bson:"details"` // the description of the gateway 58 | Website string `bson:"website"` // the external website of the gateway 59 | } 60 | 61 | DocTxMsgEditGateway struct { 62 | Owner string `bson:"owner"` // Owner of the gateway 63 | Moniker string `bson:"moniker"` // Moniker of the gateway 64 | Identity string `bson:"identity"` // Identity of the gateway 65 | Details string `bson:"details"` // Details of the gateway 66 | Website string `bson:"website"` // Website of the gateway 67 | } 68 | 69 | DocTxMsgTransferGatewayOwner struct { 70 | Owner string `bson:"owner"` // the current owner address of the gateway 71 | Moniker string `bson:"moniker"` // the unique name of the gateway to be transferred 72 | To string `bson:"to"` // the new owner to which the gateway ownership will be transferred 73 | } 74 | 75 | assetTokenUdInfo struct { 76 | Source string `bson:"source"` 77 | Gateway string `bson:"gateway"` 78 | Symbol string `bson:"symbol"` 79 | } 80 | ) 81 | 82 | const ( 83 | separatorOfSymbolInTokenId = "." 84 | externalTokenPrefix = "x" 85 | SourceNative = "native" 86 | SourceExternal = "external" 87 | SourceGateWay = "gateway" 88 | ) 89 | 90 | func (m *DocTxMsgIssueToken) Type() string { 91 | return constant.TxMsgTypeAssetIssueToken 92 | } 93 | 94 | func (m *DocTxMsgIssueToken) BuildMsg(txMsg interface{}) { 95 | msg := txMsg.(itypes.AssetIssueToken) 96 | 97 | m.Family = msg.Family.String() 98 | m.Source = msg.Source.String() 99 | m.Gateway = msg.Gateway 100 | m.Symbol = msg.Symbol 101 | m.CanonicalSymbol = msg.CanonicalSymbol 102 | m.Name = msg.Name 103 | m.Decimal = msg.Decimal 104 | m.MinUnitAlias = msg.MinUnitAlias 105 | m.InitialSupply = msg.InitialSupply 106 | m.MaxSupply = msg.MaxSupply 107 | m.Mintable = msg.Mintable 108 | m.Owner = msg.Owner.String() 109 | m.UdInfo = assetTokenUdInfo{ 110 | Source: msg.Source.String(), 111 | Gateway: msg.Gateway, 112 | Symbol: msg.Symbol, 113 | } 114 | } 115 | 116 | func (m *DocTxMsgEditToken) Type() string { 117 | return constant.TxMsgTypeAssetEditToken 118 | } 119 | 120 | func (m *DocTxMsgEditToken) BuildMsg(txMsg interface{}) { 121 | msg := txMsg.(itypes.AssetEditToken) 122 | 123 | m.TokenId = msg.TokenId 124 | m.Owner = msg.Owner.String() 125 | m.CanonicalSymbol = msg.CanonicalSymbol 126 | m.MinUnitAlias = msg.MinUnitAlias 127 | m.MaxSupply = msg.MaxSupply 128 | switch msg.Mintable { 129 | case constant.TrueStr: 130 | m.Mintable = true 131 | break 132 | case constant.FalseStr: 133 | m.Mintable = false 134 | break 135 | } 136 | m.Name = msg.Name 137 | m.UdInfo = getAssetTokenUdInfo(msg.TokenId) 138 | } 139 | 140 | func (m *DocTxMsgMintToken) Type() string { 141 | return constant.TxMsgTypeAssetMintToken 142 | } 143 | 144 | func (m *DocTxMsgMintToken) BuildMsg(txMsg interface{}) { 145 | msg := txMsg.(itypes.AssetMintToken) 146 | 147 | m.TokenId = msg.TokenId 148 | m.Owner = msg.Owner.String() 149 | m.To = msg.To.String() 150 | m.Amount = msg.Amount 151 | m.UdInfo = getAssetTokenUdInfo(msg.TokenId) 152 | } 153 | 154 | func (m *DocTxMsgTransferTokenOwner) Type() string { 155 | return constant.TxMsgTypeAssetTransferTokenOwner 156 | } 157 | 158 | func (m *DocTxMsgTransferTokenOwner) BuildMsg(txMsg interface{}) { 159 | msg := txMsg.(itypes.AssetTransferTokenOwner) 160 | 161 | m.SrcOwner = msg.SrcOwner.String() 162 | m.DstOwner = msg.DstOwner.String() 163 | m.TokenId = msg.TokenId 164 | m.UdInfo = getAssetTokenUdInfo(msg.TokenId) 165 | } 166 | 167 | func (m *DocTxMsgCreateGateway) Type() string { 168 | return constant.TxMsgTypeAssetCreateGateway 169 | } 170 | 171 | func (m *DocTxMsgCreateGateway) BuildMsg(txMsg interface{}) { 172 | msg := txMsg.(itypes.AssetCreateGateway) 173 | 174 | m.Owner = msg.Owner.String() 175 | m.Moniker = msg.Moniker 176 | m.Identity = msg.Identity 177 | m.Details = msg.Details 178 | m.Website = msg.Website 179 | } 180 | 181 | func (m *DocTxMsgEditGateway) Type() string { 182 | return constant.TxMsgTypeAssetEditGateway 183 | } 184 | 185 | func (m *DocTxMsgEditGateway) BuildMsg(txMsg interface{}) { 186 | msg := txMsg.(itypes.AssetEditGateWay) 187 | 188 | m.Owner = msg.Owner.String() 189 | m.Moniker = msg.Moniker 190 | m.Identity = msg.Identity 191 | m.Details = msg.Details 192 | m.Website = msg.Website 193 | } 194 | 195 | func (m *DocTxMsgTransferGatewayOwner) Type() string { 196 | return constant.TxMsgTypeAssetTransferGatewayOwner 197 | } 198 | 199 | func (m *DocTxMsgTransferGatewayOwner) BuildMsg(txMsg interface{}) { 200 | msg := txMsg.(itypes.AssetTransferGatewayOwner) 201 | 202 | m.Owner = msg.Owner.String() 203 | m.Moniker = msg.Moniker 204 | m.To = msg.To.String() 205 | } 206 | 207 | // get assetTokenUdInfo by parse tokenId 208 | // Global Unique Token ID Generation Rule 209 | // When Source is native: ID = [Symbol], e.g. iris 210 | // When Source is external: ID = x.[Symbol], e.g. x.btc 211 | // When Source is gateway: ID = [Gateway].[Symbol], e.g. cats.kitty 212 | func getAssetTokenUdInfo(tokenId string) assetTokenUdInfo { 213 | var ( 214 | source, gateway, symbol string 215 | ) 216 | strs := strings.Split(tokenId, separatorOfSymbolInTokenId) 217 | 218 | switch len(strs) { 219 | case 1: 220 | source = SourceNative 221 | symbol = tokenId 222 | break 223 | case 2: 224 | if strs[0] == externalTokenPrefix { 225 | source = SourceExternal 226 | symbol = strs[1] 227 | } else { 228 | source = SourceGateWay 229 | gateway = strs[0] 230 | symbol = strs[1] 231 | } 232 | default: 233 | logger.Warn("can't get assetUserDefinedInfo by tokenId", logger.String("tokenId", tokenId)) 234 | } 235 | 236 | return assetTokenUdInfo{ 237 | Source: source, 238 | Gateway: gateway, 239 | Symbol: symbol, 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /types/msg/bank.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | itypes "github.com/irisnet/irishub-sync/types" 6 | "github.com/irisnet/irishub-sync/util/constant" 7 | ) 8 | 9 | type DocTxMsgSetMemoRegexp struct { 10 | Owner string `bson:"owner"` 11 | MemoRegexp string `bson:"memo_regexp"` 12 | } 13 | 14 | func (doctx *DocTxMsgSetMemoRegexp) Type() string { 15 | return constant.TxTypeSetMemoRegexp 16 | } 17 | 18 | func (doctx *DocTxMsgSetMemoRegexp) BuildMsg(txMsg interface{}) { 19 | msg := txMsg.(itypes.MsgSetMemoRegexp) 20 | doctx.MemoRegexp = msg.MemoRegexp 21 | doctx.Owner = msg.Owner.String() 22 | } 23 | 24 | // MsgBurn - high level transaction of the coin module 25 | type DocTxMsgBurn struct { 26 | Owner string `bson:"owner"` 27 | Coins store.Coins `bson:"coins"` 28 | } 29 | 30 | func (doctx *DocTxMsgBurn) Type() string { 31 | return constant.TxTypeBurn 32 | } 33 | 34 | func (doctx *DocTxMsgBurn) BuildMsg(txMsg interface{}) { 35 | msg := txMsg.(itypes.MsgBurn) 36 | doctx.Owner = msg.Owner.String() 37 | doctx.Coins = itypes.ParseCoins(msg.Coins.String()) 38 | } 39 | 40 | // MsgSend - high level transaction of the coin module 41 | type DocTxMsgSend struct { 42 | Inputs []Data `bson:"inputs"` 43 | Outputs []Data `bson:"outputs"` 44 | } 45 | 46 | // Transaction 47 | type Data struct { 48 | Address string `bson:"address"` 49 | Coins store.Coins `bson:"coins"` 50 | } 51 | 52 | func (doctx *DocTxMsgSend) Type() string { 53 | return constant.TxTypeTransfer 54 | } 55 | 56 | func (doctx *DocTxMsgSend) BuildMsg(txMsg interface{}) { 57 | msg := txMsg.(itypes.MsgTransfer) 58 | doctx.Inputs = append(doctx.Inputs, Data{ 59 | Address: msg.Inputs[0].Address.String(), 60 | Coins: itypes.ParseCoins(msg.Inputs[0].Coins.String()), 61 | }) 62 | doctx.Outputs = append(doctx.Outputs, Data{ 63 | Address: msg.Outputs[0].Address.String(), 64 | Coins: itypes.ParseCoins(msg.Outputs[0].Coins.String()), 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /types/msg/coinswap.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | itypes "github.com/irisnet/irishub-sync/types" 6 | "github.com/irisnet/irishub-sync/util/constant" 7 | ) 8 | 9 | type DocTxMsgAddLiquidity struct { 10 | MaxToken store.Coin `bson:"max_token"` // coin to be deposited as liquidity with an upper bound for its amount 11 | ExactIrisAmt string `bson:"exact_iris_amt"` // exact amount of native asset being add to the liquidity pool 12 | MinLiquidity string `bson:"min_liquidity"` // lower bound UNI sender is willing to accept for deposited coins 13 | Deadline int64 `bson:"deadline"` 14 | Sender string `bson:"sender"` 15 | } 16 | 17 | func (doctx *DocTxMsgAddLiquidity) Type() string { 18 | return constant.TxTypeAddLiquidity 19 | } 20 | 21 | func (doctx *DocTxMsgAddLiquidity) BuildMsg(txMsg interface{}) { 22 | msg := txMsg.(itypes.MsgAddLiquidity) 23 | doctx.Sender = msg.Sender.String() 24 | doctx.MinLiquidity = msg.MinLiquidity.String() 25 | doctx.ExactIrisAmt = msg.ExactIrisAmt.String() 26 | doctx.Deadline = msg.Deadline 27 | doctx.MaxToken = itypes.ParseCoin(msg.MaxToken.String()) 28 | } 29 | 30 | type DocTxMsgRemoveLiquidity struct { 31 | MinToken string `bson:"min_token"` // coin to be withdrawn with a lower bound for its amount 32 | WithdrawLiquidity store.Coin `bson:"withdraw_liquidity"` // amount of UNI to be burned to withdraw liquidity from a reserve pool 33 | MinIrisAmt string `bson:"min_iris_amt"` // minimum amount of the native asset the sender is willing to accept 34 | Deadline int64 `bson:"deadline"` 35 | Sender string `bson:"sender"` 36 | } 37 | 38 | func (doctx *DocTxMsgRemoveLiquidity) Type() string { 39 | return constant.TxTypeRemoveLiquidity 40 | } 41 | 42 | func (doctx *DocTxMsgRemoveLiquidity) BuildMsg(txMsg interface{}) { 43 | msg := txMsg.(itypes.MsgRemoveLiquidity) 44 | doctx.Sender = msg.Sender.String() 45 | doctx.MinIrisAmt = msg.MinIrisAmt.String() 46 | doctx.MinToken = msg.MinToken.String() 47 | doctx.Deadline = msg.Deadline 48 | doctx.WithdrawLiquidity = itypes.ParseCoin(msg.WithdrawLiquidity.String()) 49 | } 50 | 51 | type DocTxMsgSwapOrder struct { 52 | Input Input `bson:"input"` // the amount the sender is trading 53 | Output Output `bson:"output"` // the amount the sender is receiving 54 | Deadline int64 `bson:"deadline"` // deadline for the transaction to still be considered valid 55 | IsBuyOrder bool `bson:"is_buy_order"` // boolean indicating whether the order should be treated as a buy or sell 56 | } 57 | 58 | type Input struct { 59 | Address string `bson:"address"` 60 | Coin store.Coin `bson:"coin"` 61 | } 62 | 63 | type Output struct { 64 | Address string `bson:"address"` 65 | Coin store.Coin `bson:"coin"` 66 | } 67 | 68 | func (doctx *DocTxMsgSwapOrder) Type() string { 69 | return constant.TxTypeSwapOrder 70 | } 71 | 72 | func (doctx *DocTxMsgSwapOrder) BuildMsg(txMsg interface{}) { 73 | msg := txMsg.(itypes.MsgSwapOrder) 74 | doctx.Deadline = msg.Deadline 75 | doctx.IsBuyOrder = msg.IsBuyOrder 76 | doctx.Input = Input{ 77 | Address: msg.Input.Address.String(), 78 | Coin: itypes.ParseCoin(msg.Input.Coin.String()), 79 | } 80 | doctx.Output = Output{ 81 | Address: msg.Output.Address.String(), 82 | Coin: itypes.ParseCoin(msg.Output.Coin.String()), 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /types/msg/distribution.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | itypes "github.com/irisnet/irishub-sync/types" 5 | "github.com/irisnet/irishub-sync/util/constant" 6 | ) 7 | 8 | // msg struct for changing the withdraw address for a delegator (or validator self-delegation) 9 | type DocTxMsgSetWithdrawAddress struct { 10 | DelegatorAddr string `bson:"delegator_addr"` 11 | WithdrawAddr string `bson:"withdraw_addr"` 12 | } 13 | 14 | func (doctx *DocTxMsgSetWithdrawAddress) Type() string { 15 | return constant.TxTypeSetWithdrawAddress 16 | } 17 | 18 | func (doctx *DocTxMsgSetWithdrawAddress) BuildMsg(txMsg interface{}) { 19 | msg := txMsg.(itypes.MsgSetWithdrawAddress) 20 | doctx.DelegatorAddr = msg.DelegatorAddr.String() 21 | doctx.WithdrawAddr = msg.WithdrawAddr.String() 22 | } 23 | 24 | // msg struct for delegation withdraw from a single validator 25 | type DocTxMsgWithdrawDelegatorReward struct { 26 | DelegatorAddr string `bson:"delegator_addr"` 27 | ValidatorAddr string `bson:"validator_addr"` 28 | } 29 | 30 | func (doctx *DocTxMsgWithdrawDelegatorReward) Type() string { 31 | return constant.TxTypeWithdrawDelegatorReward 32 | } 33 | 34 | func (doctx *DocTxMsgWithdrawDelegatorReward) BuildMsg(txMsg interface{}) { 35 | msg := txMsg.(itypes.MsgWithdrawDelegatorReward) 36 | doctx.DelegatorAddr = msg.DelegatorAddr.String() 37 | doctx.ValidatorAddr = msg.ValidatorAddr.String() 38 | } 39 | 40 | // msg struct for delegation withdraw for all of the delegator's delegations 41 | type DocTxMsgWithdrawDelegatorRewardsAll struct { 42 | DelegatorAddr string `bson:"delegator_addr"` 43 | } 44 | 45 | func (doctx *DocTxMsgWithdrawDelegatorRewardsAll) Type() string { 46 | return constant.TxTypeWithdrawDelegatorRewardsAll 47 | } 48 | 49 | func (doctx *DocTxMsgWithdrawDelegatorRewardsAll) BuildMsg(txMsg interface{}) { 50 | msg := txMsg.(itypes.MsgWithdrawDelegatorRewardsAll) 51 | doctx.DelegatorAddr = msg.DelegatorAddr.String() 52 | } 53 | 54 | // msg struct for validator withdraw 55 | type DocTxMsgWithdrawValidatorRewardsAll struct { 56 | ValidatorAddr string `bson:"validator_addr"` 57 | } 58 | 59 | func (doctx *DocTxMsgWithdrawValidatorRewardsAll) Type() string { 60 | return constant.TxTypeWithdrawValidatorRewardsAll 61 | } 62 | 63 | func (doctx *DocTxMsgWithdrawValidatorRewardsAll) BuildMsg(txMsg interface{}) { 64 | msg := txMsg.(itypes.MsgWithdrawValidatorRewardsAll) 65 | doctx.ValidatorAddr = msg.ValidatorAddr.String() 66 | } 67 | -------------------------------------------------------------------------------- /types/msg/gov.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | itypes "github.com/irisnet/irishub-sync/types" 6 | "github.com/irisnet/irishub-sync/util/constant" 7 | "github.com/irisnet/irishub/app/v1/gov" 8 | ) 9 | 10 | type DocTxMsgSubmitProposal struct { 11 | Title string `bson:"title"` // Title of the proposal 12 | Description string `bson:"description"` // Description of the proposal 13 | Proposer string `bson:"proposer"` // Address of the proposer 14 | InitialDeposit store.Coins `bson:"initialDeposit"` // Initial deposit paid by sender. Must be strictly positive. 15 | ProposalType string `bson:"proposalType"` // Initial deposit paid by sender. Must be strictly positive. 16 | Params Params `bson:"params"` 17 | } 18 | 19 | type Param struct { 20 | Subspace string `json:"subspace" bson:"subspace"` 21 | Key string `json:"key" bson:"key"` 22 | Value string `json:"value" bson:"value"` 23 | } 24 | 25 | type Params []Param 26 | 27 | func (doctx *DocTxMsgSubmitProposal) Type() string { 28 | return constant.TxTypeSubmitProposal 29 | } 30 | 31 | func (doctx *DocTxMsgSubmitProposal) BuildMsg(txMsg interface{}) { 32 | msg := txMsg.(itypes.MsgSubmitProposal) 33 | doctx.Title = msg.Title 34 | doctx.Description = msg.Description 35 | doctx.ProposalType = msg.ProposalType.String() 36 | doctx.Proposer = msg.Proposer.String() 37 | doctx.Params = loadParams(msg.Params) 38 | doctx.InitialDeposit = itypes.ParseCoins(msg.InitialDeposit.String()) 39 | } 40 | 41 | func loadParams(params []gov.Param) (result []Param) { 42 | for _, val := range params { 43 | result = append(result, Param{Subspace: val.Subspace, Value: val.Value, Key: val.Key}) 44 | } 45 | return 46 | } 47 | 48 | type DocTxMsgSubmitSoftwareUpgradeProposal struct { 49 | DocTxMsgSubmitProposal 50 | Version uint64 `bson:"version"` 51 | Software string `bson:"software"` 52 | SwitchHeight uint64 `bson:"switch_height"` 53 | Threshold string `bson:"threshold"` 54 | } 55 | 56 | func (doctx *DocTxMsgSubmitSoftwareUpgradeProposal) Type() string { 57 | return constant.TxMsgTypeSubmitSoftwareUpgradeProposal 58 | } 59 | 60 | func (doctx *DocTxMsgSubmitSoftwareUpgradeProposal) BuildMsg(txMsg interface{}) { 61 | msg := txMsg.(itypes.MsgSubmitSoftwareUpgradeProposal) 62 | doctx.Title = msg.Title 63 | doctx.Description = msg.Description 64 | doctx.ProposalType = msg.ProposalType.String() 65 | doctx.Proposer = msg.Proposer.String() 66 | doctx.Params = loadParams(msg.Params) 67 | doctx.InitialDeposit = itypes.ParseCoins(msg.InitialDeposit.String()) 68 | doctx.Version = msg.Version 69 | doctx.Software = msg.Software 70 | doctx.SwitchHeight = msg.SwitchHeight 71 | doctx.Threshold = msg.Threshold.String() 72 | } 73 | 74 | type DocTxMsgSubmitCommunityTaxUsageProposal struct { 75 | DocTxMsgSubmitProposal 76 | Usage string `bson:"usage"` 77 | DestAddress string `bson:"dest_address"` 78 | Percent string `bson:"percent"` 79 | } 80 | 81 | func (doctx *DocTxMsgSubmitCommunityTaxUsageProposal) Type() string { 82 | return constant.TxMsgTypeSubmitTaxUsageProposal 83 | } 84 | 85 | func (doctx *DocTxMsgSubmitCommunityTaxUsageProposal) BuildMsg(txMsg interface{}) { 86 | msg := txMsg.(itypes.MsgSubmitTaxUsageProposal) 87 | doctx.Title = msg.Title 88 | doctx.Description = msg.Description 89 | doctx.ProposalType = msg.ProposalType.String() 90 | doctx.Proposer = msg.Proposer.String() 91 | doctx.Params = loadParams(msg.Params) 92 | doctx.InitialDeposit = itypes.ParseCoins(msg.InitialDeposit.String()) 93 | doctx.Usage = msg.Usage.String() 94 | doctx.DestAddress = msg.DestAddress.String() 95 | doctx.Percent = msg.Percent.String() 96 | } 97 | 98 | type DocTxMsgSubmitTokenAdditionProposal struct { 99 | DocTxMsgSubmitProposal 100 | Symbol string `bson:"symbol"` 101 | CanonicalSymbol string `bson:"canonical_symbol"` 102 | Name string `bson:"name"` 103 | Decimal uint8 `bson:"decimal"` 104 | MinUnitAlias string `bson:"min_unit_alias"` 105 | //InitialSupply uint64 `bson:"initial_supply"` 106 | } 107 | 108 | func (doctx *DocTxMsgSubmitTokenAdditionProposal) Type() string { 109 | return constant.TxMsgTypeSubmitTokenAdditionProposal 110 | } 111 | 112 | func (doctx *DocTxMsgSubmitTokenAdditionProposal) BuildMsg(txMsg interface{}) { 113 | msg := txMsg.(itypes.MsgSubmitTokenAdditionProposal) 114 | var params Params 115 | for _, p := range msg.Params { 116 | params = append(params, Param{ 117 | Subspace: p.Subspace, 118 | Key: p.Key, 119 | Value: p.Value, 120 | }) 121 | } 122 | doctx.Title = msg.Title 123 | doctx.Description = msg.Description 124 | doctx.ProposalType = msg.ProposalType.String() 125 | doctx.Proposer = msg.Proposer.String() 126 | doctx.Params = params 127 | doctx.InitialDeposit = itypes.ParseCoins(msg.InitialDeposit.String()) 128 | doctx.Symbol = msg.Symbol 129 | doctx.MinUnitAlias = msg.MinUnitAlias 130 | doctx.CanonicalSymbol = msg.CanonicalSymbol 131 | doctx.Name = msg.Name 132 | doctx.Decimal = msg.Decimal 133 | } 134 | 135 | // MsgVote 136 | type DocTxMsgVote struct { 137 | ProposalID uint64 `bson:"proposal_id"` // ID of the proposal 138 | Voter string `bson:"voter"` // address of the voter 139 | Option string `bson:"option"` // option from OptionSet chosen by the voter 140 | } 141 | 142 | func (doctx *DocTxMsgVote) Type() string { 143 | return constant.TxTypeVote 144 | } 145 | 146 | func (doctx *DocTxMsgVote) BuildMsg(txMsg interface{}) { 147 | msg := txMsg.(itypes.MsgVote) 148 | doctx.Voter = msg.Voter.String() 149 | doctx.Option = msg.Option.String() 150 | doctx.ProposalID = msg.ProposalID 151 | } 152 | 153 | // MsgDeposit 154 | type DocTxMsgDeposit struct { 155 | ProposalID uint64 `bson:"proposal_id"` // ID of the proposal 156 | Depositor string `bson:"depositor"` // Address of the depositor 157 | Amount store.Coins `bson:"amount"` // Coins to add to the proposal's deposit 158 | } 159 | 160 | func (doctx *DocTxMsgDeposit) Type() string { 161 | return constant.TxTypeDeposit 162 | } 163 | 164 | func (doctx *DocTxMsgDeposit) BuildMsg(txMsg interface{}) { 165 | msg := txMsg.(itypes.MsgDeposit) 166 | doctx.Depositor = msg.Depositor.String() 167 | doctx.Amount = itypes.ParseCoins(msg.Amount.String()) 168 | doctx.ProposalID = msg.ProposalID 169 | } 170 | -------------------------------------------------------------------------------- /types/msg/guardian.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | itypes "github.com/irisnet/irishub-sync/types" 5 | "github.com/irisnet/irishub-sync/util/constant" 6 | ) 7 | 8 | type DocTxMsgAddProfiler struct { 9 | AddGuardian 10 | } 11 | 12 | func (doctx *DocTxMsgAddProfiler) Type() string { 13 | return constant.TxTypeAddProfiler 14 | } 15 | 16 | func (doctx *DocTxMsgAddProfiler) BuildMsg(txMsg interface{}) { 17 | msg := txMsg.(itypes.MsgAddProfiler) 18 | doctx.Address = msg.Address.String() 19 | doctx.AddedBy = msg.AddedBy.String() 20 | doctx.Description = msg.Description 21 | } 22 | 23 | type DocTxMsgAddTrustee struct { 24 | AddGuardian 25 | } 26 | 27 | func (doctx *DocTxMsgAddTrustee) Type() string { 28 | return constant.TxTypeAddTrustee 29 | } 30 | 31 | func (doctx *DocTxMsgAddTrustee) BuildMsg(txMsg interface{}) { 32 | msg := txMsg.(itypes.MsgAddTrustee) 33 | doctx.Address = msg.Address.String() 34 | doctx.AddedBy = msg.AddedBy.String() 35 | doctx.Description = msg.Description 36 | } 37 | 38 | type AddGuardian struct { 39 | Description string `bson:"description"` 40 | Address string `bson:"address"` // address added 41 | AddedBy string `bson:"added_by"` // address that initiated the tx 42 | } 43 | 44 | type DocTxMsgDeleteProfiler struct { 45 | DeleteGuardian 46 | } 47 | 48 | func (doctx *DocTxMsgDeleteProfiler) Type() string { 49 | return constant.TxTypeDeleteProfiler 50 | } 51 | 52 | func (doctx *DocTxMsgDeleteProfiler) BuildMsg(txMsg interface{}) { 53 | msg := txMsg.(itypes.MsgDeleteProfiler) 54 | doctx.Address = msg.Address.String() 55 | doctx.DeletedBy = msg.DeletedBy.String() 56 | } 57 | 58 | type DocTxMsgDeleteTrustee struct { 59 | DeleteGuardian 60 | } 61 | 62 | func (doctx *DocTxMsgDeleteTrustee) Type() string { 63 | return constant.TxTypeDeleteTrustee 64 | } 65 | 66 | func (doctx *DocTxMsgDeleteTrustee) BuildMsg(txMsg interface{}) { 67 | msg := txMsg.(itypes.MsgDeleteTrustee) 68 | doctx.Address = msg.Address.String() 69 | doctx.DeletedBy = msg.DeletedBy.String() 70 | } 71 | 72 | type DeleteGuardian struct { 73 | Address string `bson:"address"` // address deleted 74 | DeletedBy string `bson:"deleted_by"` // address that initiated the tx 75 | } 76 | -------------------------------------------------------------------------------- /types/msg/htlc.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "encoding/hex" 5 | "github.com/irisnet/irishub-sync/store" 6 | itypes "github.com/irisnet/irishub-sync/types" 7 | "github.com/irisnet/irishub-sync/util/constant" 8 | ) 9 | 10 | type DocTxMsgCreateHTLC struct { 11 | Sender string `bson:"sender"` // the initiator address 12 | To string `bson:"to"` // the destination address 13 | ReceiverOnOtherChain string `bson:"receiver_on_other_chain"` // the claim receiving address on the other chain 14 | Amount store.Coins `bson:"amount"` // the amount to be transferred 15 | HashLock string `bson:"hash_lock"` // the hash lock generated from secret (and timestamp if provided) 16 | Timestamp uint64 `bson:"timestamp"` // if provided, used to generate the hash lock together with secret 17 | TimeLock uint64 `bson:"time_lock"` // the time span after which the HTLC will expire 18 | } 19 | 20 | func (doctx *DocTxMsgCreateHTLC) Type() string { 21 | return constant.TxTypeCreateHTLC 22 | } 23 | 24 | func (doctx *DocTxMsgCreateHTLC) BuildMsg(txMsg interface{}) { 25 | msg := txMsg.(itypes.MsgCreateHTLC) 26 | doctx.Sender = msg.Sender.String() 27 | doctx.To = msg.To.String() 28 | doctx.Amount = itypes.ParseCoins(msg.Amount.String()) 29 | doctx.Timestamp = msg.Timestamp 30 | doctx.HashLock = hex.EncodeToString(msg.HashLock) 31 | doctx.TimeLock = msg.TimeLock 32 | doctx.ReceiverOnOtherChain = msg.ReceiverOnOtherChain 33 | } 34 | 35 | type DocTxMsgClaimHTLC struct { 36 | Sender string `bson:"sender"` // the initiator address 37 | HashLock string `bson:"hash_lock"` // the hash lock identifying the HTLC to be claimed 38 | Secret string `bson:"secret"` // the secret with which to claim 39 | } 40 | 41 | func (doctx *DocTxMsgClaimHTLC) Type() string { 42 | return constant.TxTypeClaimHTLC 43 | } 44 | 45 | func (doctx *DocTxMsgClaimHTLC) BuildMsg(txMsg interface{}) { 46 | msg := txMsg.(itypes.MsgClaimHTLC) 47 | doctx.Sender = msg.Sender.String() 48 | doctx.Secret = hex.EncodeToString(msg.Secret) 49 | doctx.HashLock = hex.EncodeToString(msg.HashLock) 50 | } 51 | 52 | type DocTxMsgRefundHTLC struct { 53 | Sender string `bson:"sender"` // the initiator address 54 | HashLock string `bson:"hash_lock"` // the hash lock identifying the HTLC to be refunded 55 | } 56 | 57 | func (doctx *DocTxMsgRefundHTLC) Type() string { 58 | return constant.TxTypeRefundHTLC 59 | } 60 | 61 | func (doctx *DocTxMsgRefundHTLC) BuildMsg(txMsg interface{}) { 62 | msg := txMsg.(itypes.MsgRefundHTLC) 63 | doctx.Sender = msg.Sender.String() 64 | doctx.HashLock = hex.EncodeToString(msg.HashLock) 65 | } 66 | -------------------------------------------------------------------------------- /types/msg/rand.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | itypes "github.com/irisnet/irishub-sync/types" 5 | "github.com/irisnet/irishub-sync/util/constant" 6 | ) 7 | 8 | type DocTxMsgRequestRand struct { 9 | Consumer string `bson:"consumer"` // request address 10 | BlockInterval uint64 `bson:"block-interval"` // block interval after which the requested random number will be generated 11 | } 12 | 13 | func (doctx *DocTxMsgRequestRand) Type() string { 14 | return constant.TxTypeRequestRand 15 | } 16 | 17 | func (doctx *DocTxMsgRequestRand) BuildMsg(txMsg interface{}) { 18 | msg := txMsg.(itypes.MsgRequestRand) 19 | doctx.Consumer = msg.Consumer.String() 20 | doctx.BlockInterval = msg.BlockInterval 21 | } 22 | -------------------------------------------------------------------------------- /types/msg/stake.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/store" 5 | "github.com/irisnet/irishub-sync/store/document" 6 | itypes "github.com/irisnet/irishub-sync/types" 7 | "github.com/irisnet/irishub-sync/util/constant" 8 | "github.com/irisnet/irishub/app/v1/stake" 9 | ) 10 | 11 | // MsgDelegate - struct for bonding transactions 12 | type DocTxMsgBeginRedelegate struct { 13 | DelegatorAddr string `bson:"delegator_addr"` 14 | ValidatorSrcAddr string `bson:"validator_src_addr"` 15 | ValidatorDstAddr string `bson:"validator_dst_addr"` 16 | SharesAmount string `bson:"shares_amount"` 17 | } 18 | 19 | func (doctx *DocTxMsgBeginRedelegate) Type() string { 20 | return constant.TxTypeBeginRedelegate 21 | } 22 | 23 | func (doctx *DocTxMsgBeginRedelegate) BuildMsg(txMsg interface{}) { 24 | msg := txMsg.(itypes.MsgBeginRedelegate) 25 | doctx.DelegatorAddr = msg.DelegatorAddr.String() 26 | doctx.ValidatorSrcAddr = msg.ValidatorSrcAddr.String() 27 | doctx.ValidatorDstAddr = msg.ValidatorDstAddr.String() 28 | doctx.SharesAmount = msg.SharesAmount.String() 29 | } 30 | 31 | // MsgUnjail - struct for unjailing jailed validator 32 | type DocTxMsgUnjail struct { 33 | ValidatorAddr string `bson:"address"` // address of the validator operator 34 | } 35 | 36 | func (doctx *DocTxMsgUnjail) Type() string { 37 | return constant.TxTypeUnjail 38 | } 39 | 40 | func (doctx *DocTxMsgUnjail) BuildMsg(txMsg interface{}) { 41 | msg := txMsg.(itypes.MsgUnjail) 42 | doctx.ValidatorAddr = msg.ValidatorAddr.String() 43 | } 44 | 45 | // MsgBeginUnbonding - struct for unbonding transactions 46 | type DocTxMsgBeginUnbonding struct { 47 | DelegatorAddr string `bson:"delegator_addr"` 48 | ValidatorAddr string `bson:"validator_addr"` 49 | SharesAmount string `bson:"shares_amount"` 50 | } 51 | 52 | func (doctx *DocTxMsgBeginUnbonding) Type() string { 53 | return constant.TxTypeStakeBeginUnbonding 54 | } 55 | 56 | func (doctx *DocTxMsgBeginUnbonding) BuildMsg(txMsg interface{}) { 57 | msg := txMsg.(itypes.MsgStakeBeginUnbonding) 58 | doctx.ValidatorAddr = msg.ValidatorAddr.String() 59 | doctx.DelegatorAddr = msg.DelegatorAddr.String() 60 | doctx.SharesAmount = msg.SharesAmount.String() 61 | } 62 | 63 | // MsgDelegate - struct for bonding transactions 64 | type DocTxMsgDelegate struct { 65 | DelegatorAddr string `bson:"delegator_addr"` 66 | ValidatorAddr string `bson:"validator_addr"` 67 | Delegation store.Coin `bson:"delegation"` 68 | } 69 | 70 | func (doctx *DocTxMsgDelegate) Type() string { 71 | return constant.TxTypeStakeDelegate 72 | } 73 | 74 | func (doctx *DocTxMsgDelegate) BuildMsg(txMsg interface{}) { 75 | msg := txMsg.(itypes.MsgStakeDelegate) 76 | doctx.ValidatorAddr = msg.ValidatorAddr.String() 77 | doctx.DelegatorAddr = msg.DelegatorAddr.String() 78 | doctx.Delegation = itypes.ParseCoin(msg.Delegation.String()) 79 | } 80 | 81 | // MsgEditValidator - struct for editing a validator 82 | type DocTxMsgStakeEdit struct { 83 | document.ValDescription 84 | ValidatorAddr string `bson:"address"` 85 | CommissionRate string `bson:"commission_rate"` 86 | } 87 | 88 | func (doctx *DocTxMsgStakeEdit) Type() string { 89 | return constant.TxTypeStakeEditValidator 90 | } 91 | 92 | func (doctx *DocTxMsgStakeEdit) BuildMsg(txMsg interface{}) { 93 | msg := txMsg.(itypes.MsgStakeEdit) 94 | doctx.ValidatorAddr = msg.ValidatorAddr.String() 95 | commissionRate := msg.CommissionRate 96 | if commissionRate == nil { 97 | doctx.CommissionRate = "" 98 | } else { 99 | doctx.CommissionRate = commissionRate.String() 100 | } 101 | doctx.ValDescription = loadDescription(msg.Description) 102 | } 103 | 104 | type DocTxMsgStakeCreate struct { 105 | document.ValDescription 106 | Commission document.CommissionMsg 107 | DelegatorAddr string `bson:"delegator_address"` 108 | ValidatorAddr string `bson:"validator_address"` 109 | PubKey string `bson:"pubkey"` 110 | Delegation store.Coin `bson:"delegation"` 111 | } 112 | 113 | func (doctx *DocTxMsgStakeCreate) Type() string { 114 | return constant.TxTypeStakeCreateValidator 115 | } 116 | 117 | func (doctx *DocTxMsgStakeCreate) BuildMsg(txMsg interface{}) { 118 | msg := txMsg.(itypes.MsgStakeCreate) 119 | pubKey, err := itypes.Bech32ifyValPub(msg.PubKey) 120 | if err != nil { 121 | pubKey = "" 122 | } 123 | doctx.ValidatorAddr = msg.ValidatorAddr.String() 124 | doctx.PubKey = pubKey 125 | doctx.DelegatorAddr = msg.DelegatorAddr.String() 126 | doctx.Delegation = itypes.ParseCoin(msg.Delegation.String()) 127 | doctx.Commission = document.CommissionMsg{ 128 | Rate: msg.Commission.Rate.String(), 129 | MaxChangeRate: msg.Commission.MaxChangeRate.String(), 130 | MaxRate: msg.Commission.MaxRate.String(), 131 | } 132 | doctx.ValDescription = loadDescription(msg.Description) 133 | } 134 | 135 | func loadDescription(description stake.Description) document.ValDescription { 136 | return document.ValDescription{ 137 | Moniker: description.Moniker, 138 | Details: description.Details, 139 | Identity: description.Identity, 140 | Website: description.Website, 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /types/msg_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestNewSubmitSoftwareUpgradeProposal(t *testing.T) { 10 | submitProposal := MsgSubmitProposal{ 11 | Title: "title", 12 | Description: "description", 13 | } 14 | msg := MsgSubmitSoftwareUpgradeProposal{ 15 | MsgSubmitProposal: submitProposal, 16 | Version: uint64(1), 17 | Software: "aa", 18 | SwitchHeight: uint64(2), 19 | } 20 | 21 | bz, _ := json.Marshal(msg) 22 | fmt.Println(string(bz)) 23 | 24 | var p MsgSubmitProposal 25 | json.Unmarshal(bz, &p) 26 | 27 | bz1, _ := json.Marshal(p) 28 | fmt.Println(string(bz1)) 29 | } 30 | -------------------------------------------------------------------------------- /types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "github.com/irisnet/irishub-sync/conf/server" 6 | "github.com/irisnet/irishub-sync/logger" 7 | "github.com/irisnet/irishub-sync/store" 8 | "github.com/irisnet/irishub-sync/util/constant" 9 | "github.com/irisnet/irishub/app" 10 | "github.com/irisnet/irishub/app/v1/asset" 11 | "github.com/irisnet/irishub/app/v1/auth" 12 | "github.com/irisnet/irishub/app/v1/bank" 13 | "github.com/irisnet/irishub/app/v1/distribution" 14 | dtags "github.com/irisnet/irishub/app/v1/distribution/tags" 15 | dtypes "github.com/irisnet/irishub/app/v1/distribution/types" 16 | "github.com/irisnet/irishub/app/v1/gov" 17 | "github.com/irisnet/irishub/app/v1/gov/tags" 18 | "github.com/irisnet/irishub/app/v1/rand" 19 | "github.com/irisnet/irishub/app/v1/slashing" 20 | "github.com/irisnet/irishub/app/v1/stake" 21 | stags "github.com/irisnet/irishub/app/v1/stake/tags" 22 | staketypes "github.com/irisnet/irishub/app/v1/stake/types" 23 | "github.com/irisnet/irishub/app/v2/coinswap" 24 | "github.com/irisnet/irishub/app/v2/htlc" 25 | "github.com/irisnet/irishub/client/utils" 26 | "github.com/irisnet/irishub/codec" 27 | "github.com/irisnet/irishub/modules/guardian" 28 | "github.com/irisnet/irishub/types" 29 | abci "github.com/tendermint/tendermint/abci/types" 30 | cmn "github.com/tendermint/tendermint/libs/common" 31 | rpcclient "github.com/tendermint/tendermint/rpc/client" 32 | ctypes "github.com/tendermint/tendermint/rpc/core/types" 33 | tm "github.com/tendermint/tendermint/types" 34 | "regexp" 35 | "strconv" 36 | "strings" 37 | ) 38 | 39 | type ( 40 | MsgTransfer = bank.MsgSend 41 | MsgBurn = bank.MsgBurn 42 | MsgSetMemoRegexp = bank.MsgSetMemoRegexp 43 | 44 | MsgStakeCreate = stake.MsgCreateValidator 45 | MsgStakeEdit = stake.MsgEditValidator 46 | MsgStakeDelegate = stake.MsgDelegate 47 | MsgStakeBeginUnbonding = stake.MsgBeginUnbonding 48 | MsgBeginRedelegate = stake.MsgBeginRedelegate 49 | MsgUnjail = slashing.MsgUnjail 50 | MsgSetWithdrawAddress = dtypes.MsgSetWithdrawAddress 51 | MsgWithdrawDelegatorReward = distribution.MsgWithdrawDelegatorReward 52 | MsgWithdrawDelegatorRewardsAll = distribution.MsgWithdrawDelegatorRewardsAll 53 | MsgWithdrawValidatorRewardsAll = distribution.MsgWithdrawValidatorRewardsAll 54 | StakeValidator = stake.Validator 55 | Delegation = stake.Delegation 56 | UnbondingDelegation = stake.UnbondingDelegation 57 | 58 | MsgDeposit = gov.MsgDeposit 59 | MsgSubmitProposal = gov.MsgSubmitProposal 60 | MsgSubmitSoftwareUpgradeProposal = gov.MsgSubmitSoftwareUpgradeProposal 61 | MsgSubmitTaxUsageProposal = gov.MsgSubmitCommunityTaxUsageProposal 62 | MsgSubmitTokenAdditionProposal = gov.MsgSubmitTokenAdditionProposal 63 | MsgVote = gov.MsgVote 64 | Proposal = gov.Proposal 65 | SdkVote = gov.Vote 66 | 67 | MsgSwapOrder = coinswap.MsgSwapOrder 68 | MsgAddLiquidity = coinswap.MsgAddLiquidity 69 | MsgRemoveLiquidity = coinswap.MsgRemoveLiquidity 70 | 71 | MsgClaimHTLC = htlc.MsgClaimHTLC 72 | MsgCreateHTLC = htlc.MsgCreateHTLC 73 | MsgRefundHTLC = htlc.MsgRefundHTLC 74 | 75 | MsgRequestRand = rand.MsgRequestRand 76 | 77 | AssetIssueToken = asset.MsgIssueToken 78 | AssetEditToken = asset.MsgEditToken 79 | AssetMintToken = asset.MsgMintToken 80 | AssetTransferTokenOwner = asset.MsgTransferTokenOwner 81 | AssetCreateGateway = asset.MsgCreateGateway 82 | AssetEditGateWay = asset.MsgEditGateway 83 | AssetTransferGatewayOwner = asset.MsgTransferGatewayOwner 84 | 85 | MsgAddProfiler = guardian.MsgAddProfiler 86 | MsgAddTrustee = guardian.MsgAddTrustee 87 | MsgDeleteProfiler = guardian.MsgDeleteProfiler 88 | MsgDeleteTrustee = guardian.MsgDeleteTrustee 89 | 90 | ResponseDeliverTx = abci.ResponseDeliverTx 91 | 92 | StdTx = auth.StdTx 93 | SdkCoins = types.Coins 94 | KVPair = types.KVPair 95 | AccAddress = types.AccAddress 96 | ValAddress = types.ValAddress 97 | Dec = types.Dec 98 | Int = types.Int 99 | Validator = tm.Validator 100 | Tx = tm.Tx 101 | Block = tm.Block 102 | BlockMeta = tm.BlockMeta 103 | HexBytes = cmn.HexBytes 104 | TmKVPair = cmn.KVPair 105 | 106 | ABCIQueryOptions = rpcclient.ABCIQueryOptions 107 | Client = rpcclient.Client 108 | HTTP = rpcclient.HTTP 109 | ResultStatus = ctypes.ResultStatus 110 | ) 111 | 112 | var ( 113 | ValidatorsKey = stake.ValidatorsKey 114 | GetValidatorKey = stake.GetValidatorKey 115 | GetDelegationKey = stake.GetDelegationKey 116 | GetDelegationsKey = stake.GetDelegationsKey 117 | GetUBDKey = stake.GetUBDKey 118 | GetUBDsKey = stake.GetUBDsKey 119 | ValAddressFromBech32 = types.ValAddressFromBech32 120 | 121 | UnmarshalValidator = staketypes.UnmarshalValidator 122 | MustUnmarshalValidator = staketypes.MustUnmarshalValidator 123 | UnmarshalDelegation = staketypes.UnmarshalDelegation 124 | MustUnmarshalDelegation = staketypes.MustUnmarshalDelegation 125 | MustUnmarshalUBD = staketypes.MustUnmarshalUBD 126 | 127 | Bech32ifyValPub = types.Bech32ifyValPub 128 | Bech32AccountAddrPrefix string 129 | RegisterCodec = types.RegisterCodec 130 | AccAddressFromBech32 = types.AccAddressFromBech32 131 | BondStatusToString = types.BondStatusToString 132 | 133 | NewDecFromStr = types.NewDecFromStr 134 | 135 | AddressStoreKey = auth.AddressStoreKey 136 | GetAccountDecoder = utils.GetAccountDecoder 137 | 138 | KeyProposal = gov.KeyProposal 139 | KeyVotesSubspace = gov.KeyVotesSubspace 140 | 141 | NewHTTP = rpcclient.NewHTTP 142 | 143 | //tags 144 | TagGovProposalID = tags.ProposalID 145 | TagDistributionReward = dtags.Reward 146 | TagStakeActionCompleteRedelegation = stags.ActionCompleteRedelegation 147 | TagStakeDelegator = stags.Delegator 148 | TagStakeSrcValidator = stags.SrcValidator 149 | TagAction = types.TagAction 150 | 151 | cdc *codec.Codec 152 | ) 153 | 154 | // 初始化账户地址前缀 155 | func init() { 156 | if server.Network == constant.NetworkMainnet { 157 | types.SetNetworkType(types.Mainnet) 158 | } 159 | Bech32AccountAddrPrefix = types.GetConfig().GetBech32AccountAddrPrefix() 160 | cdc = app.MakeLatestCodec() 161 | } 162 | 163 | func GetCodec() *codec.Codec { 164 | return cdc 165 | } 166 | 167 | // 168 | func ParseCoins(coinsStr string) (coins store.Coins) { 169 | coinsStr = strings.TrimSpace(coinsStr) 170 | if len(coinsStr) == 0 { 171 | return 172 | } 173 | 174 | coinStrs := strings.Split(coinsStr, ",") 175 | for _, coinStr := range coinStrs { 176 | coin := ParseCoin(coinStr) 177 | coins = append(coins, coin) 178 | } 179 | return coins 180 | } 181 | 182 | func ParseCoin(coinStr string) (coin store.Coin) { 183 | var ( 184 | reDnm = `[A-Za-z]{1,}\S*` 185 | reAmt = `[0-9]+[.]?[0-9]*` 186 | reSpc = `[[:space:]]*` 187 | reCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, reDnm)) 188 | ) 189 | 190 | coinStr = strings.TrimSpace(coinStr) 191 | 192 | matches := reCoin.FindStringSubmatch(coinStr) 193 | if matches == nil { 194 | logger.Error("invalid coin expression", logger.Any("coin", coinStr)) 195 | return coin 196 | } 197 | denom, amount := matches[2], matches[1] 198 | 199 | amount = getPrecision(amount) 200 | amt, err := strconv.ParseFloat(amount, 64) 201 | if err != nil { 202 | logger.Error("Convert str to int failed", logger.Any("amount", amount)) 203 | return coin 204 | } 205 | 206 | return store.Coin{ 207 | Denom: denom, 208 | Amount: amt, 209 | } 210 | } 211 | 212 | func getPrecision(amount string) string { 213 | length := len(amount) 214 | 215 | if length > 15 { 216 | nums := strings.Split(amount, ".") 217 | if len(nums) > 2 { 218 | return amount 219 | } 220 | 221 | if len_num0 := len(nums[0]); len_num0 > 15 { 222 | amount = string([]byte(nums[0])[:15]) 223 | for i := 1; i <= len_num0-15; i++ { 224 | amount += "0" 225 | } 226 | } else { 227 | leng_append := 16 - len_num0 228 | amount = nums[0] + "." + string([]byte(nums[1])[:leng_append]) 229 | } 230 | } 231 | return amount 232 | } 233 | 234 | func BuildFee(fee auth.StdFee) store.Fee { 235 | return store.Fee{ 236 | Amount: ParseCoins(fee.Amount.String()), 237 | Gas: int64(fee.Gas), 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /types/types_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | func TestParseCoin(t *testing.T) { 11 | coin := ParseCoin("11111lc1") 12 | coinStr, _ := json.Marshal(coin) 13 | t.Log(string(coinStr)) 14 | } 15 | 16 | func Test_getPrecision(t *testing.T) { 17 | exam1 := "4999999999999999999999" 18 | exam2 := "49999999999999999999.99" 19 | exam3 := "49999999.999999.99999" 20 | exam4 := "49999999.999999" 21 | exam5 := "4999999999999.9999999" 22 | 23 | data := getPrecision(exam1) 24 | if data == "4999999999999990000000" { 25 | t.Log("OK") 26 | } else { 27 | t.Fatal("Failed") 28 | } 29 | data = getPrecision(exam2) 30 | if data == "49999999999999900000" { 31 | t.Log("OK") 32 | 33 | } else { 34 | t.Fatal("Failed") 35 | } 36 | data = getPrecision(exam3) 37 | if data == "49999999.999999.99999" { 38 | t.Log("OK") 39 | } else { 40 | t.Fatal("Failed") 41 | } 42 | data = getPrecision(exam4) 43 | if data == "49999999.999999" { 44 | t.Log("OK") 45 | } else { 46 | t.Fatal("Failed") 47 | } 48 | data = getPrecision(exam5) 49 | fmt.Println(data) 50 | amt, _ := strconv.ParseFloat(data, 64) 51 | fmt.Println(amt) 52 | if data == "4999999999999.999" { 53 | t.Log("OK") 54 | } else { 55 | t.Fatal("Failed") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /util/constant/types.go: -------------------------------------------------------------------------------- 1 | // package for define constants 2 | 3 | package constant 4 | 5 | const ( 6 | TxTypeTransfer = "Transfer" 7 | TxTypeBurn = "Burn" 8 | TxTypeSetMemoRegexp = "SetMemoRegexp" 9 | TxTypeStakeCreateValidator = "CreateValidator" 10 | TxTypeStakeEditValidator = "EditValidator" 11 | TxTypeStakeDelegate = "Delegate" 12 | TxTypeStakeBeginUnbonding = "BeginUnbonding" 13 | TxTypeBeginRedelegate = "BeginRedelegate" 14 | TxTypeUnjail = "Unjail" 15 | TxTypeSetWithdrawAddress = "SetWithdrawAddress" 16 | TxTypeWithdrawDelegatorReward = "WithdrawDelegatorReward" 17 | TxTypeWithdrawDelegatorRewardsAll = "WithdrawDelegatorRewardsAll" 18 | TxTypeWithdrawValidatorRewardsAll = "WithdrawValidatorRewardsAll" 19 | TxTypeSubmitProposal = "SubmitProposal" 20 | TxTypeDeposit = "Deposit" 21 | TxTypeVote = "Vote" 22 | TxTypeRequestRand = "RequestRand" 23 | TxTypeAssetIssueToken = "IssueToken" 24 | TxTypeAssetEditToken = "EditToken" 25 | TxTypeAssetMintToken = "MintToken" 26 | TxTypeAssetTransferTokenOwner = "TransferTokenOwner" 27 | TxTypeAssetCreateGateway = "CreateGateway" 28 | TxTypeAssetEditGateway = "EditGateway" 29 | TxTypeAssetTransferGatewayOwner = "TransferGatewayOwner" 30 | 31 | TxTypeAddProfiler = "AddProfiler" 32 | TxTypeAddTrustee = "AddTrustee" 33 | TxTypeDeleteTrustee = "DeleteTrustee" 34 | TxTypeDeleteProfiler = "DeleteProfiler" 35 | 36 | TxTypeCreateHTLC = "CreateHTLC" 37 | TxTypeClaimHTLC = "ClaimHTLC" 38 | TxTypeRefundHTLC = "RefundHTLC" 39 | 40 | TxTypeAddLiquidity = "AddLiquidity" 41 | TxTypeRemoveLiquidity = "RemoveLiquidity" 42 | TxTypeSwapOrder = "SwapOrder" 43 | 44 | TxMsgTypeSubmitProposal = "SubmitProposal" 45 | TxMsgTypeSubmitSoftwareUpgradeProposal = "SubmitSoftwareUpgradeProposal" 46 | TxMsgTypeSubmitTaxUsageProposal = "SubmitTaxUsageProposal" 47 | TxMsgTypeSubmitTokenAdditionProposal = "SubmitTokenAdditionProposal" 48 | TxMsgTypeAssetIssueToken = "IssueToken" 49 | TxMsgTypeAssetEditToken = "EditToken" 50 | TxMsgTypeAssetMintToken = "MintToken" 51 | TxMsgTypeAssetTransferTokenOwner = "TransferTokenOwner" 52 | TxMsgTypeAssetCreateGateway = "CreateGateway" 53 | TxMsgTypeAssetEditGateway = "EditGateway" 54 | TxMsgTypeAssetTransferGatewayOwner = "TransferGatewayOwner" 55 | 56 | TxTagVotingPeriodStart = "voting-period-start" 57 | BlockTagProposalId = "proposal-id" 58 | 59 | EnvNameDbAddr = "DB_ADDR" 60 | EnvNameDbUser = "DB_USER" 61 | EnvNameDbPassWd = "DB_PASSWD" 62 | EnvNameDbDataBase = "DB_DATABASE" 63 | 64 | EnvNameSerNetworkFullNode = "SER_BC_FULL_NODE" 65 | EnvNameSerNetworkChainId = "SER_BC_CHAIN_ID" 66 | EnvNameWorkerNumCreateTask = "WORKER_NUM_CREATE_TASK" 67 | EnvNameWorkerNumExecuteTask = "WORKER_NUM_EXECUTE_TASK" 68 | 69 | EnvNameNetwork = "NETWORK" 70 | 71 | EnvLogFileName = "LOG_FILE_NAME" 72 | EnvLogFileMaxSize = "LOG_FILE_MAX_SIZE" 73 | EnvLogFileMaxAge = "LOG_FILE_MAX_AGE" 74 | EnvLogCompress = "LOG_COMPRESS" 75 | EnableAtomicLevel = "ENABLE_ATOMIC_LEVEL" 76 | 77 | // define store name 78 | StoreNameStake = "stake" 79 | StoreDefaultEndPath = "key" 80 | 81 | StatusDepositPeriod = "DepositPeriod" 82 | StatusVotingPeriod = "VotingPeriod" 83 | StatusPassed = "Passed" 84 | StatusRejected = "Rejected" 85 | 86 | NetworkMainnet = "mainnet" 87 | 88 | // define coin unit 89 | IrisAttoUnit = "iris-atto" 90 | 91 | TrueStr = "true" 92 | FalseStr = "false" 93 | ) 94 | -------------------------------------------------------------------------------- /util/helper/account.go: -------------------------------------------------------------------------------- 1 | // This package is used for Query balance of account 2 | 3 | package helper 4 | 5 | import ( 6 | "github.com/irisnet/irishub-sync/logger" 7 | "github.com/irisnet/irishub-sync/store" 8 | "github.com/irisnet/irishub-sync/types" 9 | "github.com/irisnet/irishub-sync/util/constant" 10 | "github.com/tendermint/tendermint/libs/bech32" 11 | ) 12 | 13 | // query account balance from sdk store 14 | func QueryAccountInfo(address string) (store.Coins, uint64) { 15 | cdc := types.GetCodec() 16 | 17 | addr, err := types.AccAddressFromBech32(address) 18 | if err != nil { 19 | logger.Error("get addr from hex failed", logger.Any("err", err)) 20 | return nil, 0 21 | } 22 | 23 | res, err := Query(types.AddressStoreKey(addr), "acc", 24 | constant.StoreDefaultEndPath) 25 | 26 | if err != nil { 27 | logger.Error("Query balance from tendermint failed", logger.Any("err", err)) 28 | return nil, 0 29 | } 30 | 31 | // balance is empty 32 | if len(res) <= 0 { 33 | return nil, 0 34 | } 35 | 36 | decoder := types.GetAccountDecoder(cdc) 37 | account, err := decoder(res) 38 | if err != nil { 39 | logger.Error("decode account failed", logger.Any("err", err)) 40 | return nil, 0 41 | } 42 | 43 | return types.ParseCoins(account.GetCoins().String()), account.GetAccountNumber() 44 | } 45 | 46 | func ValAddrToAccAddr(address string) (accAddr string) { 47 | valAddr, err := types.ValAddressFromBech32(address) 48 | if err != nil { 49 | logger.Error("ValAddressFromBech32 decode account failed", logger.String("address", address)) 50 | return 51 | } 52 | 53 | return types.AccAddress(valAddr.Bytes()).String() 54 | } 55 | 56 | // convert account address from hex to bech32 57 | func ConvertAccountAddrFromHexToBech32(address []byte) (string, error) { 58 | return bech32.ConvertAndEncode(types.Bech32AccountAddrPrefix, address) 59 | } 60 | -------------------------------------------------------------------------------- /util/helper/account_test.go: -------------------------------------------------------------------------------- 1 | // This package is used for Query balance of account 2 | 3 | package helper 4 | 5 | import ( 6 | "fmt" 7 | "github.com/stretchr/testify/require" 8 | "testing" 9 | "time" 10 | 11 | "encoding/hex" 12 | "github.com/irisnet/irishub-sync/logger" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestQueryAccountBalance(t *testing.T) { 17 | //InitClientPool() 18 | 19 | type args struct { 20 | address string 21 | } 22 | tests := []struct { 23 | name string 24 | args args 25 | }{ 26 | { 27 | name: "test balance not nil", 28 | args: args{ 29 | address: "faa1eqvkfthtrr93g4p9qspp54w6dtjtrn279vcmpn", 30 | }, 31 | }, 32 | //{ 33 | // name: "test balance is nil", 34 | // args: args{ 35 | // address: "faa1utem9ysq9gkpkhnrrtznmrxyy238kwd0gkcz60", 36 | // }, 37 | //}, 38 | } 39 | for _, tt := range tests { 40 | t.Run(tt.name, func(t *testing.T) { 41 | res, accNumber := QueryAccountInfo(tt.args.address) 42 | logger.Info("accNum info", logger.Uint64("accNumber", accNumber)) 43 | logger.Info(ToJson(res)) 44 | }) 45 | } 46 | } 47 | 48 | func TestValAddrToAccAddr(t *testing.T) { 49 | valAddr := "fva1qz47703lujvyumg4k3fgl7uf9v7uruhzqqh5f8" 50 | fmt.Println(ValAddrToAccAddr(valAddr)) 51 | } 52 | 53 | func TestConvertAccountAddrFromHexToBech32(t *testing.T) { 54 | hexAddr := "7c99a0d9ab962250b83234f830666d785e5406ff" 55 | bech32Addr := "faa10jv6pkdtjc39pwpjxnurqend0p09gphl3xg5yc" 56 | if bytes, err := hex.DecodeString(hexAddr); err != nil { 57 | t.Fatal(err) 58 | } else { 59 | res, err := ConvertAccountAddrFromHexToBech32(bytes) 60 | if err != nil { 61 | t.Fatal(err) 62 | } 63 | assert.Equal(t, bech32Addr, res) 64 | } 65 | 66 | } 67 | 68 | type Student struct { 69 | Name string `json:"name"` 70 | Age int `json:"age"` 71 | Course []Course `json:"course"` 72 | } 73 | 74 | type Course struct { 75 | Name string `json:"name"` 76 | Schedule []Schedule `json:"schedule"` 77 | } 78 | 79 | type Schedule struct { 80 | Time time.Time 81 | } 82 | 83 | func TestMap2Struct(t *testing.T) { 84 | data := Student{ 85 | Name: "zhansan", 86 | Age: 10, 87 | Course: []Course{ 88 | {Name: "MAth", Schedule: []Schedule{ 89 | {Time: time.Now().UTC()}, 90 | }}, 91 | }, 92 | } 93 | 94 | mp := Struct2Map(data) 95 | 96 | var data1 Student 97 | Map2Struct(mp, &data1) 98 | 99 | require.EqualValues(t, data, data1) 100 | 101 | } 102 | -------------------------------------------------------------------------------- /util/helper/common.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/irisnet/irishub-sync/logger" 7 | "reflect" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // convert object to json 13 | func ToJson(v interface{}) string { 14 | data, err := json.Marshal(v) 15 | if err != nil { 16 | logger.Error(err.Error()) 17 | } 18 | return string(data) 19 | } 20 | 21 | func ParseStrToFloat(s string) (float64, error) { 22 | return strconv.ParseFloat(s, 64) 23 | } 24 | 25 | func ParseFloat(s string, bit ...int) float64 { 26 | f, err := strconv.ParseFloat(s, 64) 27 | if err != nil { 28 | logger.Error("common.ParseFloat error", logger.String("value", s)) 29 | return 0 30 | } 31 | 32 | if len(bit) > 0 { 33 | return RoundFloat(f, bit[0]) 34 | } 35 | return f 36 | } 37 | 38 | func RoundFloat(num float64, bit int) (i float64) { 39 | format := "%" + fmt.Sprintf("0.%d", bit) + "f" 40 | s := fmt.Sprintf(format, num) 41 | i, err := strconv.ParseFloat(s, 0) 42 | if err != nil { 43 | logger.Error("common.RoundFloat error", logger.String("format", format)) 44 | return 0 45 | } 46 | return i 47 | } 48 | 49 | func RoundString(decimal string, bit int) (i string) { 50 | f := ParseFloat(decimal, bit) 51 | return strconv.FormatFloat(f, 'f', bit, 64) 52 | } 53 | 54 | func Struct2Map(obj interface{}) map[string]interface{} { 55 | t := reflect.TypeOf(obj) 56 | v := reflect.ValueOf(obj) 57 | 58 | var data = make(map[string]interface{}) 59 | for i := 0; i < t.NumField(); i++ { 60 | tag := t.Field(i).Tag 61 | key := tag.Get("json") 62 | if len(key) == 0 { 63 | key = strings.ToLower(t.Field(i).Name) 64 | } 65 | data[key] = v.Field(i).Interface() 66 | } 67 | return data 68 | } 69 | 70 | func Map2Struct(srcMap map[string]interface{}, obj interface{}) { 71 | bz, err := json.Marshal(srcMap) 72 | if err != nil { 73 | logger.Error("map convert to struct failed") 74 | } 75 | err = json.Unmarshal(bz, obj) 76 | if err != nil { 77 | logger.Error("map convert to struct failed") 78 | } 79 | } 80 | 81 | func Min(x, y int64) int64 { 82 | if x < y { 83 | return x 84 | } 85 | return y 86 | } 87 | 88 | func DistinctStringSlice(slice []string) []string { 89 | var res []string 90 | elementExistMap := make(map[string]bool) 91 | if len(slice) > 0 { 92 | for _, v := range slice { 93 | if !elementExistMap[v] { 94 | res = append(res, v) 95 | elementExistMap[v] = true 96 | } 97 | } 98 | } 99 | 100 | return res 101 | } 102 | -------------------------------------------------------------------------------- /util/helper/common_test.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestDistinctStringSlice(t *testing.T) { 8 | type args struct { 9 | slice []string 10 | } 11 | tests := []struct { 12 | name string 13 | args args 14 | }{ 15 | { 16 | name: "TestDistinctStringSlice", 17 | args: args{ 18 | slice: append([]string{"1", "2", "3"}, []string{"2", "4", "6", "3"}...), 19 | }, 20 | }, 21 | } 22 | for _, tt := range tests { 23 | t.Run(tt.name, func(t *testing.T) { 24 | res := DistinctStringSlice(tt.args.slice) 25 | t.Log(res) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /util/helper/delegator.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "github.com/irisnet/irishub-sync/logger" 5 | "github.com/irisnet/irishub-sync/types" 6 | "github.com/irisnet/irishub/app/v1/stake" 7 | sdktypes "github.com/irisnet/irishub/types" 8 | ) 9 | 10 | const ( 11 | delegatorDelegationsPath = "custom/stake/delegatorDelegations" 12 | delegatorUnbondingDelegationsPath = "custom/stake/delegatorUnbondingDelegations" 13 | ) 14 | 15 | // query delegator delegations from store 16 | func GetDelegations(delegator string) []types.Delegation { 17 | var ( 18 | delegations []types.Delegation 19 | ) 20 | cdc := types.GetCodec() 21 | 22 | addr, err := types.AccAddressFromBech32(delegator) 23 | if err != nil { 24 | logger.Error("get addr from hex failed", logger.String("address", delegator), 25 | logger.String("err", err.Error())) 26 | return nil 27 | } 28 | 29 | params := stake.NewQueryDelegatorParams(addr) 30 | bz, err := cdc.MarshalJSON(params) 31 | if err != nil { 32 | logger.Error("get query key fail", logger.String("delegatorAddr", delegator), 33 | logger.String("err", err.Error())) 34 | return nil 35 | } 36 | 37 | res, err := QueryWithPath(bz, delegatorDelegationsPath) 38 | if err != nil { 39 | logger.Error("query tm store fail", logger.String("err", err.Error())) 40 | return nil 41 | } 42 | 43 | if err := cdc.UnmarshalJSON(res, &delegations); err != nil { 44 | logger.Error("unmarshal json fail", logger.String("err", err.Error())) 45 | return nil 46 | } else { 47 | return delegations 48 | } 49 | } 50 | 51 | // query delegator unbondingDelegations from store 52 | func GetUnbondingDelegations(delegator string) []types.UnbondingDelegation { 53 | var ( 54 | unbondingDelegations []types.UnbondingDelegation 55 | ) 56 | cdc := types.GetCodec() 57 | 58 | addr, err := types.AccAddressFromBech32(delegator) 59 | if err != nil { 60 | logger.Error("get addr from hex failed", logger.String("address", delegator), 61 | logger.String("err", err.Error())) 62 | return nil 63 | } 64 | 65 | params := stake.NewQueryDelegatorParams(addr) 66 | bz, err := cdc.MarshalJSON(params) 67 | if err != nil { 68 | logger.Error("get query key fail", logger.String("delegatorAddr", delegator), 69 | logger.String("err", err.Error())) 70 | return nil 71 | } 72 | 73 | res, err := QueryWithPath(bz, delegatorUnbondingDelegationsPath) 74 | if err != nil { 75 | logger.Error("query tm store fail", logger.String("err", err.Error())) 76 | return nil 77 | } 78 | 79 | if err := cdc.UnmarshalJSON(res, &unbondingDelegations); err != nil { 80 | logger.Error("unmarshal json fail", logger.String("err", err.Error())) 81 | return nil 82 | } else { 83 | return unbondingDelegations 84 | } 85 | } 86 | 87 | func CalculateDelegatorDelegationTokens(delegations []types.Delegation) float64 { 88 | var ( 89 | token types.Dec 90 | ) 91 | token = sdktypes.ZeroDec() 92 | if len(delegations) > 0 { 93 | for _, v := range delegations { 94 | validatorAddr := v.ValidatorAddr.String() 95 | if validator, err := GetValidator(validatorAddr); err != nil { 96 | logger.Error("get validator fail", logger.String("validatorAddr", validatorAddr), 97 | logger.String("err", err.Error())) 98 | continue 99 | } else { 100 | token = token.Add(validator.DelegatorShareExRate().Mul(v.Shares)) 101 | } 102 | } 103 | } 104 | 105 | return ParseFloat(token.String()) 106 | } 107 | 108 | func CalculateDelegatorUnbondingDelegationTokens(unbondingDelegations []types.UnbondingDelegation) float64 { 109 | var ( 110 | token types.Int 111 | ) 112 | token = sdktypes.ZeroInt() 113 | 114 | if len(unbondingDelegations) > 0 { 115 | for _, v := range unbondingDelegations { 116 | token = token.Add(v.InitialBalance.Amount) 117 | } 118 | } 119 | 120 | return ParseFloat(token.String()) 121 | } 122 | -------------------------------------------------------------------------------- /util/helper/delegator_test.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | func TestGetDelegations(t *testing.T) { 9 | type args struct { 10 | delegator string 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | }{ 16 | { 17 | name: "TestGetDelegations", 18 | args: args{ 19 | delegator: "faa192vef4442d07lqde59mx35dvmfv9v72wrsu84a", 20 | }, 21 | }, 22 | } 23 | for _, tt := range tests { 24 | t.Run(tt.name, func(t *testing.T) { 25 | res := GetDelegations(tt.args.delegator) 26 | resBytes, _ := json.MarshalIndent(res, "", "\t") 27 | t.Log(string(resBytes)) 28 | }) 29 | } 30 | } 31 | 32 | func TestGetUnbondingDelegations(t *testing.T) { 33 | type args struct { 34 | delegator string 35 | } 36 | tests := []struct { 37 | name string 38 | args args 39 | }{ 40 | { 41 | name: "TestGetDelegations", 42 | args: args{ 43 | delegator: "faa192vef4442d07lqde59mx35dvmfv9v72wrsu84a", 44 | }, 45 | }, 46 | } 47 | for _, tt := range tests { 48 | t.Run(tt.name, func(t *testing.T) { 49 | res := GetUnbondingDelegations(tt.args.delegator) 50 | resBytes, _ := json.MarshalIndent(res, "", "\t") 51 | t.Log(string(resBytes)) 52 | }) 53 | } 54 | } 55 | 56 | func TestCalculateDelegatorDelegationTokens(t *testing.T) { 57 | delegatorAddr := "faa192vef4442d07lqde59mx35dvmfv9v72wrsu84a" 58 | tokens := CalculateDelegatorDelegationTokens(GetDelegations(delegatorAddr)) 59 | t.Log(tokens) 60 | } 61 | 62 | func TestCalculateDelegatorUnbondingDelegationTokens(t *testing.T) { 63 | delegatorAddr := "faa192vef4442d07lqde59mx35dvmfv9v72wrsu84a" 64 | tokens := CalculateDelegatorUnbondingDelegationTokens(GetUnbondingDelegations(delegatorAddr)) 65 | t.Log(tokens) 66 | } 67 | -------------------------------------------------------------------------------- /util/helper/pool_client.go: -------------------------------------------------------------------------------- 1 | // init client from clientPool. 2 | // client is httpClient of tendermint 3 | 4 | package helper 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "github.com/irisnet/irishub-sync/logger" 10 | "github.com/irisnet/irishub-sync/types" 11 | "time" 12 | ) 13 | 14 | type Client struct { 15 | types.Client 16 | Id string 17 | } 18 | 19 | func newClient(addr string) *Client { 20 | return &Client{ 21 | Client: types.NewHTTP(addr, "/websocket"), 22 | Id: generateId(addr), 23 | } 24 | } 25 | 26 | // get client from pool 27 | func GetClient() *Client { 28 | 29 | c, err := pool.BorrowObject(ctx) 30 | for err != nil { 31 | logger.Error("GetClient failed,will try again after 3 seconds", logger.String("err", err.Error())) 32 | time.Sleep(3 * time.Second) 33 | c, err = pool.BorrowObject(ctx) 34 | } 35 | 36 | logger.Debug("current available connection", logger.Int("Num", pool.GetNumIdle())) 37 | logger.Debug("current used connection", logger.Int("Num", pool.GetNumActive())) 38 | return c.(*Client) 39 | } 40 | 41 | func GetClientWithTimeout(timeout time.Duration) (*Client, error) { 42 | 43 | c := make(chan interface{}) 44 | errCh := make(chan error) 45 | go func() { 46 | client, err := pool.BorrowObject(ctx) 47 | if err != nil { 48 | errCh <- err 49 | } else { 50 | c <- client 51 | } 52 | }() 53 | select { 54 | case res := <-c: 55 | return res.(*Client), nil 56 | case res := <-errCh: 57 | return nil, res 58 | case <-time.After(timeout): 59 | return nil, errors.New("rpc node timeout") 60 | } 61 | } 62 | 63 | // release client 64 | func (c *Client) Release() { 65 | err := pool.ReturnObject(ctx, c) 66 | if err != nil { 67 | logger.Debug("debug=======================Release err=======================debug") 68 | logger.Error(err.Error()) 69 | } 70 | logger.Debug("debug=======================Release return=======================debug") 71 | } 72 | 73 | func (c *Client) HeartBeat() error { 74 | http := c.Client.(*types.HTTP) 75 | _, err := http.Health() 76 | return err 77 | } 78 | 79 | func generateId(address string) string { 80 | return fmt.Sprintf("peer[%s]", address) 81 | } 82 | -------------------------------------------------------------------------------- /util/helper/pool_client_test.go: -------------------------------------------------------------------------------- 1 | // init client from clientPool. 2 | // client is httpClient of tendermint 3 | 4 | package helper 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "github.com/tendermint/tendermint/types" 10 | "testing" 11 | 12 | "github.com/irisnet/irishub-sync/logger" 13 | ) 14 | 15 | func TestInitClientPool(t *testing.T) { 16 | a := []int{1, 2, 3} 17 | b := make([]int, 6, 6) 18 | for index, value := range a { 19 | b[index] = value 20 | } 21 | b[3] = 4 22 | } 23 | 24 | func TestGetClient(t *testing.T) { 25 | client := GetClient() 26 | fmt.Println("====1======") 27 | defer func() { 28 | fmt.Println("====3======") 29 | if err := recover(); err != nil { 30 | logger.Debug("debug=======================recover=======================debug") 31 | } 32 | }() 33 | _, err := client.Status() 34 | if err != nil { 35 | fmt.Println(err) 36 | return 37 | } 38 | fmt.Println("====4======") 39 | 40 | txEventsCh := make(chan interface{}) 41 | client.Start() 42 | client.Subscribe(context.Background(), "xxx", types.EventQueryValidatorSetUpdates, txEventsCh) 43 | for e := range txEventsCh { 44 | edt := e.(types.EventDataValidatorSetUpdates) 45 | fmt.Println(edt.ValidatorUpdates) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /util/helper/pool_factory.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "context" 5 | conf "github.com/irisnet/irishub-sync/conf/server" 6 | "github.com/irisnet/irishub-sync/logger" 7 | gcp "github.com/jolestar/go-commons-pool" 8 | "github.com/robfig/cron" 9 | "math/rand" 10 | "sync" 11 | ) 12 | 13 | var ( 14 | factory PoolFactory 15 | pool *NodePool 16 | ctx = context.Background() 17 | ) 18 | 19 | func init() { 20 | var syncMap sync.Map 21 | for _, url := range conf.BlockChainMonitorUrl { 22 | key := generateId(url) 23 | endPoint := EndPoint{ 24 | Address: url, 25 | Available: true, 26 | } 27 | 28 | syncMap.Store(key, endPoint) 29 | } 30 | factory = PoolFactory{ 31 | cron: cron.New(), 32 | peersMap: syncMap, 33 | } 34 | config := gcp.NewDefaultPoolConfig() 35 | 36 | config.MaxTotal = conf.MaxConnectionNum 37 | config.MaxIdle = conf.InitConnectionNum 38 | config.MinIdle = conf.InitConnectionNum 39 | config.TestOnBorrow = true 40 | config.TestOnCreate = true 41 | config.TestWhileIdle = true 42 | 43 | logger.Info("PoolConfig", logger.Int("config.MaxTotal", config.MaxTotal), logger.Int("config.MaxIdle", config.MaxIdle)) 44 | pool = &NodePool{ 45 | gcp.NewObjectPool(ctx, &factory, config), 46 | } 47 | pool.PreparePool(ctx) 48 | //自动搜索可用节点 49 | //factory.StartCrawlPeers() 50 | } 51 | 52 | type EndPoint struct { 53 | Address string 54 | Available bool 55 | } 56 | 57 | type NodePool struct { 58 | *gcp.ObjectPool 59 | } 60 | 61 | type PoolFactory struct { 62 | peersMap sync.Map 63 | cron *cron.Cron 64 | } 65 | 66 | func ClosePool() { 67 | logger.Info("release resource nodePool") 68 | pool.Close(ctx) 69 | factory.cron.Stop() 70 | } 71 | 72 | func (f *PoolFactory) MakeObject(ctx context.Context) (*gcp.PooledObject, error) { 73 | endpoint := f.GetEndPoint() 74 | logger.Debug("PoolFactory MakeObject peer", logger.Any("endpoint", endpoint)) 75 | return gcp.NewPooledObject(newClient(endpoint.Address)), nil 76 | } 77 | 78 | func (f *PoolFactory) DestroyObject(ctx context.Context, object *gcp.PooledObject) error { 79 | logger.Debug("PoolFactory DestroyObject peer", logger.Any("peer", object.Object)) 80 | c := object.Object.(*Client) 81 | if c.IsRunning() { 82 | c.Stop() 83 | } 84 | return nil 85 | } 86 | 87 | func (f *PoolFactory) ValidateObject(ctx context.Context, object *gcp.PooledObject) bool { 88 | // do validate 89 | logger.Debug("PoolFactory ValidateObject peer", logger.Any("peer", object.Object)) 90 | c := object.Object.(*Client) 91 | if c.HeartBeat() != nil { 92 | value, ok := f.peersMap.Load(c.Id) 93 | if ok { 94 | endPoint := value.(EndPoint) 95 | endPoint.Available = false 96 | f.peersMap.Store(c.Id, endPoint) 97 | } 98 | return false 99 | } 100 | return true 101 | } 102 | 103 | func (f *PoolFactory) ActivateObject(ctx context.Context, object *gcp.PooledObject) error { 104 | logger.Debug("PoolFactory ActivateObject peer", logger.Any("peer", object.Object)) 105 | return nil 106 | } 107 | 108 | func (f *PoolFactory) PassivateObject(ctx context.Context, object *gcp.PooledObject) error { 109 | logger.Debug("PoolFactory PassivateObject peer", logger.Any("peer", object.Object)) 110 | return nil 111 | } 112 | 113 | func (f *PoolFactory) GetEndPoint() EndPoint { 114 | var ( 115 | keys []string 116 | selectedKey string 117 | ) 118 | 119 | f.peersMap.Range(func(k, value interface{}) bool { 120 | key := k.(string) 121 | endPoint := value.(EndPoint) 122 | if endPoint.Available { 123 | keys = append(keys, key) 124 | } 125 | selectedKey = key 126 | 127 | return true 128 | }) 129 | 130 | if len(keys) > 0 { 131 | index := rand.Intn(len(keys)) 132 | selectedKey = keys[index] 133 | } 134 | value, ok := f.peersMap.Load(selectedKey) 135 | if ok { 136 | return value.(EndPoint) 137 | } else { 138 | logger.Error("Can't get selected end point", logger.String("selectedKey", selectedKey)) 139 | } 140 | return EndPoint{} 141 | } 142 | 143 | func (f *PoolFactory) heartBeat() { 144 | go func() { 145 | f.cron.AddFunc("0 0/1 * * * *", func() { 146 | //logger.Info("PoolFactory StartCrawlPeers peer", logger.Any("peers", f.peersMap)) 147 | //client := GetClient() 148 | // 149 | //defer func() { 150 | // client.Release() 151 | // if err := recover(); err != nil { 152 | // logger.Info("PoolFactory StartCrawlPeers error", logger.Any("err", err)) 153 | // } 154 | //}() 155 | // 156 | //addrs := client.GetNodeAddress() 157 | //for _, addr := range addrs { 158 | // key := generateId(addr) 159 | // if _, ok := f.peersMap[key]; !ok { 160 | // f.peersMap[key] = EndPoint{ 161 | // Address: addr, 162 | // Available: true, 163 | // } 164 | // } 165 | //} 166 | // 167 | ////检测节点是否上线 168 | //for key := range f.peersMap { 169 | // endPoint := f.peersMap[key] 170 | // if !endPoint.Available { 171 | // node := newClient(endPoint.Address) 172 | // if node.HeartBeat() == nil { 173 | // endPoint.Available = true 174 | // f.peersMap[key] = endPoint 175 | // } 176 | // } 177 | //} 178 | }) 179 | f.cron.Start() 180 | }() 181 | } 182 | -------------------------------------------------------------------------------- /util/helper/proposal.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "errors" 5 | "github.com/irisnet/irishub-sync/store/document" 6 | "github.com/irisnet/irishub-sync/types" 7 | "github.com/irisnet/irishub-sync/util/constant" 8 | ) 9 | 10 | func GetProposal(proposalID uint64) (proposal document.Proposal, err error) { 11 | cdc := types.GetCodec() 12 | 13 | res, err := Query(types.KeyProposal(proposalID), "gov", constant.StoreDefaultEndPath) 14 | if len(res) == 0 || err != nil { 15 | return proposal, errors.New("no data") 16 | } 17 | var propo types.Proposal 18 | cdc.UnmarshalBinaryLengthPrefixed(res, &propo) //TODO 19 | proposal.ProposalId = proposalID 20 | proposal.Title = propo.GetTitle() 21 | proposal.Type = propo.GetProposalType().String() 22 | proposal.Description = propo.GetDescription() 23 | proposal.Status = propo.GetStatus().String() 24 | 25 | proposal.SubmitTime = propo.GetSubmitTime() 26 | proposal.VotingStartTime = propo.GetVotingStartTime() 27 | proposal.VotingEndTime = propo.GetVotingEndTime() 28 | proposal.DepositEndTime = propo.GetDepositEndTime() 29 | proposal.TotalDeposit = types.ParseCoins(propo.GetTotalDeposit().String()) 30 | proposal.Votes = []document.PVote{} 31 | 32 | tallyResult := propo.GetTallyResult() 33 | proposal.TallyResult = document.PTallyResult{ 34 | Yes: tallyResult.Yes.String(), 35 | Abstain: tallyResult.Abstain.String(), 36 | No: tallyResult.No.String(), 37 | NoWithVeto: tallyResult.NoWithVeto.String(), 38 | SystemVotingPower: tallyResult.SystemVotingPower.String(), 39 | } 40 | 41 | return 42 | } 43 | 44 | func GetVotes(proposalID uint64) (pVotes []document.PVote, err error) { 45 | cdc := types.GetCodec() 46 | 47 | res, err := QuerySubspace(types.KeyVotesSubspace(proposalID), "gov") 48 | if len(res) == 0 || err != nil { 49 | return pVotes, err 50 | } 51 | for i := 0; i < len(res); i++ { 52 | var vote types.SdkVote 53 | cdc.UnmarshalBinaryLengthPrefixed(res[i].Value, &vote) 54 | v := document.PVote{ 55 | Voter: vote.Voter.String(), 56 | Option: vote.Option.String(), 57 | } 58 | pVotes = append(pVotes, v) 59 | } 60 | return 61 | } 62 | -------------------------------------------------------------------------------- /util/helper/proposal_test.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | func TestGetProposal(t *testing.T) { 9 | proposalId := uint64(2) 10 | if res, err := GetProposal(proposalId); err != nil { 11 | t.Fatal(err) 12 | } else { 13 | resBytes, _ := json.MarshalIndent(res, "", "\t") 14 | t.Log(string(resBytes)) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /util/helper/query.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "fmt" 5 | "github.com/irisnet/irishub-sync/types" 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | // Query from Tendermint with the provided storename and path 10 | func Query(key types.HexBytes, storeName string, endPath string) (res []byte, err error) { 11 | path := fmt.Sprintf("/store/%s/%s", storeName, endPath) 12 | client := GetClient() 13 | defer client.Release() 14 | 15 | opts := types.ABCIQueryOptions{ 16 | Height: 0, 17 | Prove: false, //不需要验证prof 18 | } 19 | result, err := client.ABCIQueryWithOptions(path, key, opts) 20 | if err != nil { 21 | return res, err 22 | } 23 | resp := result.Response 24 | if resp.Code != uint32(0) { 25 | return res, errors.Errorf("Query failed: (%d) %s", resp.Code, resp.Log) 26 | } 27 | return resp.Value, nil 28 | } 29 | 30 | func QueryWithPath(key types.HexBytes, path string) (res []byte, err error) { 31 | client := GetClient() 32 | defer client.Release() 33 | 34 | opts := types.ABCIQueryOptions{ 35 | Height: 0, 36 | Prove: false, //不需要验证prof 37 | } 38 | result, err := client.ABCIQueryWithOptions(path, key, opts) 39 | if err != nil { 40 | return res, err 41 | } 42 | resp := result.Response 43 | if resp.Code != uint32(0) { 44 | return res, errors.Errorf("Query failed: (%d) %s", resp.Code, resp.Log) 45 | } 46 | return resp.Value, nil 47 | } 48 | 49 | func QuerySubspace(subspace []byte, storeName string) (res []types.KVPair, err error) { 50 | cdc := types.GetCodec() 51 | resRaw, err := Query(subspace, storeName, "subspace") 52 | if err != nil { 53 | return res, err 54 | } 55 | cdc.MustUnmarshalBinaryLengthPrefixed(resRaw, &res) 56 | return 57 | } 58 | -------------------------------------------------------------------------------- /util/helper/query_test.go: -------------------------------------------------------------------------------- 1 | package helper 2 | -------------------------------------------------------------------------------- /util/helper/tx.go: -------------------------------------------------------------------------------- 1 | // package for parse tx struct from binary data 2 | 3 | package helper 4 | 5 | import ( 6 | "encoding/hex" 7 | "github.com/irisnet/irishub-sync/logger" 8 | "github.com/irisnet/irishub-sync/store" 9 | "github.com/irisnet/irishub-sync/store/document" 10 | itypes "github.com/irisnet/irishub-sync/types" 11 | imsg "github.com/irisnet/irishub-sync/types/msg" 12 | "github.com/irisnet/irishub-sync/util/constant" 13 | "strconv" 14 | "strings" 15 | "time" 16 | ) 17 | 18 | func ParseTx(txBytes itypes.Tx, block *itypes.Block) document.CommonTx { 19 | var ( 20 | authTx itypes.StdTx 21 | methodName = "ParseTx" 22 | docTx document.CommonTx 23 | gasPrice float64 24 | actualFee store.ActualFee 25 | signers []document.Signer 26 | docTxMsgs []document.DocTxMsg 27 | ) 28 | 29 | cdc := itypes.GetCodec() 30 | 31 | err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &authTx) 32 | if err != nil { 33 | logger.Error(err.Error()) 34 | return docTx 35 | } 36 | 37 | height := block.Height 38 | blockTime := block.Time 39 | txHash := BuildHex(txBytes.Hash()) 40 | fee := itypes.BuildFee(authTx.Fee) 41 | memo := authTx.Memo 42 | 43 | // get tx signers 44 | if len(authTx.Signatures) > 0 { 45 | for _, signature := range authTx.Signatures { 46 | address := signature.Address() 47 | 48 | signer := document.Signer{} 49 | signer.AddrHex = address.String() 50 | if addrBech32, err := ConvertAccountAddrFromHexToBech32(address.Bytes()); err != nil { 51 | logger.Error("convert account addr from hex to bech32 fail", 52 | logger.String("addrHex", address.String()), logger.String("err", err.Error())) 53 | } else { 54 | signer.AddrBech32 = addrBech32 55 | } 56 | signers = append(signers, signer) 57 | } 58 | } 59 | 60 | // get tx status, gasUsed, gasPrice and actualFee from tx result 61 | status, result, err := QueryTxResult(txBytes.Hash()) 62 | if err != nil { 63 | logger.Error("get txResult err", logger.String("method", methodName), logger.String("err", err.Error())) 64 | } 65 | log := result.Log 66 | gasUsed := Min(result.GasUsed, fee.Gas) 67 | if len(fee.Amount) > 0 { 68 | gasPrice = fee.Amount[0].Amount / float64(fee.Gas) 69 | actualFee = store.ActualFee{ 70 | Denom: fee.Amount[0].Denom, 71 | Amount: float64(gasUsed) * gasPrice, 72 | } 73 | } else { 74 | gasPrice = 0 75 | actualFee = store.ActualFee{} 76 | } 77 | 78 | msgs := authTx.GetMsgs() 79 | if len(msgs) <= 0 { 80 | logger.Error("can't get msgs", logger.String("method", methodName)) 81 | return docTx 82 | } 83 | msg := msgs[0] 84 | 85 | docTx = document.CommonTx{ 86 | Height: height, 87 | Time: blockTime, 88 | TxHash: txHash, 89 | Fee: fee, 90 | Memo: memo, 91 | Status: status, 92 | Code: result.Code, 93 | Log: log, 94 | GasUsed: gasUsed, 95 | GasWanted: result.GasUsed, 96 | GasPrice: gasPrice, 97 | ActualFee: actualFee, 98 | Tags: parseTags(result), 99 | Signers: signers, 100 | } 101 | 102 | switch msg.(type) { 103 | case itypes.MsgTransfer: 104 | msg := msg.(itypes.MsgTransfer) 105 | 106 | docTx.From = msg.Inputs[0].Address.String() 107 | docTx.To = msg.Outputs[0].Address.String() 108 | docTx.Amount = itypes.ParseCoins(msg.Inputs[0].Coins.String()) 109 | docTx.Type = constant.TxTypeTransfer 110 | txMsg := imsg.DocTxMsgSend{} 111 | txMsg.BuildMsg(msg) 112 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 113 | Type: txMsg.Type(), 114 | Msg: &txMsg, 115 | }) 116 | return docTx 117 | case itypes.MsgBurn: 118 | msg := msg.(itypes.MsgBurn) 119 | docTx.From = msg.Owner.String() 120 | docTx.To = "" 121 | docTx.Amount = itypes.ParseCoins(msg.Coins.String()) 122 | docTx.Type = constant.TxTypeBurn 123 | txMsg := imsg.DocTxMsgBurn{} 124 | txMsg.BuildMsg(msg) 125 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 126 | Type: txMsg.Type(), 127 | Msg: &txMsg, 128 | }) 129 | return docTx 130 | case itypes.MsgSetMemoRegexp: 131 | msg := msg.(itypes.MsgSetMemoRegexp) 132 | docTx.From = msg.Owner.String() 133 | docTx.To = "" 134 | docTx.Amount = []store.Coin{} 135 | docTx.Type = constant.TxTypeSetMemoRegexp 136 | txMsg := imsg.DocTxMsgSetMemoRegexp{} 137 | txMsg.BuildMsg(msg) 138 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 139 | Type: txMsg.Type(), 140 | Msg: &txMsg, 141 | }) 142 | return docTx 143 | case itypes.MsgStakeCreate: 144 | msg := msg.(itypes.MsgStakeCreate) 145 | 146 | docTx.From = msg.DelegatorAddr.String() 147 | docTx.To = msg.ValidatorAddr.String() 148 | docTx.Amount = []store.Coin{itypes.ParseCoin(msg.Delegation.String())} 149 | docTx.Type = constant.TxTypeStakeCreateValidator 150 | txMsg := imsg.DocTxMsgStakeCreate{} 151 | txMsg.BuildMsg(msg) 152 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 153 | Type: txMsg.Type(), 154 | Msg: &txMsg, 155 | }) 156 | return docTx 157 | case itypes.MsgStakeEdit: 158 | msg := msg.(itypes.MsgStakeEdit) 159 | 160 | docTx.From = msg.ValidatorAddr.String() 161 | docTx.To = "" 162 | docTx.Amount = []store.Coin{} 163 | docTx.Type = constant.TxTypeStakeEditValidator 164 | txMsg := imsg.DocTxMsgStakeEdit{} 165 | txMsg.BuildMsg(msg) 166 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 167 | Type: txMsg.Type(), 168 | Msg: &txMsg, 169 | }) 170 | return docTx 171 | case itypes.MsgStakeDelegate: 172 | msg := msg.(itypes.MsgStakeDelegate) 173 | 174 | docTx.From = msg.DelegatorAddr.String() 175 | docTx.To = msg.ValidatorAddr.String() 176 | docTx.Amount = []store.Coin{itypes.ParseCoin(msg.Delegation.String())} 177 | docTx.Type = constant.TxTypeStakeDelegate 178 | txMsg := imsg.DocTxMsgDelegate{} 179 | txMsg.BuildMsg(msg) 180 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 181 | Type: txMsg.Type(), 182 | Msg: &txMsg, 183 | }) 184 | 185 | return docTx 186 | case itypes.MsgStakeBeginUnbonding: 187 | msg := msg.(itypes.MsgStakeBeginUnbonding) 188 | 189 | shares := ParseFloat(msg.SharesAmount.String()) 190 | docTx.From = msg.DelegatorAddr.String() 191 | docTx.To = msg.ValidatorAddr.String() 192 | 193 | coin := store.Coin{ 194 | Amount: shares, 195 | } 196 | docTx.Amount = []store.Coin{coin} 197 | docTx.Type = constant.TxTypeStakeBeginUnbonding 198 | txMsg := imsg.DocTxMsgBeginUnbonding{} 199 | txMsg.BuildMsg(msg) 200 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 201 | Type: txMsg.Type(), 202 | Msg: &txMsg, 203 | }) 204 | return docTx 205 | case itypes.MsgBeginRedelegate: 206 | msg := msg.(itypes.MsgBeginRedelegate) 207 | 208 | shares := ParseFloat(msg.SharesAmount.String()) 209 | docTx.From = msg.ValidatorSrcAddr.String() 210 | docTx.To = msg.ValidatorDstAddr.String() 211 | coin := store.Coin{ 212 | Amount: shares, 213 | } 214 | docTx.Amount = []store.Coin{coin} 215 | docTx.Type = constant.TxTypeBeginRedelegate 216 | txMsg := imsg.DocTxMsgBeginRedelegate{} 217 | txMsg.BuildMsg(msg) 218 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 219 | Type: txMsg.Type(), 220 | Msg: &txMsg, 221 | }) 222 | return docTx 223 | case itypes.MsgUnjail: 224 | msg := msg.(itypes.MsgUnjail) 225 | 226 | docTx.From = msg.ValidatorAddr.String() 227 | docTx.Type = constant.TxTypeUnjail 228 | txMsg := imsg.DocTxMsgUnjail{} 229 | txMsg.BuildMsg(msg) 230 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 231 | Type: txMsg.Type(), 232 | Msg: &txMsg, 233 | }) 234 | case itypes.MsgSetWithdrawAddress: 235 | msg := msg.(itypes.MsgSetWithdrawAddress) 236 | 237 | docTx.From = msg.DelegatorAddr.String() 238 | docTx.To = msg.WithdrawAddr.String() 239 | docTx.Type = constant.TxTypeSetWithdrawAddress 240 | txMsg := imsg.DocTxMsgSetWithdrawAddress{} 241 | txMsg.BuildMsg(msg) 242 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 243 | Type: txMsg.Type(), 244 | Msg: &txMsg, 245 | }) 246 | case itypes.MsgWithdrawDelegatorReward: 247 | msg := msg.(itypes.MsgWithdrawDelegatorReward) 248 | 249 | docTx.From = msg.DelegatorAddr.String() 250 | docTx.To = msg.ValidatorAddr.String() 251 | docTx.Type = constant.TxTypeWithdrawDelegatorReward 252 | txMsg := imsg.DocTxMsgWithdrawDelegatorReward{} 253 | txMsg.BuildMsg(msg) 254 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 255 | Type: txMsg.Type(), 256 | Msg: &txMsg, 257 | }) 258 | 259 | for _, tag := range result.Tags { 260 | key := string(tag.Key) 261 | if key == itypes.TagDistributionReward { 262 | reward := string(tag.Value) 263 | docTx.Amount = itypes.ParseCoins(reward) 264 | break 265 | } 266 | } 267 | case itypes.MsgWithdrawDelegatorRewardsAll: 268 | msg := msg.(itypes.MsgWithdrawDelegatorRewardsAll) 269 | 270 | docTx.From = msg.DelegatorAddr.String() 271 | docTx.Type = constant.TxTypeWithdrawDelegatorRewardsAll 272 | txMsg := imsg.DocTxMsgWithdrawDelegatorRewardsAll{} 273 | txMsg.BuildMsg(msg) 274 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 275 | Type: txMsg.Type(), 276 | Msg: &txMsg, 277 | }) 278 | for _, tag := range result.Tags { 279 | key := string(tag.Key) 280 | if key == itypes.TagDistributionReward { 281 | reward := string(tag.Value) 282 | docTx.Amount = itypes.ParseCoins(reward) 283 | break 284 | } 285 | } 286 | case itypes.MsgWithdrawValidatorRewardsAll: 287 | msg := msg.(itypes.MsgWithdrawValidatorRewardsAll) 288 | 289 | docTx.From = msg.ValidatorAddr.String() 290 | docTx.Type = constant.TxTypeWithdrawValidatorRewardsAll 291 | txMsg := imsg.DocTxMsgWithdrawValidatorRewardsAll{} 292 | txMsg.BuildMsg(msg) 293 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 294 | Type: txMsg.Type(), 295 | Msg: &txMsg, 296 | }) 297 | for _, tag := range result.Tags { 298 | key := string(tag.Key) 299 | if key == itypes.TagDistributionReward { 300 | reward := string(tag.Value) 301 | docTx.Amount = itypes.ParseCoins(reward) 302 | break 303 | } 304 | } 305 | case itypes.MsgSubmitProposal: 306 | msg := msg.(itypes.MsgSubmitProposal) 307 | 308 | docTx.From = msg.Proposer.String() 309 | docTx.To = "" 310 | docTx.Amount = itypes.ParseCoins(msg.InitialDeposit.String()) 311 | docTx.Type = constant.TxTypeSubmitProposal 312 | txMsg := imsg.DocTxMsgSubmitProposal{} 313 | txMsg.BuildMsg(msg) 314 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 315 | Type: txMsg.Type(), 316 | Msg: &txMsg, 317 | }) 318 | 319 | //query proposal_id 320 | proposalId, err := getProposalIdFromTags(result.Tags) 321 | if err != nil { 322 | logger.Error("can't get proposal id from tags", logger.String("txHash", docTx.TxHash), 323 | logger.String("err", err.Error())) 324 | } 325 | docTx.ProposalId = proposalId 326 | 327 | return docTx 328 | case itypes.MsgSubmitSoftwareUpgradeProposal: 329 | msg := msg.(itypes.MsgSubmitSoftwareUpgradeProposal) 330 | 331 | docTx.From = msg.Proposer.String() 332 | docTx.To = "" 333 | docTx.Amount = itypes.ParseCoins(msg.InitialDeposit.String()) 334 | docTx.Type = constant.TxTypeSubmitProposal 335 | txMsg := imsg.DocTxMsgSubmitSoftwareUpgradeProposal{} 336 | txMsg.BuildMsg(msg) 337 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 338 | Type: txMsg.Type(), 339 | Msg: &txMsg, 340 | }) 341 | 342 | //query proposal_id 343 | proposalId, err := getProposalIdFromTags(result.Tags) 344 | if err != nil { 345 | logger.Error("can't get proposal id from tags", logger.String("txHash", docTx.TxHash), 346 | logger.String("err", err.Error())) 347 | } 348 | docTx.ProposalId = proposalId 349 | 350 | return docTx 351 | case itypes.MsgSubmitTaxUsageProposal: 352 | msg := msg.(itypes.MsgSubmitTaxUsageProposal) 353 | 354 | docTx.From = msg.Proposer.String() 355 | docTx.To = "" 356 | docTx.Amount = itypes.ParseCoins(msg.InitialDeposit.String()) 357 | docTx.Type = constant.TxTypeSubmitProposal 358 | txMsg := imsg.DocTxMsgSubmitCommunityTaxUsageProposal{} 359 | txMsg.BuildMsg(msg) 360 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 361 | Type: txMsg.Type(), 362 | Msg: &txMsg, 363 | }) 364 | 365 | //query proposal_id 366 | proposalId, err := getProposalIdFromTags(result.Tags) 367 | if err != nil { 368 | logger.Error("can't get proposal id from tags", logger.String("txHash", docTx.TxHash), 369 | logger.String("err", err.Error())) 370 | } 371 | docTx.ProposalId = proposalId 372 | return docTx 373 | case itypes.MsgSubmitTokenAdditionProposal: 374 | msg := msg.(itypes.MsgSubmitTokenAdditionProposal) 375 | 376 | docTx.From = msg.Proposer.String() 377 | docTx.To = "" 378 | docTx.Amount = itypes.ParseCoins(msg.InitialDeposit.String()) 379 | docTx.Type = constant.TxTypeSubmitProposal 380 | txMsg := imsg.DocTxMsgSubmitTokenAdditionProposal{} 381 | txMsg.BuildMsg(msg) 382 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 383 | Type: txMsg.Type(), 384 | Msg: &txMsg, 385 | }) 386 | //query proposal_id 387 | proposalId, err := getProposalIdFromTags(result.Tags) 388 | if err != nil { 389 | logger.Error("can't get proposal id from tags", logger.String("txHash", docTx.TxHash), 390 | logger.String("err", err.Error())) 391 | } 392 | docTx.ProposalId = proposalId 393 | return docTx 394 | case itypes.MsgDeposit: 395 | msg := msg.(itypes.MsgDeposit) 396 | 397 | docTx.From = msg.Depositor.String() 398 | docTx.Amount = itypes.ParseCoins(msg.Amount.String()) 399 | docTx.Type = constant.TxTypeDeposit 400 | docTx.ProposalId = msg.ProposalID 401 | txMsg := imsg.DocTxMsgDeposit{} 402 | txMsg.BuildMsg(msg) 403 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 404 | Type: txMsg.Type(), 405 | Msg: &txMsg, 406 | }) 407 | return docTx 408 | case itypes.MsgVote: 409 | msg := msg.(itypes.MsgVote) 410 | 411 | docTx.From = msg.Voter.String() 412 | docTx.Amount = []store.Coin{} 413 | docTx.Type = constant.TxTypeVote 414 | docTx.ProposalId = msg.ProposalID 415 | txMsg := imsg.DocTxMsgVote{} 416 | txMsg.BuildMsg(msg) 417 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 418 | Type: txMsg.Type(), 419 | Msg: &txMsg, 420 | }) 421 | return docTx 422 | case itypes.MsgRequestRand: 423 | msg := msg.(itypes.MsgRequestRand) 424 | 425 | docTx.From = msg.Consumer.String() 426 | docTx.Amount = []store.Coin{} 427 | docTx.Type = constant.TxTypeRequestRand 428 | txMsg := imsg.DocTxMsgRequestRand{} 429 | txMsg.BuildMsg(msg) 430 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 431 | Type: txMsg.Type(), 432 | Msg: &txMsg, 433 | }) 434 | return docTx 435 | case itypes.AssetIssueToken: 436 | msg := msg.(itypes.AssetIssueToken) 437 | 438 | docTx.From = msg.Owner.String() 439 | docTx.Type = constant.TxTypeAssetIssueToken 440 | txMsg := imsg.DocTxMsgIssueToken{} 441 | txMsg.BuildMsg(msg) 442 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 443 | Type: txMsg.Type(), 444 | Msg: &txMsg, 445 | }) 446 | 447 | return docTx 448 | case itypes.AssetEditToken: 449 | msg := msg.(itypes.AssetEditToken) 450 | 451 | docTx.From = msg.Owner.String() 452 | docTx.Type = constant.TxTypeAssetEditToken 453 | txMsg := imsg.DocTxMsgEditToken{} 454 | txMsg.BuildMsg(msg) 455 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 456 | Type: txMsg.Type(), 457 | Msg: &txMsg, 458 | }) 459 | 460 | return docTx 461 | case itypes.AssetMintToken: 462 | msg := msg.(itypes.AssetMintToken) 463 | 464 | docTx.From = msg.Owner.String() 465 | docTx.To = msg.To.String() 466 | docTx.Type = constant.TxTypeAssetMintToken 467 | txMsg := imsg.DocTxMsgMintToken{} 468 | txMsg.BuildMsg(msg) 469 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 470 | Type: txMsg.Type(), 471 | Msg: &txMsg, 472 | }) 473 | 474 | return docTx 475 | case itypes.AssetTransferTokenOwner: 476 | msg := msg.(itypes.AssetTransferTokenOwner) 477 | 478 | docTx.From = msg.SrcOwner.String() 479 | docTx.To = msg.DstOwner.String() 480 | docTx.Type = constant.TxTypeAssetTransferTokenOwner 481 | txMsg := imsg.DocTxMsgTransferTokenOwner{} 482 | txMsg.BuildMsg(msg) 483 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 484 | Type: txMsg.Type(), 485 | Msg: &txMsg, 486 | }) 487 | 488 | return docTx 489 | case itypes.AssetCreateGateway: 490 | msg := msg.(itypes.AssetCreateGateway) 491 | 492 | docTx.From = msg.Owner.String() 493 | docTx.Type = constant.TxTypeAssetCreateGateway 494 | txMsg := imsg.DocTxMsgCreateGateway{} 495 | txMsg.BuildMsg(msg) 496 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 497 | Type: txMsg.Type(), 498 | Msg: &txMsg, 499 | }) 500 | 501 | return docTx 502 | case itypes.AssetEditGateWay: 503 | msg := msg.(itypes.AssetEditGateWay) 504 | 505 | docTx.From = msg.Owner.String() 506 | docTx.Type = constant.TxTypeAssetEditGateway 507 | txMsg := imsg.DocTxMsgEditGateway{} 508 | txMsg.BuildMsg(msg) 509 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 510 | Type: txMsg.Type(), 511 | Msg: &txMsg, 512 | }) 513 | 514 | return docTx 515 | case itypes.AssetTransferGatewayOwner: 516 | msg := msg.(itypes.AssetTransferGatewayOwner) 517 | 518 | docTx.From = msg.Owner.String() 519 | docTx.To = msg.To.String() 520 | docTx.Type = constant.TxTypeAssetTransferGatewayOwner 521 | txMsg := imsg.DocTxMsgTransferGatewayOwner{} 522 | txMsg.BuildMsg(msg) 523 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 524 | Type: txMsg.Type(), 525 | Msg: &txMsg, 526 | }) 527 | return docTx 528 | 529 | case itypes.MsgAddProfiler: 530 | msg := msg.(itypes.MsgAddProfiler) 531 | 532 | docTx.From = msg.AddedBy.String() 533 | docTx.To = msg.Address.String() 534 | docTx.Type = constant.TxTypeAddProfiler 535 | txMsg := imsg.DocTxMsgAddProfiler{} 536 | txMsg.BuildMsg(msg) 537 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 538 | Type: txMsg.Type(), 539 | Msg: &txMsg, 540 | }) 541 | return docTx 542 | 543 | case itypes.MsgAddTrustee: 544 | msg := msg.(itypes.MsgAddTrustee) 545 | 546 | docTx.From = msg.AddedBy.String() 547 | docTx.To = msg.Address.String() 548 | docTx.Type = constant.TxTypeAddTrustee 549 | txMsg := imsg.DocTxMsgAddTrustee{} 550 | txMsg.BuildMsg(msg) 551 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 552 | Type: txMsg.Type(), 553 | Msg: &txMsg, 554 | }) 555 | return docTx 556 | 557 | case itypes.MsgDeleteTrustee: 558 | msg := msg.(itypes.MsgDeleteTrustee) 559 | 560 | docTx.From = msg.DeletedBy.String() 561 | docTx.To = msg.Address.String() 562 | docTx.Type = constant.TxTypeDeleteTrustee 563 | txMsg := imsg.DocTxMsgDeleteTrustee{} 564 | txMsg.BuildMsg(msg) 565 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 566 | Type: txMsg.Type(), 567 | Msg: &txMsg, 568 | }) 569 | return docTx 570 | 571 | case itypes.MsgDeleteProfiler: 572 | msg := msg.(itypes.MsgDeleteProfiler) 573 | 574 | docTx.From = msg.DeletedBy.String() 575 | docTx.To = msg.Address.String() 576 | docTx.Type = constant.TxTypeDeleteProfiler 577 | txMsg := imsg.DocTxMsgDeleteProfiler{} 578 | txMsg.BuildMsg(msg) 579 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 580 | Type: txMsg.Type(), 581 | Msg: &txMsg, 582 | }) 583 | return docTx 584 | 585 | case itypes.MsgCreateHTLC: 586 | msg := msg.(itypes.MsgCreateHTLC) 587 | 588 | docTx.From = msg.Sender.String() 589 | docTx.To = msg.To.String() 590 | docTx.Amount = itypes.ParseCoins(msg.Amount.String()) 591 | docTx.Type = constant.TxTypeCreateHTLC 592 | txMsg := imsg.DocTxMsgCreateHTLC{} 593 | txMsg.BuildMsg(msg) 594 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 595 | Type: txMsg.Type(), 596 | Msg: &txMsg, 597 | }) 598 | return docTx 599 | case itypes.MsgClaimHTLC: 600 | msg := msg.(itypes.MsgClaimHTLC) 601 | 602 | docTx.From = msg.Sender.String() 603 | docTx.To = "" 604 | docTx.Type = constant.TxTypeClaimHTLC 605 | txMsg := imsg.DocTxMsgClaimHTLC{} 606 | txMsg.BuildMsg(msg) 607 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 608 | Type: txMsg.Type(), 609 | Msg: &txMsg, 610 | }) 611 | return docTx 612 | case itypes.MsgRefundHTLC: 613 | msg := msg.(itypes.MsgRefundHTLC) 614 | 615 | docTx.From = msg.Sender.String() 616 | docTx.To = "" 617 | docTx.Type = constant.TxTypeRefundHTLC 618 | txMsg := imsg.DocTxMsgRefundHTLC{} 619 | txMsg.BuildMsg(msg) 620 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 621 | Type: txMsg.Type(), 622 | Msg: &txMsg, 623 | }) 624 | return docTx 625 | case itypes.MsgAddLiquidity: 626 | msg := msg.(itypes.MsgAddLiquidity) 627 | 628 | docTx.From = msg.Sender.String() 629 | docTx.To = "" 630 | docTx.Amount = itypes.ParseCoins(msg.MaxToken.String()) 631 | docTx.Type = constant.TxTypeAddLiquidity 632 | txMsg := imsg.DocTxMsgAddLiquidity{} 633 | txMsg.BuildMsg(msg) 634 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 635 | Type: txMsg.Type(), 636 | Msg: &txMsg, 637 | }) 638 | return docTx 639 | case itypes.MsgRemoveLiquidity: 640 | msg := msg.(itypes.MsgRemoveLiquidity) 641 | 642 | docTx.From = msg.Sender.String() 643 | docTx.To = "" 644 | docTx.Amount = itypes.ParseCoins(msg.WithdrawLiquidity.String()) 645 | docTx.Type = constant.TxTypeRemoveLiquidity 646 | txMsg := imsg.DocTxMsgRemoveLiquidity{} 647 | txMsg.BuildMsg(msg) 648 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 649 | Type: txMsg.Type(), 650 | Msg: &txMsg, 651 | }) 652 | return docTx 653 | case itypes.MsgSwapOrder: 654 | msg := msg.(itypes.MsgSwapOrder) 655 | 656 | docTx.From = msg.Input.Address.String() 657 | docTx.To = msg.Output.Address.String() 658 | docTx.Amount = itypes.ParseCoins(msg.Input.Coin.String()) 659 | docTx.Type = constant.TxTypeSwapOrder 660 | txMsg := imsg.DocTxMsgSwapOrder{} 661 | txMsg.BuildMsg(msg) 662 | docTx.Msgs = append(docTxMsgs, document.DocTxMsg{ 663 | Type: txMsg.Type(), 664 | Msg: &txMsg, 665 | }) 666 | return docTx 667 | 668 | default: 669 | logger.Warn("unknown msg type") 670 | } 671 | 672 | return docTx 673 | } 674 | 675 | func parseTags(result itypes.ResponseDeliverTx) map[string]string { 676 | tags := make(map[string]string, 0) 677 | for _, tag := range result.Tags { 678 | key := string(tag.Key) 679 | value := string(tag.Value) 680 | tags[key] = value 681 | } 682 | return tags 683 | } 684 | 685 | // get proposalId from tags 686 | func getProposalIdFromTags(tags []itypes.TmKVPair) (uint64, error) { 687 | //query proposal_id 688 | for _, tag := range tags { 689 | key := string(tag.Key) 690 | if key == itypes.TagGovProposalID { 691 | if proposalId, err := strconv.ParseInt(string(tag.Value), 10, 0); err != nil { 692 | return 0, err 693 | } else { 694 | return uint64(proposalId), nil 695 | } 696 | } 697 | } 698 | return 0, nil 699 | } 700 | 701 | func BuildHex(bytes []byte) string { 702 | return strings.ToUpper(hex.EncodeToString(bytes)) 703 | } 704 | 705 | // get tx status and log by query txHash 706 | func QueryTxResult(txHash []byte) (string, itypes.ResponseDeliverTx, error) { 707 | var resDeliverTx itypes.ResponseDeliverTx 708 | status := document.TxStatusSuccess 709 | 710 | client := GetClient() 711 | defer client.Release() 712 | 713 | res, err := client.Tx(txHash, false) 714 | if err != nil { 715 | // try again 716 | time.Sleep(time.Duration(1) * time.Second) 717 | if res, err := client.Tx(txHash, false); err != nil { 718 | return "unknown", resDeliverTx, err 719 | } else { 720 | resDeliverTx = res.TxResult 721 | } 722 | } else { 723 | resDeliverTx = res.TxResult 724 | } 725 | 726 | if resDeliverTx.Code != 0 { 727 | status = document.TxStatusFail 728 | } 729 | 730 | return status, resDeliverTx, nil 731 | } 732 | -------------------------------------------------------------------------------- /util/helper/tx_test.go: -------------------------------------------------------------------------------- 1 | // package for parse tx struct from binary data 2 | 3 | package helper 4 | 5 | import ( 6 | "encoding/json" 7 | "os" 8 | "testing" 9 | ) 10 | 11 | func TestMain(m *testing.M) { 12 | //InitClientPool() 13 | code := m.Run() 14 | os.Exit(code) 15 | } 16 | 17 | func TestParseTx(t *testing.T) { 18 | client := GetClient() 19 | // release client 20 | defer client.Release() 21 | 22 | var height = int64(710) 23 | 24 | block, err := client.Client.Block(&height) 25 | 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | if block.BlockMeta.Header.NumTxs > 0 { 31 | txs := block.Block.Data.Txs 32 | tx := ParseTx(txs[0], block.Block) 33 | txBytes, _ := json.Marshal(tx) 34 | t.Logf("tx is %v\n", string(txBytes)) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /util/helper/validator.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "fmt" 5 | "github.com/irisnet/irishub-sync/logger" 6 | "github.com/irisnet/irishub-sync/types" 7 | "github.com/irisnet/irishub-sync/util/constant" 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | func GetValidators() (validators []types.StakeValidator) { 12 | keys := types.ValidatorsKey 13 | cdc := types.GetCodec() 14 | var kvs []types.KVPair 15 | 16 | resRaw, err := Query(keys, constant.StoreNameStake, "subspace") 17 | 18 | if err != nil || len(resRaw) == 0 { 19 | logger.Error("GetValidators Failed ", logger.String("err", err.Error())) 20 | return 21 | } 22 | 23 | err = cdc.UnmarshalBinaryLengthPrefixed(resRaw, &kvs) 24 | if err != nil { 25 | logger.Error("UnmarshalBinaryLengthPrefixed validators err ", logger.String("err", err.Error())) 26 | return 27 | } 28 | 29 | for _, v := range kvs { 30 | addr := v.Key[1:] 31 | validator, err2 := types.UnmarshalValidator(cdc, addr, v.Value) 32 | 33 | if err2 != nil { 34 | logger.Error("types.UnmarshalValidator", logger.String("err", err2.Error())) 35 | continue 36 | } 37 | 38 | validators = append(validators, validator) 39 | } 40 | return validators 41 | } 42 | 43 | // get validator 44 | func GetValidator(valAddr string) (types.StakeValidator, error) { 45 | var ( 46 | validatorAddr types.ValAddress 47 | err error 48 | res types.StakeValidator 49 | ) 50 | 51 | cdc := types.GetCodec() 52 | 53 | validatorAddr, err = types.ValAddressFromBech32(valAddr) 54 | 55 | resRaw, err := Query(types.GetValidatorKey(validatorAddr), constant.StoreNameStake, constant.StoreDefaultEndPath) 56 | if err != nil || resRaw == nil { 57 | return res, errors.New(fmt.Sprintf("validator not found:%s", valAddr)) 58 | } 59 | 60 | res = types.MustUnmarshalValidator(cdc, validatorAddr, resRaw) 61 | 62 | return res, err 63 | } 64 | 65 | // Query a delegation based on address and validator address 66 | func GetDelegation(delAddr, valAddr string) (res types.Delegation) { 67 | var ( 68 | validatorAddr types.ValAddress 69 | err error 70 | ) 71 | cdc := types.GetCodec() 72 | 73 | delegatorAddr, err := types.AccAddressFromBech32(delAddr) 74 | if err != nil { 75 | logger.Error("types.AccAddressFromBech32 err ", logger.String("err", err.Error())) 76 | return 77 | } 78 | validatorAddr, err = types.ValAddressFromBech32(valAddr) 79 | if err != nil { 80 | logger.Error("types.ValAddressFromBech32 err ", logger.String("err", err.Error())) 81 | return 82 | } 83 | 84 | key := types.GetDelegationKey(delegatorAddr, validatorAddr) 85 | 86 | resRaw, err := Query(key, constant.StoreNameStake, constant.StoreDefaultEndPath) 87 | 88 | if err != nil { 89 | logger.Error("helper.GetDelegation err ", logger.String("delAddr", delAddr)) 90 | return 91 | } else if resRaw == nil { 92 | logger.Info("delegator don't exist delegation on validator", logger.String("delAddr", delAddr), logger.String("valAddr", valAddr)) 93 | return 94 | } 95 | 96 | res = types.MustUnmarshalDelegation(cdc, key, resRaw) 97 | return res 98 | } 99 | 100 | //Query all delegations made from one delegator 101 | //func GetDelegations(delAddr string) (delegations []types.Delegation) { 102 | // 103 | // delegatorAddr, err := types.AccAddressFromBech32(delAddr) 104 | // key := types.GetDelegationsKey(delegatorAddr) 105 | // resKVs, err := QuerySubspace(key, constant.StoreNameStake) 106 | // 107 | // if err != nil { 108 | // logger.Error("helper.GetDelegations err ", logger.String("delAddr", delAddr)) 109 | // return 110 | // } else if resKVs == nil { 111 | // logger.Info("delegator don't exist delegation", logger.String("delAddr", delAddr)) 112 | // return 113 | // } 114 | // 115 | // cdc := types.GetCodec() 116 | // 117 | // for _, kv := range resKVs { 118 | // delegation := types.MustUnmarshalDelegation(cdc, kv.Key, kv.Value) 119 | // delegations = append(delegations, delegation) 120 | // } 121 | // return 122 | //} 123 | 124 | // GetCmdQueryUnbondingDelegation implements the command to query a single unbonding-delegation record. 125 | func GetUnbondingDelegation(delAddr, valAddr string) (res types.UnbondingDelegation) { 126 | cdc := types.GetCodec() 127 | 128 | delegatorAddr, _ := types.AccAddressFromBech32(delAddr) 129 | validatorAddr, _ := types.ValAddressFromBech32(valAddr) 130 | 131 | key := types.GetUBDKey(delegatorAddr, validatorAddr) 132 | 133 | resRaw, err := Query(key, constant.StoreNameStake, constant.StoreDefaultEndPath) 134 | 135 | if err != nil { 136 | logger.Error("helper.GetDelegations err ", logger.String("delAddr", delAddr)) 137 | return 138 | } else if resRaw == nil { 139 | logger.Info("delegator don't exist unbondingDelegation", logger.String("delAddr", delAddr), logger.String("valAddr", valAddr)) 140 | return 141 | } 142 | 143 | res = types.MustUnmarshalUBD(cdc, key, resRaw) 144 | 145 | return res 146 | } 147 | 148 | //Query all unbonding-delegations records for one delegator 149 | //func GetUnbondingDelegations(delAddr string) (ubds []types.UnbondingDelegation) { 150 | // delegatorAddr, _ := types.AccAddressFromBech32(delAddr) 151 | // 152 | // cdc := types.GetCodec() 153 | // key := types.GetUBDsKey(delegatorAddr) 154 | // 155 | // resKVs, err := QuerySubspace(key, constant.StoreNameStake) 156 | // if err != nil { 157 | // logger.Error("helper.GetDelegations err ", logger.String("delAddr", delAddr)) 158 | // return 159 | // } else if resKVs == nil { 160 | // logger.Info("delegator don't exist unbondingDelegation", logger.String("delAddr", delAddr)) 161 | // return 162 | // } 163 | // for _, kv := range resKVs { 164 | // ubd := types.MustUnmarshalUBD(cdc, kv.Key, kv.Value) 165 | // ubds = append(ubds, ubd) 166 | // } 167 | // return 168 | //} 169 | -------------------------------------------------------------------------------- /util/helper/validator_test.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestGetUnbondingDelegation(t *testing.T) { 10 | var delAddr = "faa1ljemm0yznz58qxxs8xyak7fashcfxf5lssn6jm" 11 | var valAddr = "fva1kca5vw7r2k72d5zy0demszmrhdz4dp8t4uat0c" 12 | 13 | res := GetUnbondingDelegation(delAddr, valAddr) 14 | r, _ := json.Marshal(res) 15 | fmt.Println(string(r)) 16 | } 17 | 18 | func TestGetValidator(t *testing.T) { 19 | var valAddr = "fva1phst8wkk27jd748p0nmffzh6288kldlpxq39h8" 20 | if res, err := GetValidator(valAddr); err != nil { 21 | t.Fatal(err) 22 | } else { 23 | resBytes, _ := json.MarshalIndent(res, "", "\t") 24 | t.Log(string(resBytes)) 25 | } 26 | } 27 | --------------------------------------------------------------------------------