diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a2ab5e61..25588ac99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.6.1-hf.2 +BUG FIXES +* [\#641](https://github.com/binance-chain/node/pull/641) [Dex] Add max lock time in time lock plugin + +IMPROVEMENTS +* [\#638](https://github.com/binance-chain/node/pull/638) [Pub] BEP39 - add memo to transfer kafka message +* [\#639](https://github.com/binance-chain/node/pull/639) [ABCI] add levels parameter to depth ABCI query + ## 0.6.1-hf.1 BUG FIXES * [\#635](https://github.com/binance-chain/node/pull/635) fix panic in pre-check is not recovered diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 23c9f5d7c..351cb45f7 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,3 +1,2 @@ # Changelog ## Develop - diff --git a/app/pub/helpers.go b/app/pub/helpers.go index 6522f610e..5a743abed 100644 --- a/app/pub/helpers.go +++ b/app/pub/helpers.go @@ -46,8 +46,16 @@ func GetTransferPublished(pool *sdk.Pool, height, blockTime int64) *Transfers { txs := pool.GetTxs() txs.Range(func(key, value interface{}) bool { txhash := key.(string) - t := value.(sdk.Tx) - msgs := t.GetMsgs() + stdTx, ok := value.(auth.StdTx) + var memo string + if ok { + memo = stdTx.GetMemo() + } else { + Logger.Error("tx is not an auth.StdTx", "hash", txhash) + return true + } + + msgs := stdTx.GetMsgs() for _, m := range msgs { msg, ok := m.(bank.MsgSend) if !ok { @@ -61,7 +69,7 @@ func GetTransferPublished(pool *sdk.Pool, height, blockTime int64) *Transfers { } receivers = append(receivers, Receiver{Addr: o.Address.String(), Coins: coins}) } - transferToPublish = append(transferToPublish, Transfer{TxHash: txhash, From: msg.Inputs[0].Address.String(), To: receivers}) + transferToPublish = append(transferToPublish, Transfer{TxHash: txhash, Memo: memo, From: msg.Inputs[0].Address.String(), To: receivers}) } return true }) diff --git a/app/pub/msgs.go b/app/pub/msgs.go index e8f735e89..980f0e565 100644 --- a/app/pub/msgs.go +++ b/app/pub/msgs.go @@ -48,7 +48,7 @@ var latestSchemaVersions = map[msgType]int{ booksTpe: 0, executionResultTpe: 1, blockFeeTpe: 0, - transferTpe: 0, + transferTpe: 1, } type AvroOrJsonMsg interface { @@ -652,17 +652,19 @@ func (msg Receiver) ToNativeMap() map[string]interface{} { type Transfer struct { TxHash string + Memo string // Added for BEP39 From string To []Receiver } func (msg Transfer) String() string { - return fmt.Sprintf("Transfer : from: %s, to: %v", msg.From, msg.To) + return fmt.Sprintf("Transfer: txHash: %s, memo: %s, from: %s, to: %v", msg.TxHash, msg.Memo, msg.From, msg.To) } func (msg Transfer) ToNativeMap() map[string]interface{} { var native = make(map[string]interface{}) native["txhash"] = msg.TxHash + native["memo"] = msg.Memo native["from"] = msg.From to := make([]map[string]interface{}, len(msg.To), len(msg.To)) for idx, t := range msg.To { diff --git a/app/pub/schema_test.go b/app/pub/schema_test.go index 811a5a895..935fb5e01 100644 --- a/app/pub/schema_test.go +++ b/app/pub/schema_test.go @@ -104,7 +104,7 @@ func TestBlockFeeMarshaling(t *testing.T) { func TestTransferMarshaling(t *testing.T) { publisher := NewKafkaMarketDataPublisher(Logger, "") - msg := Transfers{42, 20, 1000, []Transfer{{TxHash: "123456ABCDE", From: "", To: []Receiver{Receiver{"bnc1", []Coin{{"BNB", 100}, {"BTC", 100}}}, Receiver{"bnc2", []Coin{{"BNB", 200}, {"BTC", 200}}}}}}} + msg := Transfers{42, 20, 1000, []Transfer{{TxHash: "123456ABCDE", Memo: "1234", From: "", To: []Receiver{Receiver{"bnc1", []Coin{{"BNB", 100}, {"BTC", 100}}}, Receiver{"bnc2", []Coin{{"BNB", 200}, {"BTC", 200}}}}}}} _, err := publisher.marshal(&msg, transferTpe) if err != nil { t.Fatal(err) diff --git a/app/pub/schemas.go b/app/pub/schemas.go index de4d6f0c6..4a06419e9 100644 --- a/app/pub/schemas.go +++ b/app/pub/schemas.go @@ -6,7 +6,7 @@ package pub // put old version into pub/schemas with version suffixed to filename for tracking historical version // Backward compatibility: -// 1. publisher add field, consumer should initialize two decoder with two publisher schema, choose which decoded should be used by `lastestSchemaVersion` component in kafka message Key +// 1. publisher add field, consumer should initialize two decoder with two publisher schema, choose which decoder should be used by `lastestSchemaVersion` component in kafka message Key // 2. consumer add field, consumer should initialize one decode with publisher schema and consumer schema. In which, consumer schema should define default value for added field const ( @@ -264,6 +264,7 @@ const ( "namespace": "com.company", "fields": [ { "name": "txhash", "type": "string" }, + { "name": "memo", "type": "string" }, { "name": "from", "type": "string" }, { "name": "to", "type": { diff --git a/app/pub/schemas/transfers0.avsc b/app/pub/schemas/transfers0.avsc new file mode 100644 index 000000000..d528e5a38 --- /dev/null +++ b/app/pub/schemas/transfers0.avsc @@ -0,0 +1,51 @@ +{ + "type": "record", + "name": "Transfers", + "namespace": "com.company", + "fields": [ + { "name": "height", "type": "long"}, + { "name": "num", "type": "int" }, + { "name": "timestamp", "type": "long" }, + { "name": "transfers", + "type": { + "type": "array", + "items": { + "type": "record", + "name": "Transfer", + "namespace": "com.company", + "fields": [ + { "name": "txhash", "type": "string" }, + { "name": "from", "type": "string" }, + { "name": "to", + "type": { + "type": "array", + "items": { + "type": "record", + "name": "Receiver", + "namespace": "com.company", + "fields": [ + { "name": "addr", "type": "string" }, + { "name": "coins", + "type": { + "type": "array", + "items": { + "type": "record", + "name": "Coin", + "namespace": "com.company", + "fields": [ + { "name": "denom", "type": "string" }, + { "name": "amount", "type": "long" } + ] + } + } + } + ] + } + } + } + ] + } + } + } + ] +} \ No newline at end of file diff --git a/app/sentryapp.go b/app/sentryapp.go index f2e9f9a7b..d5edc91c2 100644 --- a/app/sentryapp.go +++ b/app/sentryapp.go @@ -83,7 +83,7 @@ func (app *SentryApplication) InitChain(req abci.RequestInitChain) (res abci.Res msgs := tx.GetMsgs() for _, msg := range msgs { switch msg := msg.(type) { - case stake.MsgCreateValidator: + case stake.MsgCreateValidatorProposal: validators = append(validators, abci.ValidatorUpdate{PubKey: tmtypes.TM2PB.PubKey(msg.PubKey), Power: defaultPower.ToInt64()}) default: app.logger.Info("MsgType %s not supported ", msg.Type()) diff --git a/cmd/bnbsentry/main.go b/cmd/bnbsentry/main.go index a3c2e17ed..e0c0a0db6 100644 --- a/cmd/bnbsentry/main.go +++ b/cmd/bnbsentry/main.go @@ -4,7 +4,6 @@ import ( "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/server" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/libs/cli" "github.com/binance-chain/node/app" @@ -15,12 +14,6 @@ const flagSequentialABCI = "seq-abci" func main() { cdc := app.Codec ctx := app.ServerContext - config := sdk.GetConfig() - - config.SetBech32PrefixForAccount(ctx.Bech32PrefixAccAddr, ctx.Bech32PrefixAccPub) - config.SetBech32PrefixForValidator(ctx.Bech32PrefixValAddr, ctx.Bech32PrefixValPub) - config.SetBech32PrefixForConsensusNode(ctx.Bech32PrefixConsAddr, ctx.Bech32PrefixConsPub) - config.Seal() rootCmd := &cobra.Command{ Use: "bnbsentry", @@ -34,7 +27,6 @@ func main() { startCmd.Flags().IntVarP(&app.SentryAppConfig.CacheSize, "cache_size", "c", app.DefaultCacheSize, "The cache size of sentry node") startCmd.Flags().IntVarP(&app.SentryAppConfig.MaxSurvive, "max_survive", "s", app.DefaultMaxSurvive, "The max survive of sentry node") rootCmd.AddCommand(startCmd) - startCmd.Flags().Bool(flagSequentialABCI, true, "whether check tx in sequentially ad") // prepare and add flags executor := cli.PrepareBaseCmd(rootCmd, "BC", app.DefaultNodeHome) diff --git a/networks/publisher/newPublisher.sh b/networks/publisher/newPublisher.sh index 823aaeff5..951bfa248 100755 --- a/networks/publisher/newPublisher.sh +++ b/networks/publisher/newPublisher.sh @@ -43,6 +43,7 @@ sed -i -e "s/publishBlockFee = false/publishBlockFee = true/g" ${witnesshome}/co sed -i -e "s/accountBalanceTopic = \"accounts\"/accountBalanceTopic = \"test\"/g" ${witnesshome}/config/app.toml sed -i -e "s/orderBookTopic = \"orders\"/orderBookTopic = \"test\"/g" ${witnesshome}/config/app.toml sed -i -e "s/orderUpdatesTopic = \"orders\"/orderUpdatesTopic = \"test\"/g" ${witnesshome}/config/app.toml +sed -i -e "s/transferTopic = \"transfers\"/transferTopic = \"test\"/g" ${witnesshome}/config/app.toml sed -i -e "s/blockFeeTopic = \"accounts\"/blockFeeTopic = \"test\"/g" ${witnesshome}/config/app.toml sed -i -e "s/publishKafka = false/publishKafka = true/g" ${witnesshome}/config/app.toml sed -i -e "s/publishLocal = false/publishLocal = true/g" ${witnesshome}/config/app.toml diff --git a/plugins/dex/abci.go b/plugins/dex/abci.go index 8cc5723e5..00616c057 100644 --- a/plugins/dex/abci.go +++ b/plugins/dex/abci.go @@ -15,7 +15,8 @@ import ( ) // TODO: improve, should be configurable -const MaxDepthLevels = 100 // matches UI requirement +const MaxDepthLevels = 1000 // matches UI requirement +const DefaultDepthLevels = 100 // matches UI requirement func createAbciQueryHandler(keeper *DexKeeper) app.AbciQueryHandler { return func(app app.ChainApp, req abci.RequestQuery, path []string) (res *abci.ResponseQuery) { @@ -80,16 +81,32 @@ func createAbciQueryHandler(keeper *DexKeeper) app.AbciQueryHandler { Value: bz, } case "orderbook": // args: ["dex", "orderbook"] - //TODO: sync lock, validate pair, level number + //TODO: sync lock, validate pair if len(path) < 3 { return &abci.ResponseQuery{ Code: uint32(sdk.CodeUnknownRequest), - Log: "OrderBook query requires the pair symbol", + Log: "OrderBook query requires the pair symbol and levels", } } pair := path[2] height := app.GetContextForCheckState().BlockHeight() - levels := keeper.GetOrderBookLevels(pair, MaxDepthLevels) + levelLimit := DefaultDepthLevels + if len(path) == 4 { + if l, err := strconv.Atoi(path[3]); err != nil { + return &abci.ResponseQuery{ + Code: uint32(sdk.CodeUnknownRequest), + Log: fmt.Sprintf("OrderBook query requires valid int levels parameter: %v", err), + } + } else if l <= 0 || l > MaxDepthLevels { + return &abci.ResponseQuery{ + Code: uint32(sdk.CodeUnknownRequest), + Log: "OrderBook query requires valid levels (>0 && <1000)", + } + } else { + levelLimit = l + } + } + levels := keeper.GetOrderBookLevels(pair, levelLimit) book := store.OrderBook{ Height: height, Levels: levels, diff --git a/plugins/dex/client/cli/commands.go b/plugins/dex/client/cli/commands.go index 31716ac55..30c3b1f3c 100644 --- a/plugins/dex/client/cli/commands.go +++ b/plugins/dex/client/cli/commands.go @@ -9,6 +9,7 @@ import ( const ( flagSymbol = "symbol" + flagLevels = "levels" ) func AddCommands(cmd *cobra.Command, cdc *wire.Codec) { diff --git a/plugins/dex/client/cli/tx.go b/plugins/dex/client/cli/tx.go index eb335a8ce..e0b7a7d1e 100644 --- a/plugins/dex/client/cli/tx.go +++ b/plugins/dex/client/cli/tx.go @@ -16,6 +16,7 @@ import ( "github.com/binance-chain/node/common/client" "github.com/binance-chain/node/common/types" "github.com/binance-chain/node/common/utils" + "github.com/binance-chain/node/plugins/dex" "github.com/binance-chain/node/plugins/dex/order" "github.com/binance-chain/node/plugins/dex/store" "github.com/binance-chain/node/wire" @@ -95,6 +96,7 @@ func newOrderCmd(cdc *wire.Codec) *cobra.Command { }, } cmd.Flags().StringP(flagSymbol, "l", "", "the listed trading pair, such as ADA_BNB") + cmd.Flags().IntP(flagLevels, "L", 100, "maximum level (1,5,10,20,50,100,500,1000) to return") cmd.Flags().StringP(flagSide, "s", "", "side (buy as 1 or sell as 2) of the order") cmd.Flags().StringP(flagPrice, "p", "", "price for the order") cmd.Flags().StringP(flagQty, "q", "", "quantity for the order") @@ -114,8 +116,12 @@ func showOrderBookCmd(cdc *wire.Codec) *cobra.Command { if err != nil { return err } + levelsLimit := viper.GetInt(flagLevels) + if levelsLimit <= 0 || levelsLimit > dex.MaxDepthLevels { + return fmt.Errorf("%s should be greater than 0 and not exceed %d", flagLevels, dex.MaxDepthLevels) + } - ob, err := store.GetOrderBook(cdc, ctx, symbol) + ob, err := store.GetOrderBook(cdc, ctx, symbol, levelsLimit) if err != nil { return err } diff --git a/plugins/dex/client/rest/getdepth.go b/plugins/dex/client/rest/getdepth.go index 36a03216c..3d5b04c59 100644 --- a/plugins/dex/client/rest/getdepth.go +++ b/plugins/dex/client/rest/getdepth.go @@ -4,7 +4,6 @@ import ( "errors" "net/http" "strconv" - "strings" "github.com/cosmos/cosmos-sdk/client/context" @@ -13,12 +12,10 @@ import ( "github.com/binance-chain/node/wire" ) -const allowedLimits = "5,10,20,50,100" -const defaultLimit = "100" +var allowedLimits = [7]int{5, 10, 20, 50, 100, 500, 1000} // DepthReqHandler creates an http request handler to show market depth data func DepthReqHandler(cdc *wire.Codec, ctx context.CLIContext) http.HandlerFunc { - allowedLimitsA := strings.Split(allowedLimits, ",") type params struct { symbol string @@ -36,24 +33,24 @@ func DepthReqHandler(cdc *wire.Codec, ctx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { limitStr := r.FormValue("limit") + limit, err := strconv.Atoi(limitStr) + if err != nil { + throw(w, http.StatusExpectationFailed, errors.New("invalid limit, supported limits: [5,10,20,50,100,500,1000]")) + return + } // validate limit param - limitStrOk := defaultLimit - for _, lmt := range allowedLimitsA { - if lmt == limitStr { - limitStrOk = limitStr + limitOk := -1 + for _, lmt := range allowedLimits { + if lmt == limit { + limitOk = lmt break } } - limit, _ := strconv.Atoi(defaultLimit) - if len(limitStrOk) > 0 { - var err error - limit, err = strconv.Atoi(limitStrOk) - if err != nil { - throw(w, http.StatusExpectationFailed, errors.New("invalid limit")) - return - } + if limitOk == -1 { + throw(w, http.StatusExpectationFailed, errors.New("invalid limit, supported limits: [5,10,20,50,100,500,1000]")) + return } // collect params @@ -63,14 +60,14 @@ func DepthReqHandler(cdc *wire.Codec, ctx context.CLIContext) http.HandlerFunc { } // validate pair - err := store.ValidatePairSymbol(params.symbol) + err = store.ValidatePairSymbol(params.symbol) if err != nil { throw(w, http.StatusNotFound, err) return } // query order book (includes block height) - ob, err := store.GetOrderBook(cdc, ctx, params.symbol) + ob, err := store.GetOrderBook(cdc, ctx, params.symbol, params.limit) if err != nil { throw(w, http.StatusInternalServerError, err) return diff --git a/plugins/dex/store/codec.go b/plugins/dex/store/codec.go index 6cfd9a3a3..301cf7aca 100644 --- a/plugins/dex/store/codec.go +++ b/plugins/dex/store/codec.go @@ -9,8 +9,8 @@ import ( ) // queryOrderBook queries the store for the serialized order book for a given pair. -func queryOrderBook(cdc *wire.Codec, ctx context.CLIContext, pair string) (*[]byte, error) { - bz, err := ctx.Query(fmt.Sprintf("dex/orderbook/%s", pair), nil) +func queryOrderBook(cdc *wire.Codec, ctx context.CLIContext, pair string, levels int) (*[]byte, error) { + bz, err := ctx.Query(fmt.Sprintf("dex/orderbook/%s/%d", pair, levels), nil) if err != nil { return nil, err } @@ -28,8 +28,8 @@ func decodeOrderBook(cdc *wire.Codec, bz *[]byte) (*OrderBook, error) { } // GetOrderBook decodes the order book from the serialized store -func GetOrderBook(cdc *wire.Codec, ctx context.CLIContext, pair string) (*OrderBook, error) { - bz, err := queryOrderBook(cdc, ctx, pair) +func GetOrderBook(cdc *wire.Codec, ctx context.CLIContext, pair string, levels int) (*OrderBook, error) { + bz, err := queryOrderBook(cdc, ctx, pair, levels) if err != nil { return nil, err } diff --git a/plugins/tokens/timelock/msgs.go b/plugins/tokens/timelock/msgs.go index 5e1cc2f3e..46fc46f8f 100644 --- a/plugins/tokens/timelock/msgs.go +++ b/plugins/tokens/timelock/msgs.go @@ -11,8 +11,9 @@ import ( const ( MsgRoute = "timelock" - MaxDescriptionLength = 128 - MinLockTime = 60 * time.Second + MaxDescriptionLength = 128 + MinLockTime = 60 * time.Second + MaxLockTime int64 = 253402300800 //seconds of 10000-01-01, which is required by amino ) var _ sdk.Msg = TimeLockMsg{} @@ -54,6 +55,10 @@ func (msg TimeLockMsg) ValidateBasic() sdk.Error { return ErrInvalidLockTime(DefaultCodespace, fmt.Sprintf("lock time(%d) should be larger than 0", msg.LockTime)) } + if msg.LockTime >= MaxLockTime { + return ErrInvalidLockTime(DefaultCodespace, fmt.Sprintf("lock time(%d) should be less than %d", msg.LockTime, MaxLockTime)) + } + if !msg.Amount.IsValid() { return sdk.ErrInvalidCoins(msg.Amount.String()) } @@ -118,6 +123,10 @@ func (msg TimeRelockMsg) ValidateBasic() sdk.Error { return ErrInvalidLockTime(DefaultCodespace, fmt.Sprintf("lock time(%d) should not be less than 0", msg.LockTime)) } + if msg.LockTime >= MaxLockTime { + return ErrInvalidLockTime(DefaultCodespace, fmt.Sprintf("lock time(%d) should be less than %d", msg.LockTime, MaxLockTime)) + } + if !msg.Amount.IsValid() { return sdk.ErrInvalidCoins(msg.Amount.String()) } diff --git a/plugins/tokens/timelock/msgs_test.go b/plugins/tokens/timelock/msgs_test.go index 79dc2e77d..7b33b5eea 100644 --- a/plugins/tokens/timelock/msgs_test.go +++ b/plugins/tokens/timelock/msgs_test.go @@ -72,6 +72,17 @@ func TestTimeLockMsg(t *testing.T) { pass: true, errorCode: sdk.CodeType(0), }, + { + from: addrs[0], + description: strings.Repeat("d", 120), + amount: sdk.Coins{ + sdk.NewCoin("ANB", 2000e8), + sdk.NewCoin("BNB", 2000e8), + }, + lockTime: MaxLockTime, + pass: false, + errorCode: CodeInvalidLockTime, + }, } for i, tc := range tests { @@ -181,6 +192,18 @@ func TestTimeRelockMsg(t *testing.T) { pass: true, errorCode: sdk.CodeType(0), }, + { + from: addrs[0], + id: 1, + description: "", + amount: sdk.Coins{ + sdk.NewCoin("ANB", 2000e8), + sdk.NewCoin("BNB", 2000e8), + }, + lockTime: MaxLockTime, + pass: false, + errorCode: CodeInvalidLockTime, + }, } for i, tc := range tests { diff --git a/version/version.go b/version/version.go index 0840f16c5..6acde6882 100644 --- a/version/version.go +++ b/version/version.go @@ -12,7 +12,7 @@ var ( Version string ) -const NodeVersion = "0.6.1-hf.1" +const NodeVersion = "0.6.1-hf.2" func init() { Version = fmt.Sprintf("Binance Chain Release: %s;", NodeVersion)