diff --git a/accountmanager/accountmanager.go b/accountmanager/accountmanager.go index 9140c915..7c257ffd 100644 --- a/accountmanager/accountmanager.go +++ b/accountmanager/accountmanager.go @@ -1187,19 +1187,6 @@ func (am *AccountManager) IncAsset2Acct(fromName common.Name, toName common.Name return am.AddAccountBalanceByID(toName, assetID, amount) } -//AddBalanceByName add balance to account -//func (am *AccountManager) AddBalanceByName(accountName common.Name, assetID uint64, amount *big.Int) error { -// acct, err := am.GetAccountByName(accountName) -// if err != nil { -// return err -// } -// if acct == nil { -// return ErrAccountNotExist -// } -// return acct.AddBalanceByID(assetID, amount) -// rerturn -//} - //Process account action func (am *AccountManager) Process(accountManagerContext *types.AccountManagerContext) ([]*types.InternalAction, error) { snap := am.sdb.Snapshot() @@ -1213,7 +1200,7 @@ func (am *AccountManager) Process(accountManagerContext *types.AccountManagerCon func (am *AccountManager) process(accountManagerContext *types.AccountManagerContext) ([]*types.InternalAction, error) { action := accountManagerContext.Action number := accountManagerContext.Number - if err := action.CheckValid(accountManagerContext.ChainConfig); err != nil { + if err := action.Check(accountManagerContext.ChainConfig); err != nil { return nil, err } diff --git a/asset/asset_object.go b/asset/asset_object.go index 0a3b801a..5fc85379 100644 --- a/asset/asset_object.go +++ b/asset/asset_object.go @@ -22,8 +22,8 @@ import ( ) type AssetObject struct { - AssetId uint64 `json:"assetId,omitempty"` - Number uint64 `json:"number,omitempty"` + AssetId uint64 `json:"assetId"` + Number uint64 `json:"number"` AssetName string `json:"assetName"` Symbol string `json:"symbol"` Amount *big.Int `json:"amount"` diff --git a/blockchain/blockgenerator.go b/blockchain/blockgenerator.go index 65b6d368..6fe4dad5 100644 --- a/blockchain/blockgenerator.go +++ b/blockchain/blockgenerator.go @@ -47,14 +47,14 @@ type BlockGenerator struct { } // SetCoinbase sets the coinbase of the generated block. -func (bg *BlockGenerator) SetCoinbase(addr common.Name) { +func (bg *BlockGenerator) SetCoinbase(name common.Name) { if bg.gasPool != nil { if len(bg.txs) > 0 { panic("coinbase must be set before adding transactions") } panic("coinbase can only be set once") } - bg.header.Coinbase = addr + bg.header.Coinbase = name bg.gasPool = new(common.GasPool).AddGas(bg.header.GasLimit) } diff --git a/blockchain/forkcontroller_test.go b/blockchain/forkcontroller_test.go index af54b6f6..dfeb66d7 100644 --- a/blockchain/forkcontroller_test.go +++ b/blockchain/forkcontroller_test.go @@ -39,23 +39,23 @@ func TestForkController(t *testing.T) { } fc := NewForkController(testcfg, params.DefaultChainconfig) - var height int64 + var number int64 for j := 0; j < 2; j++ { for i := 0; i < 8; i++ { - block := &types.Block{Head: &types.Header{Number: big.NewInt(height)}} + block := &types.Block{Head: &types.Header{Number: big.NewInt(number)}} block.Head.WithForkID(uint64(j), uint64(j+1)) assert.NoError(t, fc.checkForkID(block.Header(), statedb)) assert.NoError(t, fc.update(block, statedb)) - height++ + number++ } for i := 0; i < 10; i++ { - block := &types.Block{Head: &types.Header{Number: big.NewInt(height)}} + block := &types.Block{Head: &types.Header{Number: big.NewInt(number)}} block.Head.WithForkID(uint64(j+1), uint64(j+1)) assert.NoError(t, fc.checkForkID(block.Header(), statedb)) assert.NoError(t, fc.update(block, statedb)) - height++ + number++ } id, _, err := fc.currentForkID(statedb) @@ -77,14 +77,14 @@ func TestUpdateDifferentForkBlock(t *testing.T) { } fc := NewForkController(testcfg, params.DefaultChainconfig) - var height int64 + var number int64 for j := 0; j < 2; j++ { for i := 0; i < 7; i++ { - block := &types.Block{Head: &types.Header{Number: big.NewInt(height)}} + block := &types.Block{Head: &types.Header{Number: big.NewInt(number)}} block.Head.WithForkID(uint64(0), uint64(j+1)) assert.NoError(t, fc.checkForkID(block.Header(), statedb)) assert.NoError(t, fc.update(block, statedb)) - height++ + number++ info, err := fc.getForkInfo(statedb) if err != nil { diff --git a/blockchain/genesis.go b/blockchain/genesis.go index d051e742..fcd44c46 100644 --- a/blockchain/genesis.go +++ b/blockchain/genesis.go @@ -138,8 +138,8 @@ func SetupGenesisBlock(db fdb.Database, genesis *Genesis) (chainCfg *params.Chai } newcfg := genesis.configOrDefault(stored) - height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db)) - if height == nil { + number := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db)) + if number == nil { return newcfg, dposConfig(newcfg), common.Hash{}, fmt.Errorf("missing block number for head header hash") } @@ -353,7 +353,7 @@ func (g *Genesis) ToBlock(db fdb.Database) (*types.Block, []*types.Receipt) { URL: candidate.URL, Quantity: big.NewInt(0), TotalQuantity: big.NewInt(0), - Height: number.Uint64(), + Number: number.Uint64(), }); err != nil { panic(fmt.Sprintf("genesis create candidate err %v", err)) } diff --git a/build/eg_config.yaml b/build/eg_config.yaml index 8c5b673f..68849470 100644 --- a/build/eg_config.yaml +++ b/build/eg_config.yaml @@ -33,8 +33,6 @@ log: node: # the node datadir datadir: "./build/testdatadir" - # Reduce key-derivation RAM & CPU usage at some expense of KDF strength - lightkdf: false # RPC:ipc file name ipcpath: "ft.ipc" # RPC:http host address diff --git a/cmd/ft/chain.go b/cmd/ft/chain.go index 69c497a1..477fd13f 100644 --- a/cmd/ft/chain.go +++ b/cmd/ft/chain.go @@ -17,52 +17,28 @@ package main import ( - "compress/gzip" - "errors" "fmt" - "io" - "os" - "os/signal" - "runtime" - "strconv" - "strings" - "sync/atomic" - "syscall" - "time" - "github.com/ethereum/go-ethereum/log" - "github.com/fractalplatform/fractal/blockchain" - "github.com/fractalplatform/fractal/ftservice" + "github.com/fractalplatform/fractal/params" "github.com/fractalplatform/fractal/types" - ldb "github.com/fractalplatform/fractal/utils/fdb/leveldb" - "github.com/fractalplatform/fractal/utils/rlp" "github.com/spf13/cobra" - "github.com/syndtr/goleveldb/leveldb/util" -) - -const ( - importBatchSize = 2500 ) var ( - importCommnad = &cobra.Command{ - Use: "import -d -g ", - Short: "Import a blockchain file", - Long: "Import a blockchain file", - Run: func(cmd *cobra.Command, args []string) { - ftCfgInstance.LogCfg.Setup() - if err := importChain(args); err != nil { - fmt.Println(err) - } - }, - } - exportCommand = &cobra.Command{ - Use: "export -d ", - Short: "Export blockchain to file", - Long: "Export blockchain to file", + chainCommand = &cobra.Command{ + Use: "chain", + Short: "Support blockchain pure state ", + Long: "Support blockchain pure state ", + Args: cobra.NoArgs, + } + + statePureCommand = &cobra.Command{ + Use: "startpure ", + Short: "Start or stop pure state ", + Long: "Start or stop pure state", + Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { - ftCfgInstance.LogCfg.Setup() - if err := exportChain(args); err != nil { + if err := prueState(args[0]); err != nil { fmt.Println(err) } }, @@ -70,289 +46,24 @@ var ( ) func init() { - RootCmd.AddCommand(importCommnad, exportCommand) - importCommnad.Flags().StringVarP(&ftCfgInstance.NodeCfg.DataDir, "datadir", "d", ftCfgInstance.NodeCfg.DataDir, "Data directory for the databases ") - importCommnad.Flags().StringVarP(&ftCfgInstance.GenesisFile, "genesis", "g", "", "genesis json file") - exportCommand.Flags().StringVarP(&ftCfgInstance.NodeCfg.DataDir, "datadir", "d", ftCfgInstance.NodeCfg.DataDir, "Data directory for the databases ") -} - -func exportChain(args []string) error { - if len(args) < 1 { - return errors.New("This command requires an argument") - } - - start := time.Now() - - stack, err := makeNode() - if err != nil { - return err - } - - ctx := stack.GetNodeConfig() - ftsrv, err := ftservice.New(ctx, ftCfgInstance.FtServiceCfg) - if err != nil { - return err - } - - fp := args[0] - if len(args) < 3 { - err = exportBlockChain(ftsrv.BlockChain(), fp) - } else { - first, ferr := strconv.ParseInt(args[1], 10, 64) - last, lerr := strconv.ParseInt(args[2], 10, 64) - if ferr != nil || lerr != nil { - return errors.New("Export error in parsing parameters: block number not an integer") - } - if first < 0 || last < 0 { - return errors.New("Export error: block number must be greater than 0") - } - err = exportAppendBlockChain(ftsrv.BlockChain(), fp, uint64(first), uint64(last)) - } - log.Info("Export done in ", "time", time.Since(start)) - if err != nil { - return err - } - - return nil -} - -func exportBlockChain(b *blockchain.BlockChain, fn string) error { - log.Info("Exporting blockchain", "file", fn) - // Open the file handle and potentially wrap with a gzip stream - fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) - if err != nil { - return err - } - defer fh.Close() - - var writer io.Writer = fh - if strings.HasSuffix(fn, ".gz") { - writer = gzip.NewWriter(writer) - defer writer.(*gzip.Writer).Close() - } - // Iterate over the blocks and export them - if err := b.Export(writer); err != nil { - return err - } - log.Info("Exported blockchain", "file", fn) - - return nil -} - -// ExportAppendChain exports a blockchain into the specified file, appending to -// the file if data already exists in it. -func exportAppendBlockChain(b *blockchain.BlockChain, fn string, first uint64, last uint64) error { - log.Info("Exporting blockchain", "file", fn) - // Open the file handle and potentially wrap with a gzip stream - fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) - if err != nil { - return err - } - defer fh.Close() - - var writer io.Writer = fh - if strings.HasSuffix(fn, ".gz") { - writer = gzip.NewWriter(writer) - defer writer.(*gzip.Writer).Close() - } - // Iterate over the blocks and export them - if err := b.ExportN(writer, first, last); err != nil { - return err - } - log.Info("Exported blockchain to", "file", fn) - return nil -} - -func importChain(args []string) error { - if len(args) < 1 { - return errors.New("This command requires an argument") - } - - stack, err := makeNode() - if err != nil { - return err - } - - ctx := stack.GetNodeConfig() - ftsrv, err := ftservice.New(ctx, ftCfgInstance.FtServiceCfg) - if err != nil { - return err - } - - // Start periodically gathering memory profiles - var peakMemAlloc, peakMemSys uint64 - go func() { - stats := new(runtime.MemStats) - for { - runtime.ReadMemStats(stats) - if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc { - atomic.StoreUint64(&peakMemAlloc, stats.Alloc) - } - if atomic.LoadUint64(&peakMemSys) < stats.Sys { - atomic.StoreUint64(&peakMemSys, stats.Sys) - } - time.Sleep(5 * time.Second) - } - }() - - start := time.Now() - - fp := args[0] - if len(args) == 1 { - if err := importBlockchain(ftsrv.BlockChain(), fp); err != nil { - return fmt.Errorf("Import error: %v", err) - } - } else { - for i := 0; i < len(args); i++ { - if err := importBlockchain(ftsrv.BlockChain(), args[i]); err != nil { - return fmt.Errorf("Import error: %v, %v", err, args[i]) - } - } - } - - log.Info("Import done in ", "time", time.Since(start)) - - db := ftsrv.ChainDb().(*ldb.LDBDatabase) - stats, err := db.LDB().GetProperty("leveldb.stats") - if err != nil { - return fmt.Errorf("Failed to read database stats: %v", err) - } - fmt.Println(stats) - - ioStats, err := db.LDB().GetProperty("leveldb.iostats") - if err != nil { - return fmt.Errorf("Failed to read database iostats: %v", err) - } - fmt.Println(ioStats) - - mem := new(runtime.MemStats) - runtime.ReadMemStats(mem) - - fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024) - fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024) - fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) - fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) - - // Compact the entire database to more accurately measure disk io and print the stats - start = time.Now() - fmt.Println("Compacting entire database...") - if err = db.LDB().CompactRange(util.Range{}); err != nil { - return fmt.Errorf("Compaction failed: %v", err) - } - fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) - - stats, err = db.LDB().GetProperty("leveldb.stats") - if err != nil { - return fmt.Errorf("Failed to read database stats: %v", err) - } - fmt.Println(stats) - - ioStats, err = db.LDB().GetProperty("leveldb.iostats") - if err != nil { - return fmt.Errorf("Failed to read database iostats: %v", err) - } - fmt.Println(ioStats) - - return nil + RootCmd.AddCommand(chainCommand) + chainCommand.AddCommand(statePureCommand) + statePureCommand.Flags().StringVarP(&ipcEndpoint, "ipcpath", "i", defaultIPCEndpoint(params.ClientIdentifier), "IPC Endpoint path") } -func importBlockchain(chain *blockchain.BlockChain, fn string) error { - // Watch for Ctrl-C while the import is running. - // If a signal is received, the import will stop at the next batch. - interrupt := make(chan os.Signal, 1) - stop := make(chan struct{}) - signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) - defer signal.Stop(interrupt) - defer close(interrupt) - go func() { - if _, ok := <-interrupt; ok { - log.Info("Interrupted during import, stopping at next batch") - } - close(stop) - }() - checkInterrupt := func() bool { - select { - case <-stop: - return true - default: - return false - } - } +func prueState(arg string) error { + var enable bool - log.Info("Importing blockchain", "file", fn) - - // Open the file handle and potentially unwrap the gzip stream - fh, err := os.Open(fn) - if err != nil { - return err + switch arg { + case "enable": + enable = true + case "disable": + default: + return fmt.Errorf("not support arg %v", arg) } - defer fh.Close() - var reader io.Reader = fh - if strings.HasSuffix(fn, ".gz") { - if reader, err = gzip.NewReader(reader); err != nil { - return err - } - } - stream := rlp.NewStream(reader, 0) - - // Run actual the import. - blocks := make(types.Blocks, importBatchSize) - n := 0 - for batch := 0; ; batch++ { - // Load a batch of RLP blocks. - if checkInterrupt() { - return fmt.Errorf("interrupted") - } - i := 0 - for ; i < importBatchSize; i++ { - var b types.Block - if err := stream.Decode(&b); err == io.EOF { - break - } else if err != nil { - return fmt.Errorf("at block %d: %v", n, err) - } - // don't import first block - if b.NumberU64() == 0 { - i-- - continue - } - blocks[i] = &b - n++ - } - if i == 0 { - break - } - // Import the batch. - if checkInterrupt() { - return fmt.Errorf("interrupted") - } - missing := missingBlocks(chain, blocks[:i]) - if len(missing) == 0 { - log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash()) - continue - } - if _, err := chain.InsertChain(missing); err != nil { - return fmt.Errorf("invalid block %d: %v", n, err) - } - } - return nil -} - -func missingBlocks(chain *blockchain.BlockChain, blocks []*types.Block) []*types.Block { - head := chain.CurrentBlock() - for i, block := range blocks { - // If we're behind the chain head, only check block, state is available at head - if head.NumberU64() > block.NumberU64() { - if !chain.HasBlock(block.Hash(), block.NumberU64()) { - return blocks[i:] - } - continue - } - // If we're above the chain head, state availability is a must - if !chain.HasBlockAndState(block.Hash(), block.NumberU64()) { - return blocks[i:] - } - } + result := new(types.BlockState) + clientCall(ipcEndpoint, &result, "ft_setStatePruning", enable) + printJSON(result) return nil } diff --git a/cmd/ft/config.go b/cmd/ft/config.go index 93313cbf..a1c94847 100644 --- a/cmd/ft/config.go +++ b/cmd/ft/config.go @@ -32,6 +32,7 @@ import ( var ( //ft config instance ftCfgInstance = defaultFtConfig() + ipcEndpoint string ) type ftConfig struct { @@ -53,20 +54,19 @@ func defaultFtConfig() *ftConfig { func defaultNodeConfig() *node.Config { return &node.Config{ - Name: params.ClientIdentifier, - DataDir: defaultDataDir(), - UseLightweightKDF: false, - IPCPath: params.ClientIdentifier + ".ipc", - HTTPHost: "localhost", - HTTPPort: 8545, - HTTPModules: []string{"ft", "miner", "dpos", "account", "txpool", "fee", "debug"}, - HTTPVirtualHosts: []string{"localhost"}, - HTTPCors: []string{"*"}, - WSHost: "localhost", - WSPort: 8546, - WSModules: []string{"ft"}, - Logger: log.New(), - P2PConfig: defaultP2pConfig(), + Name: params.ClientIdentifier, + DataDir: defaultDataDir(), + IPCPath: params.ClientIdentifier + ".ipc", + HTTPHost: "localhost", + HTTPPort: 8545, + HTTPModules: []string{"ft", "dpos", "fee", "account"}, + HTTPVirtualHosts: []string{"localhost"}, + HTTPCors: []string{"*"}, + WSHost: "localhost", + WSPort: 8546, + WSModules: []string{"ft"}, + Logger: log.New(), + P2PConfig: defaultP2pConfig(), } } diff --git a/cmd/ft/debug.go b/cmd/ft/debug.go new file mode 100644 index 00000000..a120a771 --- /dev/null +++ b/cmd/ft/debug.go @@ -0,0 +1,142 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package main + +import ( + "fmt" + "runtime" + "runtime/debug" + + "github.com/fractalplatform/fractal/params" + "github.com/spf13/cobra" +) + +var debugCmd = &cobra.Command{ + Use: "debug", + Short: "Offers and API for debug pprof", + Long: `Offers and API for debug pprof`, + Args: cobra.NoArgs, +} + +var memStatsCmd = &cobra.Command{ + Use: "memstats ", + Short: "Returns detailed runtime memory statistics.", + Long: `Returns detailed runtime memory statistics.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result = new(runtime.MemStats) + clientCall(ipcEndpoint, &result, "debug_memStats") + printJSON(result) + }, +} + +var gcStatsCmd = &cobra.Command{ + Use: "gcstats ", + Short: "Returns GC statistics.", + Long: `Returns GC statistics.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result = new(debug.GCStats) + clientCall(ipcEndpoint, &result, "debug_gcStats") + printJSON(result) + }, +} + +var cpuProfileCmd = &cobra.Command{ + Use: "cpuprofile ", + Short: "Turns on CPU profiling for nsec seconds and writesprofile data to file.", + Long: `Turns on CPU profiling for nsec seconds and writesprofile data to file.`, + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + clientCall(ipcEndpoint, nil, "debug_gcStats", args[0], parseUint64(args[1])) + printJSON(true) + }, +} + +var goTraceCmd = &cobra.Command{ + Use: "gotrace ", + Short: "Turns on CPU profiling for nsec seconds and writesprofile data to file.", + Long: `Turns on CPU profiling for nsec seconds and writesprofile data to file.`, + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + clientCall(ipcEndpoint, nil, "debug_goTrace", args[0], parseUint64(args[1])) + printJSON(true) + }, +} + +var blockProfileCmd = &cobra.Command{ + Use: "blockprofile ", + Short: "Turns on goroutine profiling for nsec seconds and writes profile data to file.", + Long: `Turns on goroutine profiling for nsec seconds and writes profile data to file.`, + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + clientCall(ipcEndpoint, nil, "debug_blockProfile", args[0], parseUint64(args[1])) + printJSON(true) + }, +} + +var mutexProfileCmd = &cobra.Command{ + Use: "mutexprofile ", + Short: "Turns on mutex profiling for nsec seconds and writes profile data to file.", + Long: `Turns on mutex profiling for nsec seconds and writes profile data to file.`, + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + clientCall(ipcEndpoint, nil, "debug_mutexProfile", args[0], parseUint64(args[1])) + printJSON(true) + }, +} + +var writeMemProfileCmd = &cobra.Command{ + Use: "writememprofile ", + Short: "Writes an allocation profile to the given file.", + Long: `Writes an allocation profile to the given file.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + clientCall(ipcEndpoint, nil, "debug_writeMemProfile", args[0]) + printJSON(true) + }, +} + +var stacksCmd = &cobra.Command{ + Use: "stacks", + Short: "Returns a printed representation of the stacks of all goroutines.", + Long: `Returns a printed representation of the stacks of all goroutines.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result []byte + clientCall(ipcEndpoint, &result, "debug_stacks") + fmt.Println(string(result)) + }, +} + +var freeOSMemoryCmd = &cobra.Command{ + Use: "freeosmemory ", + Short: "Returns unused memory to the OS.", + Long: `Returns unused memory to the OS.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + clientCall(ipcEndpoint, nil, "debug_freeOSMemory") + printJSON(true) + }, +} + +func init() { + RootCmd.AddCommand(debugCmd) + debugCmd.AddCommand(memStatsCmd, gcStatsCmd, cpuProfileCmd, goTraceCmd, blockProfileCmd, + mutexProfileCmd, writeMemProfileCmd, stacksCmd, freeOSMemoryCmd) + debugCmd.PersistentFlags().StringVarP(&ipcEndpoint, "ipcpath", "i", defaultIPCEndpoint(params.ClientIdentifier), "IPC Endpoint path") +} diff --git a/cmd/ft/export.go b/cmd/ft/export.go new file mode 100644 index 00000000..a1402190 --- /dev/null +++ b/cmd/ft/export.go @@ -0,0 +1,133 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package main + +import ( + "compress/gzip" + "errors" + "fmt" + "io" + "os" + "strconv" + "strings" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/fractalplatform/fractal/blockchain" + "github.com/fractalplatform/fractal/ftservice" + "github.com/spf13/cobra" +) + +var exportCommand = &cobra.Command{ + Use: "export -d ", + Short: "Export blockchain to file", + Long: "Export blockchain to file", + Run: func(cmd *cobra.Command, args []string) { + ftCfgInstance.LogCfg.Setup() + if err := exportChain(args); err != nil { + fmt.Println(err) + } + }, +} + +func init() { + RootCmd.AddCommand(exportCommand) + exportCommand.Flags().StringVarP(&ftCfgInstance.NodeCfg.DataDir, "datadir", "d", ftCfgInstance.NodeCfg.DataDir, "Data directory for the databases ") +} + +func exportChain(args []string) error { + if len(args) < 1 { + return errors.New("This command requires an argument") + } + + start := time.Now() + + stack, err := makeNode() + if err != nil { + return err + } + + ctx := stack.GetNodeConfig() + ftsrv, err := ftservice.New(ctx, ftCfgInstance.FtServiceCfg) + if err != nil { + return err + } + + fp := args[0] + if len(args) < 3 { + err = exportBlockChain(ftsrv.BlockChain(), fp) + } else { + first, ferr := strconv.ParseInt(args[1], 10, 64) + last, lerr := strconv.ParseInt(args[2], 10, 64) + if ferr != nil || lerr != nil { + return errors.New("Export error in parsing parameters: block number not an integer") + } + if first < 0 || last < 0 { + return errors.New("Export error: block number must be greater than 0") + } + err = exportAppendBlockChain(ftsrv.BlockChain(), fp, uint64(first), uint64(last)) + } + log.Info("Export done in ", "time", time.Since(start)) + return err +} + +func exportBlockChain(b *blockchain.BlockChain, fn string) error { + log.Info("Exporting blockchain", "file", fn) + // Open the file handle and potentially wrap with a gzip stream + fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) + if err != nil { + return err + } + defer fh.Close() + + var writer io.Writer = fh + if strings.HasSuffix(fn, ".gz") { + writer = gzip.NewWriter(writer) + defer writer.(*gzip.Writer).Close() + } + // Iterate over the blocks and export them + if err := b.Export(writer); err != nil { + return err + } + log.Info("Exported blockchain", "file", fn) + + return nil +} + +// ExportAppendChain exports a blockchain into the specified file, appending to +// the file if data already exists in it. +func exportAppendBlockChain(b *blockchain.BlockChain, fn string, first uint64, last uint64) error { + log.Info("Exporting blockchain", "file", fn) + // Open the file handle and potentially wrap with a gzip stream + fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) + if err != nil { + return err + } + defer fh.Close() + + var writer io.Writer = fh + if strings.HasSuffix(fn, ".gz") { + writer = gzip.NewWriter(writer) + defer writer.(*gzip.Writer).Close() + } + // Iterate over the blocks and export them + if err := b.ExportN(writer, first, last); err != nil { + return err + } + log.Info("Exported blockchain to", "file", fn) + return nil +} diff --git a/cmd/ft/flags.go b/cmd/ft/flags.go index 3d92e850..4acd6876 100644 --- a/cmd/ft/flags.go +++ b/cmd/ft/flags.go @@ -152,14 +152,6 @@ func addFlags(flags *flag.FlagSet) { viper.BindPFlag("node.datadir", flags.Lookup("datadir")) // node - flags.BoolVar( - &ftCfgInstance.NodeCfg.UseLightweightKDF, - "lightkdf", - ftCfgInstance.NodeCfg.UseLightweightKDF, - "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", - ) - viper.BindPFlag("node.lightkdf", flags.Lookup("lightkdf")) - flags.StringVar( &ftCfgInstance.NodeCfg.IPCPath, "ipcpath", diff --git a/cmd/ft/import.go b/cmd/ft/import.go new file mode 100644 index 00000000..22dd88c8 --- /dev/null +++ b/cmd/ft/import.go @@ -0,0 +1,257 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package main + +import ( + "compress/gzip" + "errors" + "fmt" + "io" + "os" + "os/signal" + "runtime" + "strings" + "sync/atomic" + "syscall" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/fractalplatform/fractal/blockchain" + "github.com/fractalplatform/fractal/ftservice" + "github.com/fractalplatform/fractal/types" + ldb "github.com/fractalplatform/fractal/utils/fdb/leveldb" + "github.com/fractalplatform/fractal/utils/rlp" + "github.com/spf13/cobra" + "github.com/syndtr/goleveldb/leveldb/util" +) + +const ( + importBatchSize = 2500 +) + +var importCommand = &cobra.Command{ + Use: "import -d -g ", + Short: "Import a blockchain file", + Long: "Import a blockchain file", + Run: func(cmd *cobra.Command, args []string) { + ftCfgInstance.LogCfg.Setup() + if err := importChain(args); err != nil { + fmt.Println(err) + } + }, +} + +func init() { + RootCmd.AddCommand(importCommand) + importCommand.Flags().StringVarP(&ftCfgInstance.NodeCfg.DataDir, "datadir", "d", ftCfgInstance.NodeCfg.DataDir, "Data directory for the databases ") + importCommand.Flags().StringVarP(&ftCfgInstance.GenesisFile, "genesis", "g", "", "genesis json file") + +} + +func importChain(args []string) error { + if len(args) < 1 { + return errors.New("This command requires an argument") + } + + stack, err := makeNode() + if err != nil { + return err + } + + ctx := stack.GetNodeConfig() + ftsrv, err := ftservice.New(ctx, ftCfgInstance.FtServiceCfg) + if err != nil { + return err + } + + // Start periodically gathering memory profiles + var peakMemAlloc, peakMemSys uint64 + go func() { + stats := new(runtime.MemStats) + for { + runtime.ReadMemStats(stats) + if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc { + atomic.StoreUint64(&peakMemAlloc, stats.Alloc) + } + if atomic.LoadUint64(&peakMemSys) < stats.Sys { + atomic.StoreUint64(&peakMemSys, stats.Sys) + } + time.Sleep(5 * time.Second) + } + }() + + start := time.Now() + + fp := args[0] + if len(args) == 1 { + if err := importBlockchain(ftsrv.BlockChain(), fp); err != nil { + return fmt.Errorf("Import error: %v", err) + } + } else { + for i := 0; i < len(args); i++ { + if err := importBlockchain(ftsrv.BlockChain(), args[i]); err != nil { + return fmt.Errorf("Import error: %v, %v", err, args[i]) + } + } + } + + log.Info("Import done in ", "time", time.Since(start)) + + db := ftsrv.ChainDb().(*ldb.LDBDatabase) + stats, err := db.LDB().GetProperty("leveldb.stats") + if err != nil { + return fmt.Errorf("Failed to read database stats: %v", err) + } + fmt.Println(stats) + + ioStats, err := db.LDB().GetProperty("leveldb.iostats") + if err != nil { + return fmt.Errorf("Failed to read database iostats: %v", err) + } + fmt.Println(ioStats) + + mem := new(runtime.MemStats) + runtime.ReadMemStats(mem) + + fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024) + fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024) + fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) + fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) + + // Compact the entire database to more accurately measure disk io and print the stats + start = time.Now() + fmt.Println("Compacting entire database...") + if err = db.LDB().CompactRange(util.Range{}); err != nil { + return fmt.Errorf("Compaction failed: %v", err) + } + fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) + + stats, err = db.LDB().GetProperty("leveldb.stats") + if err != nil { + return fmt.Errorf("Failed to read database stats: %v", err) + } + fmt.Println(stats) + + ioStats, err = db.LDB().GetProperty("leveldb.iostats") + if err != nil { + return fmt.Errorf("Failed to read database iostats: %v", err) + } + fmt.Println(ioStats) + + return nil +} + +func importBlockchain(chain *blockchain.BlockChain, fn string) error { + // Watch for Ctrl-C while the import is running. + // If a signal is received, the import will stop at the next batch. + interrupt := make(chan os.Signal, 1) + stop := make(chan struct{}) + signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) + defer signal.Stop(interrupt) + defer close(interrupt) + go func() { + if _, ok := <-interrupt; ok { + log.Info("Interrupted during import, stopping at next batch") + } + close(stop) + }() + checkInterrupt := func() bool { + select { + case <-stop: + return true + default: + return false + } + } + + log.Info("Importing blockchain", "file", fn) + + // Open the file handle and potentially unwrap the gzip stream + fh, err := os.Open(fn) + if err != nil { + return err + } + defer fh.Close() + + var reader io.Reader = fh + if strings.HasSuffix(fn, ".gz") { + if reader, err = gzip.NewReader(reader); err != nil { + return err + } + } + stream := rlp.NewStream(reader, 0) + + // Run actual the import. + blocks := make(types.Blocks, importBatchSize) + n := 0 + for batch := 0; ; batch++ { + // Load a batch of RLP blocks. + if checkInterrupt() { + return fmt.Errorf("interrupted") + } + i := 0 + for ; i < importBatchSize; i++ { + var b types.Block + if err := stream.Decode(&b); err == io.EOF { + break + } else if err != nil { + return fmt.Errorf("at block %d: %v", n, err) + } + // don't import first block + if b.NumberU64() == 0 { + i-- + continue + } + blocks[i] = &b + n++ + } + if i == 0 { + break + } + // Import the batch. + if checkInterrupt() { + return fmt.Errorf("interrupted") + } + missing := missingBlocks(chain, blocks[:i]) + if len(missing) == 0 { + log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash()) + continue + } + if _, err := chain.InsertChain(missing); err != nil { + return fmt.Errorf("invalid block %d: %v", n, err) + } + } + return nil +} + +func missingBlocks(chain *blockchain.BlockChain, blocks []*types.Block) []*types.Block { + head := chain.CurrentBlock() + for i, block := range blocks { + // If we're behind the chain head, only check block, state is available at head + if head.NumberU64() > block.NumberU64() { + if !chain.HasBlock(block.Hash(), block.NumberU64()) { + return blocks[i:] + } + continue + } + // If we're above the chain head, state availability is a must + if !chain.HasBlockAndState(block.Hash(), block.NumberU64()) { + return blocks[i:] + } + } + return nil +} diff --git a/cmd/ft/miner.go b/cmd/ft/miner.go new file mode 100644 index 00000000..21b2e20f --- /dev/null +++ b/cmd/ft/miner.go @@ -0,0 +1,145 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package main + +import ( + "bufio" + "io" + "os" + "regexp" + + "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/params" + "github.com/spf13/cobra" + jww "github.com/spf13/jwalterweatherman" +) + +var minerCmd = &cobra.Command{ + Use: "miner", + Short: "control miner start or stop else", + Long: `control miner start or stop else`, + Args: cobra.NoArgs, +} + +var startCmd = &cobra.Command{ + Use: "start", + Short: "start mint new block.", + Long: `start mint new block.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result bool + clientCall(ipcEndpoint, &result, "miner_start") + printJSON(result) + }, +} + +var forceCmd = &cobra.Command{ + Use: "force ", + Short: "force start mint new block.", + Long: `force start mint new block.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result bool + clientCall(ipcEndpoint, &result, "miner_force") + printJSON(result) + }, +} + +var stopCmd = &cobra.Command{ + Use: "stop", + Short: "Stop mint block.", + Long: `Stop mint block.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result bool + clientCall(ipcEndpoint, &result, "miner_stop") + printJSON(result) + }, +} + +var miningCmd = &cobra.Command{ + Use: "mining", + Short: "Return the miner is mining", + Long: `Return the miner is mining.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result bool + clientCall(ipcEndpoint, &result, "miner_mining") + printJSON(result) + }, +} + +var setCoinbaseCmd = &cobra.Command{ + Use: "setcoinbase ", + Short: "Set the coinbase of the miner.", + Long: `Set the coinbase of the miner.`, + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + name := common.Name(args[0]) + if !name.IsValid(regexp.MustCompile("^([a-z][a-z0-9]{6,15})(?:\\.([a-z0-9]{1,8})){0,1}$")) { + jww.ERROR.Println("valid name: " + name) + return + } + path := args[1] + _, err := os.Stat(path) + if os.IsNotExist(err) { + jww.ERROR.Println("file is not exist.", "path", path) + return + } + + fi, err := os.Open(path) + if err != nil { + jww.ERROR.Println("read failed.", "path", path, "err", err) + return + } + defer fi.Close() + + var keys []string + br := bufio.NewReader(fi) + for { + line, _, c := br.ReadLine() + if c == io.EOF { + break + } + keys = append(keys, string(line)) + } + + if len(keys) == 0 { + jww.ERROR.Println("keys is empty ", "path", path) + return + } + clientCall(ipcEndpoint, nil, "miner_setCoinbase", name, keys) + printJSON(true) + }, +} + +var setExtraCmd = &cobra.Command{ + Use: "setextra ", + Short: "Set the extra of the miner.", + Long: `Set the extra of the miner.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + clientCall(ipcEndpoint, nil, "miner_setExtra", args[0]) + printJSON(true) + }, +} + +func init() { + RootCmd.AddCommand(minerCmd) + minerCmd.AddCommand(startCmd, forceCmd, stopCmd, miningCmd, setCoinbaseCmd, setExtraCmd) + minerCmd.PersistentFlags().StringVarP(&ipcEndpoint, "ipcpath", "i", defaultIPCEndpoint(params.ClientIdentifier), "IPC Endpoint path") +} diff --git a/cmd/ft/p2p.go b/cmd/ft/p2p.go new file mode 100644 index 00000000..ab773ba5 --- /dev/null +++ b/cmd/ft/p2p.go @@ -0,0 +1,168 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package main + +import ( + "github.com/fractalplatform/fractal/params" + "github.com/spf13/cobra" +) + +var p2pCmd = &cobra.Command{ + Use: "p2p", + Short: "Offers and API for p2p networking", + Long: `Offers and API for p2p networking`, + Args: cobra.NoArgs, +} + +var addCmd = &cobra.Command{ + Use: "add ", + Short: "Connecting to a remote node.", + Long: `Connecting to a remote node.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + var result bool + clientCall(ipcEndpoint, &result, "p2p_addPeer", args[0]) + printJSON(result) + }, +} + +var removeCmd = &cobra.Command{ + Use: "remove ", + Short: "Disconnects from a remote node if the connection exists.", + Long: `Disconnects from a remote node if the connection exists.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + var result bool + clientCall(ipcEndpoint, &result, "p2p_removePeer", args[0]) + printJSON(result) + }, +} + +var addtrustedCmd = &cobra.Command{ + Use: "addtrusted ", + Short: "Allows a remote node to always connect, even if slots are full.", + Long: `Allows a remote node to always connect, even if slots are full.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + var result bool + clientCall(ipcEndpoint, &result, "p2p_addTrustedPeer", args[0]) + printJSON(result) + }, +} + +var removetrustedCmd = &cobra.Command{ + Use: "removetrusted ", + Short: "Removes a remote node from the trusted peer set, but it does not disconnect it automatically.", + Long: `Removes a remote node from the trusted peer set, but it does not disconnect it automatically.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + var result bool + clientCall(ipcEndpoint, &result, "p2p_removeTrustedPeer", args[0]) + printJSON(result) + }, +} + +var addbadCmd = &cobra.Command{ + Use: "addbad ", + Short: "Add a bad node in black list.", + Long: `Add a bad node in black list..`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + var result bool + clientCall(ipcEndpoint, &result, "p2p_addBadNode", args[0]) + printJSON(result) + }, +} + +var removebadCmd = &cobra.Command{ + Use: "removebad ", + Short: "Removes a bad node from the black peer set.", + Long: `Removes a bad node from the black peer set.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + var result bool + clientCall(ipcEndpoint, &result, "p2p_removeBadNode", args[0]) + printJSON(result) + }, +} + +var countCmd = &cobra.Command{ + Use: "count", + Short: "Return number of connected peers.", + Long: `Return number of connected peers.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result int + clientCall(ipcEndpoint, &result, "p2p_peerCount") + printJSON(result) + }, +} + +var listCmd = &cobra.Command{ + Use: "list", + Short: "Return connected peers list.", + Long: `Return connected peers list.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result []string + clientCall(ipcEndpoint, &result, "p2p_peers") + printJSONList(result) + }, +} + +var badcountCmd = &cobra.Command{ + Use: "badcount", + Short: "Return number of bad nodes .", + Long: `Return number of bad nodes .`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result int + clientCall(ipcEndpoint, &result, "p2p_badNodesCount") + printJSON(result) + }, +} + +var badlistCmd = &cobra.Command{ + Use: "badlist", + Short: "Return bad nodes list.", + Long: `Return bad nodes list.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result []string + clientCall(ipcEndpoint, &result, "p2p_badNodes") + printJSONList(result) + }, +} + +var selfnodeCmd = &cobra.Command{ + Use: "selfnode", + Short: "Return self enode url.", + Long: `Return self enode url.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + var result string + clientCall(ipcEndpoint, &result, "p2p_selfNode") + printJSON(result) + }, +} + +func init() { + RootCmd.AddCommand(p2pCmd) + p2pCmd.AddCommand(addCmd, removeCmd, addtrustedCmd, removetrustedCmd, + addbadCmd, removebadCmd, countCmd, listCmd, badcountCmd, badlistCmd, selfnodeCmd) + p2pCmd.PersistentFlags().StringVarP(&ipcEndpoint, "ipcpath", "i", defaultIPCEndpoint(params.ClientIdentifier), "IPC Endpoint path") +} diff --git a/cmd/ft/root.go b/cmd/ft/root.go index 646aed95..ec17386f 100644 --- a/cmd/ft/root.go +++ b/cmd/ft/root.go @@ -36,6 +36,11 @@ import ( "github.com/spf13/viper" ) +var ( + errNoConfigFile string + errViperReadConfig error +) + // RootCmd represents the base command when called without any subcommands var RootCmd = &cobra.Command{ Use: "ft", @@ -49,6 +54,14 @@ var RootCmd = &cobra.Command{ err = viperUmarshalConfig() } ftCfgInstance.LogCfg.Setup() + if errNoConfigFile != "" { + log.Info(errNoConfigFile) + } + + if errViperReadConfig != nil { + log.Error("Can't read config, use default configuration", "err", errViperReadConfig) + } + if err != nil { log.Error("viper umarshal config file faild", "err", err) } @@ -126,7 +139,6 @@ func SetupMetrics() { // start up the node itself func startNode(stack *node.Node) error { debug.Memsize.Add("node", stack) - if err := stack.Start(); err != nil { return err } @@ -159,11 +171,11 @@ func initConfig() { if ConfigFile != "" { viper.SetConfigFile(ConfigFile) } else { - fmt.Println("No config file , use default configuration.") + errNoConfigFile = "No config file , use default configuration." return } if err := viper.ReadInConfig(); err != nil { - fmt.Printf("Can't read config: %v, use default configuration.", err) + errViperReadConfig = err } } diff --git a/cmd/ft/txpool.go b/cmd/ft/txpool.go new file mode 100644 index 00000000..d28bfae8 --- /dev/null +++ b/cmd/ft/txpool.go @@ -0,0 +1,71 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package main + +import ( + "github.com/fractalplatform/fractal/params" + "github.com/fractalplatform/fractal/types" + "github.com/spf13/cobra" +) + +var txpoolCommand = &cobra.Command{ + Use: "txpool", + Short: "Query txpool state and change txpool the Minimum gas price", + Long: "Query txpool state and change txpool the Minimum gas price", +} + +var contentCmd = &cobra.Command{ + Use: "content ", + Short: "Returns the transactions contained within the transaction pool.", + Long: `Returns the transactions contained within the transaction pool.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + result := map[string]map[string]map[string]*types.RPCTransaction{} + clientCall(ipcEndpoint, &result, "txpool_content") + printJSON(result) + }, +} + +var statusCmd = &cobra.Command{ + Use: "status ", + Short: "returns the number of pending and queued transaction in the pool.", + Long: `returns the number of pending and queued transaction in the pool.`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + result := map[string]int{} + clientCall(ipcEndpoint, &result, "txpool_status") + printJSON(result) + }, +} + +var setGasPriceCmd = &cobra.Command{ + Use: "setgasprice ", + Short: "set txpool the Minimum gas price ", + Long: `set txpool the Minimum gas price `, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + var result bool + clientCall(ipcEndpoint, &result, "txpool_setGasPrice", parseUint64(args[0])) + printJSON(result) + }, +} + +func init() { + RootCmd.AddCommand(txpoolCommand) + txpoolCommand.AddCommand(contentCmd, statusCmd, setGasPriceCmd) + txpoolCommand.PersistentFlags().StringVarP(&ipcEndpoint, "ipcpath", "i", defaultIPCEndpoint(params.ClientIdentifier), "IPC Endpoint path") +} diff --git a/cmd/ft/utils.go b/cmd/ft/utils.go index 0c5370a9..885da23a 100644 --- a/cmd/ft/utils.go +++ b/cmd/ft/utils.go @@ -17,17 +17,24 @@ package main import ( + "encoding/json" "fmt" "os" "os/user" "path/filepath" "reflect" "runtime" + "strconv" + "strings" "unicode" "github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/log" + "github.com/fractalplatform/fractal/node" + "github.com/fractalplatform/fractal/params" + "github.com/fractalplatform/fractal/rpc" "github.com/naoina/toml" + jww "github.com/spf13/jwalterweatherman" ) // defaultDataDir is the default data directory to use for the databases and other @@ -92,3 +99,71 @@ var tomlSettings = toml.Config{ return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link) }, } + +func clientCall(endpoint string, result interface{}, method string, args ...interface{}) { + client, err := dialRPC(ipcEndpoint) + if err != nil { + jww.ERROR.Println(err) + os.Exit(-1) + } + if err := client.Call(result, method, args...); err != nil { + jww.ERROR.Println(err) + os.Exit(-1) + } +} + +// dialRPC returns a RPC client which connects to the given endpoint. +func dialRPC(endpoint string) (*rpc.Client, error) { + if endpoint == "" { + endpoint = defaultIPCEndpoint(params.ClientIdentifier) + } + return rpc.Dial(endpoint) +} + +// DefaultIPCEndpoint returns the IPC path used by default. +func defaultIPCEndpoint(clientIdentifier string) string { + if clientIdentifier == "" { + clientIdentifier = strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe") + if clientIdentifier == "" { + panic("empty executable name") + } + } + config := &node.Config{DataDir: defaultDataDir(), IPCPath: clientIdentifier + ".ipc"} + return config.IPCEndpoint() +} + +func printJSON(data interface{}) { + rawData, err := json.MarshalIndent(data, "", " ") + if err != nil { + jww.ERROR.Println(err) + os.Exit(1) + } + jww.FEEDBACK.Println(string(rawData)) +} + +func printJSONList(data interface{}) { + value := reflect.ValueOf(data) + if value.Kind() != reflect.Slice { + jww.ERROR.Printf("invalid type %v assertion", value.Kind()) + os.Exit(1) + } + + for idx := 0; idx < value.Len(); idx++ { + jww.FEEDBACK.Println(idx, ":") + rawData, err := json.MarshalIndent(value.Index(idx).Interface(), "", " ") + if err != nil { + jww.ERROR.Println(err) + os.Exit(1) + } + jww.FEEDBACK.Println(string(rawData)) + } +} + +func parseUint64(arg string) uint64 { + num, err := strconv.ParseUint(arg, 10, 64) + if err != nil { + jww.ERROR.Printf("Invalid fulltx value: %v err: %v", arg, err) + os.Exit(1) + } + return num +} diff --git a/common/author_test.go b/common/author_test.go new file mode 100644 index 00000000..2963a544 --- /dev/null +++ b/common/author_test.go @@ -0,0 +1,70 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package common + +import ( + "bytes" + "encoding/json" + "testing" + + "github.com/fractalplatform/fractal/utils/rlp" + "github.com/stretchr/testify/assert" +) + +func TestAuthorEncodeAndDecodeRLP(t *testing.T) { + var tests = []struct { + inputAuthor *Author + }{ + {&Author{Owner: Name("test"), Weight: 1}}, + {&Author{Owner: HexToPubKey("test"), Weight: 1}}, + {&Author{Owner: HexToAddress("test"), Weight: 1}}, + } + for _, test := range tests { + authorBytes, err := rlp.EncodeToBytes(test.inputAuthor) + if err != nil { + t.Fatal(err) + } + outputAuthor := &Author{} + if err := rlp.Decode(bytes.NewReader(authorBytes), outputAuthor); err != nil { + t.Fatal(err) + } + assert.Equal(t, test.inputAuthor, outputAuthor) + } +} + +func TestAuthorMarshalAndUnMarshal(t *testing.T) { + var tests = []struct { + inputAuthor *Author + }{ + {&Author{Owner: Name("test"), Weight: 1}}, + {&Author{Owner: HexToPubKey("test"), Weight: 1}}, + {&Author{Owner: HexToAddress("test"), Weight: 1}}, + } + for _, test := range tests { + authorBytes, err := json.Marshal(test.inputAuthor) + if err != nil { + t.Fatal(err) + } + outputAuthor := &Author{} + if err := json.Unmarshal(authorBytes, outputAuthor); err != nil { + t.Fatal(err) + } + if test.inputAuthor.Owner.String() != outputAuthor.Owner.String() { + t.Fatal("not equal") + } + } +} diff --git a/common/prque/example_test.go b/common/prque/example_test.go new file mode 100644 index 00000000..27aa1440 --- /dev/null +++ b/common/prque/example_test.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +package prque_test + +import ( + "fmt" + + "github.com/fractalplatform/fractal/common/prque" +) + +// Insert some data into a priority queue and pop them out in prioritized order. +func Example_usage() { + // Define some data to push into the priority queue + prio := []int64{77, 22, 44, 55, 11, 88, 33, 99, 0, 66} + data := []string{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"} + + // Create the priority queue and insert the prioritized data + pq := prque.New(nil) + for i := 0; i < len(data); i++ { + pq.Push(data[i], prio[i]) + } + // Pop out the data and print them + for !pq.Empty() { + val, prio := pq.Pop() + fmt.Printf("%d:%s ", prio, val) + } + // Output: + // 99:seven 88:five 77:zero 66:nine 55:three 44:two 33:six 22:one 11:four 0:eight +} diff --git a/common/prque/prque_test.go b/common/prque/prque_test.go new file mode 100644 index 00000000..ce50bca9 --- /dev/null +++ b/common/prque/prque_test.go @@ -0,0 +1,109 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package prque + +import ( + "math/rand" + "testing" +) + +func TestPrque(t *testing.T) { + // Generate a batch of random data and a specific priority order + size := 16 * blockSize + prio := rand.Perm(size) + data := make([]int, size) + for i := 0; i < size; i++ { + data[i] = rand.Int() + } + queue := New(nil) + for rep := 0; rep < 2; rep++ { + // Fill a priority queue with the above data + for i := 0; i < size; i++ { + queue.Push(data[i], int64(prio[i])) + if queue.Size() != i+1 { + t.Errorf("queue size mismatch: have %v, want %v.", queue.Size(), i+1) + } + } + // Create a map the values to the priorities for easier verification + dict := make(map[int64]int) + for i := 0; i < size; i++ { + dict[int64(prio[i])] = data[i] + } + // Pop out the elements in priority order and verify them + prevPrio := int64(size + 1) + for !queue.Empty() { + val, prio := queue.Pop() + if prio > prevPrio { + t.Errorf("invalid priority order: %v after %v.", prio, prevPrio) + } + prevPrio = prio + if val != dict[prio] { + t.Errorf("push/pop mismatch: have %v, want %v.", val, dict[prio]) + } + delete(dict, prio) + } + } +} + +func TestReset(t *testing.T) { + // Fill the queue with some random data + size := 16 * blockSize + queue := New(nil) + for i := 0; i < size; i++ { + queue.Push(rand.Int(), rand.Int63()) + } + // Reset and ensure it's empty + queue.Reset() + if !queue.Empty() { + t.Errorf("priority queue not empty after reset: %v", queue) + } +} + +func BenchmarkPush(b *testing.B) { + // Create some initial data + data := make([]int, b.N) + prio := make([]int64, b.N) + for i := 0; i < len(data); i++ { + data[i] = rand.Int() + prio[i] = rand.Int63() + } + // Execute the benchmark + b.ResetTimer() + queue := New(nil) + for i := 0; i < len(data); i++ { + queue.Push(data[i], prio[i]) + } +} + +func BenchmarkPop(b *testing.B) { + // Create some initial data + data := make([]int, b.N) + prio := make([]int64, b.N) + for i := 0; i < len(data); i++ { + data[i] = rand.Int() + prio[i] = rand.Int63() + } + queue := New(nil) + for i := 0; i < len(data); i++ { + queue.Push(data[i], prio[i]) + } + // Execute the benchmark + b.ResetTimer() + for !queue.Empty() { + queue.Pop() + } +} diff --git a/common/prque/sstack_test.go b/common/prque/sstack_test.go new file mode 100644 index 00000000..7fea45e0 --- /dev/null +++ b/common/prque/sstack_test.go @@ -0,0 +1,92 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package prque + +import ( + "math/rand" + "sort" + "testing" +) + +func TestSstack(t *testing.T) { + // Create some initial data + size := 16 * blockSize + data := make([]*item, size) + for i := 0; i < size; i++ { + data[i] = &item{rand.Int(), rand.Int63()} + } + stack := newSstack(nil) + for rep := 0; rep < 2; rep++ { + // Push all the data into the stack, pop out every second + secs := []*item{} + for i := 0; i < size; i++ { + stack.Push(data[i]) + if i%2 == 0 { + secs = append(secs, stack.Pop().(*item)) + } + } + rest := []*item{} + for stack.Len() > 0 { + rest = append(rest, stack.Pop().(*item)) + } + // Make sure the contents of the resulting slices are ok + for i := 0; i < size; i++ { + if i%2 == 0 && data[i] != secs[i/2] { + t.Errorf("push/pop mismatch: have %v, want %v.", secs[i/2], data[i]) + } + if i%2 == 1 && data[i] != rest[len(rest)-i/2-1] { + t.Errorf("push/pop mismatch: have %v, want %v.", rest[len(rest)-i/2-1], data[i]) + } + } + } +} + +func TestSstackSort(t *testing.T) { + // Create some initial data + size := 16 * blockSize + data := make([]*item, size) + for i := 0; i < size; i++ { + data[i] = &item{rand.Int(), int64(i)} + } + // Push all the data into the stack + stack := newSstack(nil) + for _, val := range data { + stack.Push(val) + } + // Sort and pop the stack contents (should reverse the order) + sort.Sort(stack) + for _, val := range data { + out := stack.Pop() + if out != val { + t.Errorf("push/pop mismatch after sort: have %v, want %v.", out, val) + } + } +} + +func TestSstackReset(t *testing.T) { + // Push some stuff onto the stack + size := 16 * blockSize + stack := newSstack(nil) + for i := 0; i < size; i++ { + stack.Push(&item{i, int64(i)}) + } + // Clear and verify + stack.Reset() + if stack.Len() != 0 { + t.Errorf("stack not empty after reset: %v", stack) + } +} diff --git a/consensus/consensus.go b/consensus/consensus.go index 0e19a5a2..d6773c76 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -77,7 +77,7 @@ type IChainReader interface { // IEngine is an algorithm agnostic consensus engine. type IEngine interface { - // Author retrieves the address of the account that minted the given block + // Author retrieves the name of the account that minted the given block Author(header *types.Header) (common.Name, error) // Prepare initializes the consensus fields of a block header according to the rules of a particular engine. The changes are executed inline. @@ -98,7 +98,7 @@ type IEngine interface { Engine() IEngine - ProcessAction(height uint64, chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) + ProcessAction(number uint64, chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) GetDelegatedByTime(state *state.StateDB, candidate string, timestamp uint64) (stake *big.Int, err error) diff --git a/consensus/dpos/api.go b/consensus/dpos/api.go index 8a16eb4c..411d1943 100644 --- a/consensus/dpos/api.go +++ b/consensus/dpos/api.go @@ -32,8 +32,8 @@ type API struct { } // Info get dpos info -func (api *API) Info() (interface{}, error) { - return api.dpos.config, nil +func (api *API) Info() interface{} { + return api.dpos.config } // IrreversibleRet result @@ -44,12 +44,12 @@ type IrreversibleRet struct { } // Irreversible get irreversible info -func (api *API) Irreversible() (interface{}, error) { +func (api *API) Irreversible() interface{} { ret := &IrreversibleRet{} ret.Reversible = api.chain.CurrentHeader().Number.Uint64() ret.ProposedIrreversible = api.dpos.CalcProposedIrreversible(api.chain, nil, false) ret.BftIrreversible = api.dpos.CalcBFTIrreversible() - return ret, nil + return ret } // Candidate get candidate info of dpos @@ -80,7 +80,7 @@ func (api *API) Candidates(detail bool) (interface{}, error) { return candidates, nil } - names := []string{} + names := make([]string, 0, len(candidates)) for _, candidate := range candidates { names = append(names, candidate.Name) } @@ -89,17 +89,17 @@ func (api *API) Candidates(detail bool) (interface{}, error) { // VotersByCandidate get voters info of candidate func (api *API) VotersByCandidate(candidate string, detail bool) (interface{}, error) { - height := api.chain.CurrentHeader().Number.Uint64() - return api.VotersByCandidateByHeight(height, candidate, detail) + number := api.chain.CurrentHeader().Number.Uint64() + return api.VotersByCandidateByNumber(number, candidate, detail) } -// VotersByCandidateByHeight get voters info of candidate -func (api *API) VotersByCandidateByHeight(height uint64, candidate string, detail bool) (interface{}, error) { +// VotersByCandidateByNumber get voters info of candidate +func (api *API) VotersByCandidateByNumber(number uint64, candidate string, detail bool) (interface{}, error) { sys, err := api.system() if err != nil { return nil, err } - epcho, err := api.epcho(height) + epcho, err := api.epcho(number) if err != nil { return nil, err } @@ -111,7 +111,7 @@ func (api *API) VotersByCandidateByHeight(height uint64, candidate string, detai return voters, nil } - names := []string{} + names := make([]string, 0, len(voters)) for _, voter := range voters { names = append(names, voter.Name) } @@ -120,17 +120,17 @@ func (api *API) VotersByCandidateByHeight(height uint64, candidate string, detai // VotersByVoter get voters info of voter func (api *API) VotersByVoter(voter string, detail bool) (interface{}, error) { - height := api.chain.CurrentHeader().Number.Uint64() - return api.VotersByVoterByHeight(height, voter, detail) + number := api.chain.CurrentHeader().Number.Uint64() + return api.VotersByVoterByNumber(number, voter, detail) } -// VotersByVoterByHeight get voters info of voter -func (api *API) VotersByVoterByHeight(height uint64, voter string, detail bool) (interface{}, error) { +// VotersByVoterByNumber get voters info of voter +func (api *API) VotersByVoterByNumber(number uint64, voter string, detail bool) (interface{}, error) { sys, err := api.system() if err != nil { return nil, err } - epcho, err := api.epcho(height) + epcho, err := api.epcho(number) if err != nil { return nil, err } @@ -151,17 +151,17 @@ func (api *API) VotersByVoterByHeight(height uint64, voter string, detail bool) // AvailableStake get available stake func (api *API) AvailableStake(voter string) (*big.Int, error) { - height := api.chain.CurrentHeader().Number.Uint64() - return api.AvailableStakeByHeight(height, voter) + number := api.chain.CurrentHeader().Number.Uint64() + return api.AvailableStakeByNumber(number, voter) } -// AvailableStakeByHeight get available stake -func (api *API) AvailableStakeByHeight(height uint64, voter string) (*big.Int, error) { +// AvailableStakeByNumber get available stake +func (api *API) AvailableStakeByNumber(number uint64, voter string) (*big.Int, error) { sys, err := api.system() if err != nil { return nil, err } - epcho, err := api.epcho(height) + epcho, err := api.epcho(number) if err != nil { return nil, err } @@ -174,13 +174,13 @@ func (api *API) AvailableStakeByHeight(height uint64, voter string) (*big.Int, e // ValidCandidates current valid candidates func (api *API) ValidCandidates() (interface{}, error) { - height := api.chain.CurrentHeader().Number.Uint64() - return api.ValidCandidatesByHeight(height) + number := api.chain.CurrentHeader().Number.Uint64() + return api.ValidCandidatesByNumber(number) } -// ValidCandidatesByHeight valid candidates -func (api *API) ValidCandidatesByHeight(height uint64) (interface{}, error) { - epcho, err := api.epcho(height) +// ValidCandidatesByNumber valid candidates +func (api *API) ValidCandidatesByNumber(number uint64) (interface{}, error) { + epcho, err := api.epcho(number) if err != nil { return nil, err } @@ -197,13 +197,13 @@ func (api *API) ValidCandidatesByHeight(height uint64) (interface{}, error) { // NextValidCandidates current valid candidates func (api *API) NextValidCandidates() (interface{}, error) { - height := api.chain.CurrentHeader().Number.Uint64() - return api.NextValidCandidatesByHeight(height) + number := api.chain.CurrentHeader().Number.Uint64() + return api.NextValidCandidatesByNumber(number) } -// NextValidCandidatesByHeight current valid candidates -func (api *API) NextValidCandidatesByHeight(height uint64) (interface{}, error) { - epcho, err := api.epcho(height) +// NextValidCandidatesByNumber current valid candidates +func (api *API) NextValidCandidatesByNumber(number uint64) (interface{}, error) { + epcho, err := api.epcho(number) if err != nil { return nil, err } @@ -216,13 +216,13 @@ func (api *API) NextValidCandidatesByHeight(height uint64) (interface{}, error) // SnapShotTime get snapshort func (api *API) SnapShotTime() (interface{}, error) { - height := api.chain.CurrentHeader().Number.Uint64() - return api.SnapShotTimeByHeight(height) + number := api.chain.CurrentHeader().Number.Uint64() + return api.SnapShotTimeByNumber(number) } -// SnapShotTimeByHeight get snapshort by height -func (api *API) SnapShotTimeByHeight(height uint64) (interface{}, error) { - epcho, err := api.epcho(height) +// SnapShotTimeByNumber get snapshort by number +func (api *API) SnapShotTimeByNumber(number uint64) (interface{}, error) { + epcho, err := api.epcho(number) if err != nil { return nil, err } @@ -244,10 +244,10 @@ func (api *API) SnapShotTimeByHeight(height uint64) (interface{}, error) { return res, nil } -func (api *API) epcho(height uint64) (uint64, error) { - header := api.chain.GetHeaderByNumber(height) +func (api *API) epcho(number uint64) (uint64, error) { + header := api.chain.GetHeaderByNumber(number) if header == nil { - return 0, fmt.Errorf("not found height %v", height) + return 0, fmt.Errorf("not found number %v", number) } timestamp := header.Time.Uint64() return api.dpos.config.epoch(timestamp), nil diff --git a/consensus/dpos/database.go b/consensus/dpos/database.go index 6cbc1ba4..eed3ee70 100644 --- a/consensus/dpos/database.go +++ b/consensus/dpos/database.go @@ -120,7 +120,7 @@ type CandidateInfo struct { URL string `json:"url"` // candidate url Quantity *big.Int `json:"quantity"` // candidate stake quantity TotalQuantity *big.Int `json:"totalQuantity"` // candidate total stake quantity - Height uint64 `json:"height"` // timestamp + Number uint64 `json:"number"` // timestamp Counter uint64 `json:"counter"` ActualCounter uint64 `json:"actualCounter"` Type CandidateType `json:"type"` @@ -138,7 +138,7 @@ type VoterInfo struct { Name string `json:"name"` // voter name Candidate string `json:"candidate"` // candidate approved by this voter Quantity *big.Int `json:"quantity"` // stake approved by this voter - Height uint64 `json:"height"` // timestamp + Number uint64 `json:"number"` // timestamp NextKeyForVoter string `json:"-"` NextKeyForCandidate string `json:"-"` } @@ -162,11 +162,11 @@ type GlobalState struct { ActivatedCandidateSchedule []string `json:"activatedCandidateSchedule"` // candidates ActivatedTotalQuantity *big.Int `json:"activatedTotalQuantity"` // the sum of activate candidate votes OffCandidateSchedule []uint64 `json:"offCandidateSchedule"` // activated backup candidates - OffCandidateHeight []uint64 `json:"offCandidateHeight"` // activated backup candidates + OffCandidateNumber []uint64 `json:"offCandidateNumber"` // activated backup candidates TotalQuantity *big.Int `json:"totalQuantity"` // the sum of all candidate votes TakeOver bool `json:"takeOver"` // systemio take over dpos Dpos bool `json:"dpos"` // dpos status - Height uint64 `json:"height"` // timestamp + Number uint64 `json:"number"` // timestamp } // CandidateInfoArray array of candidate @@ -178,10 +178,10 @@ func (prods CandidateInfoArray) Len() int { func (prods CandidateInfoArray) Less(i, j int) bool { val := prods[i].TotalQuantity.Cmp(prods[j].TotalQuantity) if val == 0 { - if prods[i].Height == prods[j].Height { + if prods[i].Number == prods[j].Number { return strings.Compare(prods[i].Name, prods[j].Name) > 0 } - return prods[i].Height < prods[j].Height + return prods[i].Number < prods[j].Number } return val > 0 } diff --git a/consensus/dpos/dpos.go b/consensus/dpos/dpos.go index 0ac91209..c20226be 100644 --- a/consensus/dpos/dpos.go +++ b/consensus/dpos/dpos.go @@ -105,14 +105,14 @@ func (s *stateDB) GetBalanceByTime(name string, timestamp uint64) (*big.Int, err } // Genesis dpos genesis store -func Genesis(cfg *Config, state *state.StateDB, timestamp uint64, height uint64) error { +func Genesis(cfg *Config, state *state.StateDB, timestamp uint64, number uint64) error { sys := NewSystem(state, cfg) if err := sys.SetCandidate(&CandidateInfo{ Name: cfg.SystemName, URL: cfg.SystemURL, Quantity: big.NewInt(0), TotalQuantity: big.NewInt(0), - Height: height, + Number: number, }); err != nil { return err } @@ -126,9 +126,9 @@ func Genesis(cfg *Config, state *state.StateDB, timestamp uint64, height uint64) PreEpcho: epcho, ActivatedTotalQuantity: big.NewInt(0), TotalQuantity: big.NewInt(0), - OffCandidateHeight: []uint64{}, + OffCandidateNumber: []uint64{}, OffCandidateSchedule: []uint64{}, - Height: height, + Number: number, }); err != nil { return err } @@ -199,7 +199,7 @@ func (dpos *Dpos) Prepare(chain consensus.IChainReader, header *types.Header, tx n := timestamp % dpos.config.mepochInterval() return m + n } - log.Debug("UpdateElectedCandidates", "prev", pepcho, "curr", epcho, "height", parent.Number.Uint64(), "time", parent.Time.Uint64()) + log.Debug("UpdateElectedCandidates", "prev", pepcho, "curr", epcho, "number", parent.Number.Uint64(), "time", parent.Time.Uint64()) sys.UpdateElectedCandidates(pepcho, epcho, parent.Number.Uint64(), counter) } return nil @@ -243,8 +243,8 @@ func (dpos *Dpos) Finalize(chain consensus.IChainReader, header *types.Header, t blk := types.NewBlock(header, txs, receipts) - // first hard fork at a specific height - // If the block height is greater than or equal to the hard forking height, + // first hard fork at a specific number + // If the block number is greater than or equal to the hard forking number, // the fork function will take effect. This function is valid only in the test network. if err := chain.ForkUpdate(blk, state); err != nil { return nil, err @@ -328,7 +328,7 @@ func (dpos *Dpos) VerifySeal(chain consensus.IChainReader, header *types.Header) // CalcDifficulty is the difficulty adjustment algorithm. // It returns the difficulty that a new block should have when created at time given the parent block's time and difficulty. func (dpos *Dpos) CalcDifficulty(chain consensus.IChainReader, time uint64, parent *types.Header) *big.Int { - // return the current height as difficulty + // return the current number as difficulty if timeOfGenesisBlock == 0 { if genesisBlock := chain.GetHeaderByNumber(0); genesisBlock != nil { timeOfGenesisBlock = genesisBlock.Time.Int64() diff --git a/consensus/dpos/ldb_test.go b/consensus/dpos/ldb_test.go index 897823d1..3c8e3652 100644 --- a/consensus/dpos/ldb_test.go +++ b/consensus/dpos/ldb_test.go @@ -243,7 +243,7 @@ func TestLDBVoter(t *testing.T) { Name: voter, Candidate: candidates[0], Quantity: big.NewInt(int64(index)), - Height: uint64(index), + Number: uint64(index), } if err := db.SetAvailableQuantity(epcho, voter, big.NewInt(int64(index))); err != nil { panic(fmt.Errorf("SetAvailableQuantity --- %v", err)) @@ -275,7 +275,7 @@ func TestLDBVoter(t *testing.T) { Name: voters[0], Candidate: candidate, Quantity: big.NewInt(int64(index)), - Height: uint64(index), + Number: uint64(index), } if err := db.SetAvailableQuantity(epcho, voters[0], big.NewInt(int64(index))); err != nil { panic(fmt.Errorf("SetAvailableQuantity --- %v", err)) @@ -307,7 +307,7 @@ func TestLDBVoter(t *testing.T) { Name: voters[index], Candidate: candidate, Quantity: big.NewInt(int64(index)), - Height: uint64(index), + Number: uint64(index), } if err := db.SetAvailableQuantity(epcho, voters[index], big.NewInt(int64(index))); err != nil { panic(fmt.Errorf("SetAvailableQuantity --- %v", err)) @@ -347,7 +347,7 @@ func TestLDBGlobalState(t *testing.T) { PreEpcho: uint64(index), ActivatedTotalQuantity: big.NewInt(0), ActivatedCandidateSchedule: candidates[index:], - OffCandidateHeight: []uint64{}, + OffCandidateNumber: []uint64{}, OffCandidateSchedule: []uint64{}, TotalQuantity: big.NewInt(0), } @@ -375,7 +375,7 @@ func TestLDBGlobalState(t *testing.T) { ActivatedTotalQuantity: big.NewInt(0), ActivatedCandidateSchedule: candidates[index:], TotalQuantity: big.NewInt(0), - OffCandidateHeight: []uint64{}, + OffCandidateNumber: []uint64{}, OffCandidateSchedule: []uint64{}, } if err := db.SetState(gstate); err != nil { diff --git a/consensus/dpos/processor.go b/consensus/dpos/processor.go index fb26da99..6c4d8c74 100644 --- a/consensus/dpos/processor.go +++ b/consensus/dpos/processor.go @@ -51,17 +51,17 @@ type KickedCandidate struct { } // ProcessAction exec action -func (dpos *Dpos) ProcessAction(height uint64, chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) { +func (dpos *Dpos) ProcessAction(number uint64, chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) { snap := state.Snapshot() - internalLogs, err := dpos.processAction(height, chainCfg, state, action) + internalLogs, err := dpos.processAction(number, chainCfg, state, action) if err != nil { state.RevertToSnapshot(snap) } return internalLogs, err } -func (dpos *Dpos) processAction(height uint64, chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) { - if err := action.CheckValid(chainCfg); err != nil { +func (dpos *Dpos) processAction(number uint64, chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) { + if err := action.Check(chainCfg); err != nil { return nil, err } sys := NewSystem(state, dpos.config) @@ -85,7 +85,7 @@ func (dpos *Dpos) processAction(height uint64, chainCfg *params.ChainConfig, sta if err := rlp.DecodeBytes(action.Data(), &arg); err != nil { return nil, err } - if err := sys.RegCandidate(epcho, action.Sender().String(), arg.URL, action.Value(), height); err != nil { + if err := sys.RegCandidate(epcho, action.Sender().String(), arg.URL, action.Value(), number); err != nil { return nil, err } case types.UpdateCandidate: @@ -93,16 +93,16 @@ func (dpos *Dpos) processAction(height uint64, chainCfg *params.ChainConfig, sta if err := rlp.DecodeBytes(action.Data(), &arg); err != nil { return nil, err } - if err := sys.UpdateCandidate(epcho, action.Sender().String(), arg.URL, action.Value(), height); err != nil { + if err := sys.UpdateCandidate(epcho, action.Sender().String(), arg.URL, action.Value(), number); err != nil { return nil, err } case types.UnregCandidate: - err := sys.UnregCandidate(epcho, action.Sender().String(), height) + err := sys.UnregCandidate(epcho, action.Sender().String(), number) if err != nil { return nil, err } case types.RefundCandidate: - err := sys.RefundCandidate(epcho, action.Sender().String(), height) + err := sys.RefundCandidate(epcho, action.Sender().String(), number) if err != nil { return nil, err } @@ -111,7 +111,7 @@ func (dpos *Dpos) processAction(height uint64, chainCfg *params.ChainConfig, sta if err := rlp.DecodeBytes(action.Data(), &arg); err != nil { return nil, err } - if err := sys.VoteCandidate(epcho, action.Sender().String(), arg.Candidate, arg.Stake, height); err != nil { + if err := sys.VoteCandidate(epcho, action.Sender().String(), arg.Candidate, arg.Stake, number); err != nil { return nil, err } case types.KickedCandidate: @@ -123,7 +123,7 @@ func (dpos *Dpos) processAction(height uint64, chainCfg *params.ChainConfig, sta return nil, err } for _, cadicate := range arg.Candidates { - if err := sys.KickedCandidate(epcho, cadicate, height); err != nil { + if err := sys.KickedCandidate(epcho, cadicate, number); err != nil { return nil, err } } diff --git a/consensus/dpos/vote.go b/consensus/dpos/vote.go index 70a5f7cc..d726ce44 100644 --- a/consensus/dpos/vote.go +++ b/consensus/dpos/vote.go @@ -48,7 +48,7 @@ func NewSystem(state *state.StateDB, config *Config) *System { } // RegCandidate register a candidate -func (sys *System) RegCandidate(epcho uint64, candidate string, url string, stake *big.Int, height uint64) error { +func (sys *System) RegCandidate(epcho uint64, candidate string, url string, stake *big.Int, number uint64) error { // url validity if uint64(len(url)) > sys.config.MaxURLLen { return fmt.Errorf("invalid url %v(too long, max %v)", url, sys.config.MaxURLLen) @@ -90,7 +90,7 @@ func (sys *System) RegCandidate(epcho uint64, candidate string, url string, stak URL: url, Quantity: big.NewInt(0), TotalQuantity: big.NewInt(0), - Height: height, + Number: number, } prod.Quantity = new(big.Int).Add(prod.Quantity, q) prod.TotalQuantity = new(big.Int).Add(prod.TotalQuantity, q) @@ -110,7 +110,7 @@ func (sys *System) RegCandidate(epcho uint64, candidate string, url string, stak } // UpdateCandidate update a candidate -func (sys *System) UpdateCandidate(epcho uint64, candidate string, url string, nstake *big.Int, height uint64) error { +func (sys *System) UpdateCandidate(epcho uint64, candidate string, url string, nstake *big.Int, number uint64) error { // url validity if uint64(len(url)) > sys.config.MaxURLLen { return fmt.Errorf("invalid url %v(too long, max %v)", url, sys.config.MaxURLLen) @@ -168,7 +168,7 @@ func (sys *System) UpdateCandidate(epcho uint64, candidate string, url string, n prod.URL = url prod.Quantity = new(big.Int).Add(prod.Quantity, q) prod.TotalQuantity = new(big.Int).Add(prod.TotalQuantity, q) - prod.Height = height + prod.Number = number if err := sys.SetCandidate(prod); err != nil { return err } @@ -185,7 +185,7 @@ func (sys *System) UpdateCandidate(epcho uint64, candidate string, url string, n } // UnregCandidate unregister a candidate -func (sys *System) UnregCandidate(epcho uint64, candidate string, height uint64) error { +func (sys *System) UnregCandidate(epcho uint64, candidate string, number uint64) error { // name validity prod, err := sys.GetCandidate(candidate) if err != nil { @@ -200,7 +200,7 @@ func (sys *System) UnregCandidate(epcho uint64, candidate string, height uint64) // db prod.Type = Freeze - prod.Height = height + prod.Number = number if err := sys.SetCandidate(prod); err != nil { return err } @@ -245,7 +245,7 @@ func (sys *System) UnregCandidate(epcho uint64, candidate string, height uint64) } // RefundCandidate refund a candidate -func (sys *System) RefundCandidate(epcho uint64, candidate string, height uint64) error { +func (sys *System) RefundCandidate(epcho uint64, candidate string, number uint64) error { // name validity prod, err := sys.GetCandidate(candidate) if err != nil { @@ -273,7 +273,7 @@ func (sys *System) RefundCandidate(epcho uint64, candidate string, height uint64 if tstate == nil { break } - if tstate.Height < prod.Height { + if tstate.Number < prod.Number { break } freeze++ @@ -320,7 +320,7 @@ func (sys *System) RefundCandidate(epcho uint64, candidate string, height uint64 } // VoteCandidate vote a candidate -func (sys *System) VoteCandidate(epcho uint64, voter string, candidate string, stake *big.Int, height uint64) error { +func (sys *System) VoteCandidate(epcho uint64, voter string, candidate string, stake *big.Int, number uint64) error { // candidate validity prod, err := sys.GetCandidate(candidate) if err != nil { @@ -365,7 +365,7 @@ func (sys *System) VoteCandidate(epcho uint64, voter string, candidate string, s } } - voterInfo.Height = height + voterInfo.Number = number voterInfo.Quantity = new(big.Int).Add(voterInfo.Quantity, q) if err := sys.SetVoter(voterInfo); err != nil { return err @@ -388,7 +388,7 @@ func (sys *System) VoteCandidate(epcho uint64, voter string, candidate string, s } // KickedCandidate kicked -func (sys *System) KickedCandidate(epcho uint64, candidate string, height uint64) error { +func (sys *System) KickedCandidate(epcho uint64, candidate string, number uint64) error { // name validity prod, err := sys.GetCandidate(candidate) if prod == nil || err != nil { @@ -422,7 +422,7 @@ func (sys *System) KickedCandidate(epcho uint64, candidate string, height uint64 // } prod.TotalQuantity = big.NewInt(0) - prod.Height = height + prod.Number = number prod.Type = Black if err := sys.SetCandidate(prod); err != nil { return err @@ -446,7 +446,7 @@ func (sys *System) ExitTakeOver(epcho uint64) error { return sys.SetState(gstate) } -func (sys *System) onblock(epcho uint64, height uint64) error { +func (sys *System) onblock(epcho uint64, number uint64) error { pepcho, err := sys.GetLastestEpcho() if err != nil { return err @@ -470,13 +470,13 @@ func (sys *System) onblock(epcho uint64, height uint64) error { TotalQuantity: new(big.Int).SetBytes(pState.TotalQuantity.Bytes()), TakeOver: pState.TakeOver, Dpos: pState.Dpos, - Height: height, + Number: number, } return sys.SetState(gstate) } // UpdateElectedCandidates update -func (sys *System) UpdateElectedCandidates(pepcho uint64, epcho uint64, height uint64, counter func(from uint64, to uint64, index uint64) uint64) error { +func (sys *System) UpdateElectedCandidates(pepcho uint64, epcho uint64, number uint64, counter func(from uint64, to uint64, index uint64) uint64) error { if pepcho > epcho { panic(fmt.Errorf("UpdateElectedCandidates unreached")) } @@ -517,7 +517,7 @@ func (sys *System) UpdateElectedCandidates(pepcho uint64, epcho uint64, height u totalQuantity := big.NewInt(0) for _, candidateInfo := range candidateInfoArray { if index, ok := oldcandidates[candidateInfo.Name]; ok { - candidateInfo.Counter += counter(ppstate.Height, height, index) + candidateInfo.Counter += counter(ppstate.Number, number, index) } totalQuantity = new(big.Int).Add(totalQuantity, candidateInfo.Quantity) if !candidateInfo.invalid() && (!pstate.Dpos || strings.Compare(candidateInfo.Name, sys.config.SystemName) != 0) { @@ -566,7 +566,7 @@ func (sys *System) UpdateElectedCandidates(pepcho uint64, epcho uint64, height u } pstate.ActivatedCandidateSchedule = activatedCandidateSchedule pstate.ActivatedTotalQuantity = activeTotalQuantity - pstate.Height = height + pstate.Number = number if err := sys.SetState(pstate); err != nil { return err } @@ -577,7 +577,7 @@ func (sys *System) UpdateElectedCandidates(pepcho uint64, epcho uint64, height u PreEpcho: pstate.Epcho, ActivatedTotalQuantity: big.NewInt(0), TotalQuantity: new(big.Int).SetBytes(totalQuantity.Bytes()), - OffCandidateHeight: []uint64{}, + OffCandidateNumber: []uint64{}, OffCandidateSchedule: []uint64{}, TakeOver: pstate.TakeOver, Dpos: pstate.Dpos, diff --git a/consensus/miner/api.go b/consensus/miner/api.go index 59ce7fc6..e858674c 100644 --- a/consensus/miner/api.go +++ b/consensus/miner/api.go @@ -48,7 +48,6 @@ func (api *API) SetCoinbase(name string, privKeys []string) error { } func (api *API) SetExtra(extra string) error { - return api.miner.SetExtra([]byte(extra)) } @@ -61,7 +60,6 @@ func (miner *Miner) APIs(chain consensus.IChainReader) []rpc.API { miner: miner, chain: chain, }, - Public: true, }, } apis = append(apis, miner.worker.Engine().APIs(chain)...) diff --git a/consensus/miner/miner.go b/consensus/miner/miner.go index b96e5a8a..ddaecec6 100644 --- a/consensus/miner/miner.go +++ b/consensus/miner/miner.go @@ -123,7 +123,7 @@ func (miner *Miner) Pending() (*types.Block, *state.StateDB) { // SetCoinbase coinbase name & private key func (miner *Miner) SetCoinbase(name string, privKeys []string) error { - privs := []*ecdsa.PrivateKey{} + privs := make([]*ecdsa.PrivateKey, 0, len(privKeys)) for _, privKey := range privKeys { bts, err := hex.DecodeString(privKey) if err != nil { diff --git a/consensus/miner/worker.go b/consensus/miner/worker.go index 1714a637..3f9c319f 100644 --- a/consensus/miner/worker.go +++ b/consensus/miner/worker.go @@ -83,7 +83,7 @@ func newWorker(consensus consensus.IConsensus) *Worker { // update keeps track of events. func (worker *Worker) update() { txsCh := make(chan *event.Event, txChanSize) - txsSub := event.Subscribe(nil, txsCh, event.TxEv, []*types.Transaction{}) + txsSub := event.Subscribe(nil, txsCh, event.NewTxs, []*types.Transaction{}) defer txsSub.Unsubscribe() chainHeadCh := make(chan *event.Event, chainHeadChanSize) diff --git a/crypto/crypto.go b/crypto/crypto.go index a83cfd9b..3e441c1b 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -184,13 +184,13 @@ func GenerateKey() (*ecdsa.PrivateKey, error) { // ValidateSignatureValues verifies whether the signature values are valid with // the given chain rules. The v value is assumed to be either 0 or 1. -func ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool { +func ValidateSignatureValues(v byte, r, s *big.Int) bool { if r.Cmp(common.Big1) < 0 || s.Cmp(common.Big1) < 0 { return false } // reject upper range of s values (ECDSA malleability) // see discussion in secp256k1/libsecp256k1/include/secp256k1.h - if homestead && s.Cmp(secp256k1halfN) > 0 { + if s.Cmp(secp256k1halfN) > 0 { return false } // Frontier: allow s to be in full N range diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 2523a18a..54aea582 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -176,14 +176,14 @@ func TestLoadECDSAFile(t *testing.T) { func TestValidateSignatureValues(t *testing.T) { check := func(expected bool, v byte, r, s *big.Int) { - if ValidateSignatureValues(v, r, s, false) != expected { + if ValidateSignatureValues(v, r, s) != expected { t.Errorf("mismatch for v: %d r: %d s: %d want: %v", v, r, s, expected) } } minusOne := big.NewInt(-1) one := common.Big1 zero := common.Big0 - secp256k1nMinus1 := new(big.Int).Sub(secp256k1N, common.Big1) + //secp256k1nMinus1 := new(big.Int).Sub(secp256k1N, common.Big1) // correct v,r,s check(true, 0, one, one) @@ -208,11 +208,11 @@ func TestValidateSignatureValues(t *testing.T) { check(false, 1, one, zero) // correct sig with max r,s - check(true, 0, secp256k1nMinus1, secp256k1nMinus1) + //check(true, 0, secp256k1nMinus1, secp256k1nMinus1) // correct v, combinations of incorrect r,s at upper limit - check(false, 0, secp256k1N, secp256k1nMinus1) - check(false, 0, secp256k1nMinus1, secp256k1N) - check(false, 0, secp256k1N, secp256k1N) + //check(false, 0, secp256k1N, secp256k1nMinus1) + //check(false, 0, secp256k1nMinus1, secp256k1N) + //check(false, 0, secp256k1N, secp256k1N) // current callers ensures r,s cannot be negative, but let's test for that too // as crypto package could be used stand-alone diff --git a/debug/api.go b/debug/api.go index 7cb24f76..97c2ef28 100644 --- a/debug/api.go +++ b/debug/api.go @@ -66,7 +66,7 @@ func (*HandlerT) GcStats() *debug.GCStats { } // CpuProfile turns on CPU profiling for nsec seconds and writesprofile data to file. -func (h *HandlerT) CpuProfile(file string, nsec uint) error { +func (h *HandlerT) CpuProfile(file string, nsec uint64) error { if err := h.StartCPUProfile(file); err != nil { return err } @@ -112,7 +112,7 @@ func (h *HandlerT) StopCPUProfile() error { // GoTrace turns on tracing for nsec seconds and writes // trace data to file. -func (h *HandlerT) GoTrace(file string, nsec uint) error { +func (h *HandlerT) GoTrace(file string, nsec uint64) error { if err := h.StartGoTrace(file); err != nil { return err } @@ -123,7 +123,7 @@ func (h *HandlerT) GoTrace(file string, nsec uint) error { // BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to // file. It uses a profile rate of 1 for most accurate information. If a different rate is // desired, set the rate and write the profile manually. -func (*HandlerT) BlockProfile(file string, nsec uint) error { +func (*HandlerT) BlockProfile(file string, nsec uint64) error { runtime.SetBlockProfileRate(1) time.Sleep(time.Duration(nsec) * time.Second) defer runtime.SetBlockProfileRate(0) @@ -144,7 +144,7 @@ func (*HandlerT) WriteBlockProfile(file string) error { // MutexProfile turns on mutex profiling for nsec seconds and writes profile data to file. // It uses a profile rate of 1 for most accurate information. If a different rate is // desired, set the rate and write the profile manually. -func (*HandlerT) MutexProfile(file string, nsec uint) error { +func (*HandlerT) MutexProfile(file string, nsec uint64) error { runtime.SetMutexProfileFraction(1) time.Sleep(time.Duration(nsec) * time.Second) defer runtime.SetMutexProfileFraction(0) diff --git a/event/router.go b/event/router.go index 597b3071..c92dff14 100644 --- a/event/router.go +++ b/event/router.go @@ -89,9 +89,8 @@ const ( DisconectCtrl // 1027 emit if needed to let remote peer disconnect NewPeerPassedNotify // 1028 emit when remote peer had same chain ID and genesis block AddPeerToBlacklist // 1029 add peer to blacklist - TxEv // 1030 - NewMinedEv // 1031 emit when new block was mined - NewTxs // 1032 emit when new transactions needed to broadcast + NewMinedEv // 1030 emit when new block was mined + NewTxs // 1031 emit when new transactions needed to broadcast EndSize ) diff --git a/ftservice/apibackend.go b/ftservice/apibackend.go index ea9b8967..5516aa3e 100644 --- a/ftservice/apibackend.go +++ b/ftservice/apibackend.go @@ -137,11 +137,8 @@ func (b *APIBackend) GetBlockDetailLog(ctx context.Context, blockNr rpc.BlockNum func (b *APIBackend) GetTxsByFilter(ctx context.Context, filterFn func(common.Name) bool, blockNr rpc.BlockNumber, lookbackNum uint64) []common.Hash { lastnum := uint64(blockNr) - lookbackNum - txHashs := make([]common.Hash, 0) - for ublocknum := uint64(blockNr); ublocknum > lastnum; ublocknum-- { - hash := rawdb.ReadCanonicalHash(b.ftservice.chainDb, ublocknum) if hash == (common.Hash{}) { continue @@ -168,11 +165,9 @@ func (b *APIBackend) GetTxsByFilter(ctx context.Context, filterFn func(common.Na func (b *APIBackend) GetDetailTxByFilter(ctx context.Context, filterFn func(common.Name) bool, blockNr rpc.BlockNumber, lookbackNum uint64) []*types.DetailTx { lastnum := uint64(blockNr) - lookbackNum - txdetails := make([]*types.DetailTx, 0) for ublocknum := uint64(blockNr); ublocknum > lastnum; ublocknum-- { - hash := rawdb.ReadCanonicalHash(b.ftservice.chainDb, ublocknum) if hash == (common.Hash{}) { continue @@ -180,7 +175,6 @@ func (b *APIBackend) GetDetailTxByFilter(ctx context.Context, filterFn func(comm batch_txdetails := rawdb.ReadDetailTxs(b.ftservice.chainDb, hash, ublocknum) for _, txd := range batch_txdetails { - new_intxs := make([]*types.DetailAction, 0) for _, intx := range txd.Actions { new_inactions := make([]*types.InternalAction, 0) @@ -212,43 +206,20 @@ func (b *APIBackend) GetTd(blockHash common.Hash) *big.Int { } func (b *APIBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { - - // Pending block is only known by the miner - if blockNr == rpc.PendingBlockNumber { - block, _ := b.ftservice.miner.Pending() - return block.Header(), nil - } - - // Otherwise resolve and return the block if blockNr == rpc.LatestBlockNumber { return b.ftservice.blockchain.CurrentBlock().Header(), nil } - return b.ftservice.blockchain.GetHeaderByNumber(uint64(blockNr)), nil } func (b *APIBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { - // Pending block is only known by the miner - if blockNr == rpc.PendingBlockNumber { - block, _ := b.ftservice.miner.Pending() - return block, nil - } - - // Otherwise resolve and return the block if blockNr == rpc.LatestBlockNumber { return b.ftservice.blockchain.CurrentBlock(), nil } return b.ftservice.blockchain.GetBlockByNumber(uint64(blockNr)), nil } -// func (b *APIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) { - // Pending state is only known by the miner - if blockNr == rpc.PendingBlockNumber { - block, state := b.ftservice.miner.Pending() - return state, block.Header(), nil - } - // Otherwise resolve the block number and return its state header, err := b.HeaderByNumber(ctx, blockNr) if header == nil || err != nil { return nil, nil, err @@ -280,11 +251,7 @@ func (b *APIBackend) GetAccountManager() (*accountmanager.AccountManager, error) if err != nil { return nil, err } - acctm, err := accountmanager.NewAccountManager(sdb) - if err != nil { - return nil, err - } - return acctm, nil + return accountmanager.NewAccountManager(sdb) } //GetFeeManager get fee manager @@ -400,12 +367,20 @@ func (b *APIBackend) AddBadNode(url string) error { return err } +// RemoveBadNode add a bad Node and would cause the node disconnected +func (b *APIBackend) RemoveBadNode(url string) error { + node, err := enode.ParseV4(url) + if err == nil { + b.ftservice.p2pServer.RemoveBadNode(node) + } + return err +} + // SelfNode returns the local node's endpoint information. func (b *APIBackend) SelfNode() string { return b.ftservice.p2pServer.Self().String() } -// APIs returns apis func (b *APIBackend) Engine() consensus.IEngine { return b.ftservice.engine } diff --git a/node/config.go b/node/config.go index 7a0516ba..6bdf9105 100644 --- a/node/config.go +++ b/node/config.go @@ -42,10 +42,9 @@ const ( // Config represents a small collection of configuration values to fine tune the // P2P network layer of a protocol stack. type Config struct { - Name string - DataDir string `mapstructure:"datadir"` - UseLightweightKDF bool `mapstructure:"lightkdf"` - IPCPath string `mapstructure:"ipcpath"` + Name string + DataDir string `mapstructure:"datadir"` + IPCPath string `mapstructure:"ipcpath"` HTTPHost string `mapstructure:"httphost"` HTTPPort int `mapstructure:"httpport"` diff --git a/p2p/enode/urlv4.go b/p2p/enode/urlv4.go index 8898e589..2063d28f 100644 --- a/p2p/enode/urlv4.go +++ b/p2p/enode/urlv4.go @@ -51,7 +51,7 @@ func MustParseV4(rawurl string) *Node { // // For incomplete nodes, the designator must look like one of these // -// enode:// +// fnode:// // // // For complete nodes, the node ID is encoded in the username portion @@ -65,7 +65,7 @@ func MustParseV4(rawurl string) *Node { // a node with IP address 10.3.58.6, TCP listening port 30303 // and UDP discovery port 30301. // -// enode://@10.3.58.6:30303?discport=30301 +// fnode://@10.3.58.6:30303?discport=30301 func ParseV4(rawurl string) (*Node, error) { if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil { id, err := parsePubkey(m[1]) @@ -124,7 +124,18 @@ func parseComplete(rawurl string) (*Node, error) { return nil, fmt.Errorf("invalid host: %v", err) } if ip = net.ParseIP(host); ip == nil { - return nil, errors.New("invalid IP address") + dips, err := net.LookupHost(host) + if err != nil || len(dips) == 0 { + return nil, errors.New("invalid IP address") + } + for _, dip := range dips { + if ip = net.ParseIP(dip); ip == nil { + return nil, errors.New("invalid IP address") + } + if ip.To4() != nil { + break + } + } } // Ensure the IP is 4 bytes long for IPv4 addresses. if ipv4 := ip.To4(); ipv4 != nil { diff --git a/p2p/enode/urlv4_test.go b/p2p/enode/urlv4_test.go index 5ee27dea..6c3a9f96 100644 --- a/p2p/enode/urlv4_test.go +++ b/p2p/enode/urlv4_test.go @@ -120,6 +120,22 @@ var parseNodeTests = []struct { }, } +var parseDNSNodeTests = []struct { + rawurl string + wantError string + wantResult *Node +}{ + { + rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@localhost:52150", + wantResult: NewV4( + hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{0x7f, 0x0, 0x0, 0x1}, + 52150, + 52150, + ), + }, +} + func hexPubkey(h string) *ecdsa.PublicKey { k, err := parsePubkey(h) if err != nil { @@ -128,6 +144,29 @@ func hexPubkey(h string) *ecdsa.PublicKey { return k } +func TestParseDNSNode(t *testing.T) { + for _, test := range parseDNSNodeTests { + n, err := ParseV4(test.rawurl) + if test.wantError != "" { + if err == nil { + t.Errorf("test %q:\n got nil error, expected %#q", test.rawurl, test.wantError) + continue + } else if err.Error() != test.wantError { + t.Errorf("test %q:\n got error %#q, expected %#q", test.rawurl, err.Error(), test.wantError) + continue + } + } else { + if err != nil { + t.Errorf("test %q:\n unexpected error: %v", test.rawurl, err) + continue + } + if !reflect.DeepEqual(n, test.wantResult) { + t.Errorf("test %q:\n result mismatch:\ngot: %#v\nwant: %#v", test.rawurl, n, test.wantResult) + } + } + } +} + func TestParseNode(t *testing.T) { for _, test := range parseNodeTests { n, err := ParseV4(test.rawurl) diff --git a/p2p/protoadaptor/ProtoAdaptor.go b/p2p/protoadaptor/ProtoAdaptor.go index 6aefbfb7..622f818a 100644 --- a/p2p/protoadaptor/ProtoAdaptor.go +++ b/p2p/protoadaptor/ProtoAdaptor.go @@ -120,7 +120,7 @@ func (adaptor *ProtoAdaptor) adaptorLoop(peer *p2p.Peer, ws p2p.MsgReadWriter) e func checkDDOS(m map[int][]int64, e *router.Event) bool { t := e.Typecode - limit := int64(router.GetDDosLimit(t)) + limit := int64(router.GetDDosLimit(t)) * 10 if limit == 0 { return false } diff --git a/p2p/server.go b/p2p/server.go index ec3aaeba..2c699154 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -170,6 +170,7 @@ type Server struct { addtrusted chan *enode.Node removetrusted chan *enode.Node addBad chan *enode.Node + removeBad chan *enode.Node posthandshake chan *conn addpeer chan *conn delpeer chan peerDrop @@ -349,6 +350,14 @@ func (srv *Server) AddBadNode(node *enode.Node) { } } +// RemoveBadNode remove the node from the blacklist +func (srv *Server) RemoveBadNode(node *enode.Node) { + select { + case srv.removeBad <- node: + case <-srv.quit: + } +} + // RemovePeer disconnects from the given node func (srv *Server) RemovePeer(node *enode.Node) { select { @@ -545,6 +554,7 @@ func (srv *Server) Start() (err error) { srv.peerOpDone = make(chan struct{}) srv.addBad = make(chan *enode.Node) + srv.removeBad = make(chan *enode.Node) srv.badNodeOp = make(chan badOpFunc) srv.badNodeOpDone = make(chan struct{}) @@ -743,6 +753,13 @@ running: if p, ok := peers[n.ID()]; ok { p.Disconnect(DiscBadNode) } + case n := <-srv.removeBad: + if _, ok := badNodes[n.ID()]; ok { + delete(badNodes, n.ID()) + } + if p, ok := peers[n.ID()]; ok { + p.rw.set(badNodeConn, false) + } case op := <-srv.badNodeOp: op(badNodes) srv.badNodeOpDone <- struct{}{} diff --git a/params/params.go b/params/params.go index 2306b09f..51868eab 100644 --- a/params/params.go +++ b/params/params.go @@ -58,7 +58,8 @@ const ( CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. - MaxCodeSize = 24576 // Maximum bytecode to permit for a contract + MaxCodeSize uint64 = 24576 // Maximum bytecode to permit for a contract + MaxTxSize uint64 = 32 * 1024 // Heuristic limit, reject transactions over 32KB to prfeed DOS attacks // Precompiled contract gas prices @@ -83,7 +84,7 @@ var ( DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. ) -var ( +const ( MaxSignDepth = uint64(10) MaxSignLength = uint64(50) ) @@ -94,3 +95,8 @@ const ( ContractFeeType = uint64(1) CoinbaseFeeType = uint64(2) ) + +//rpc max fee result count +const ( + MaxFeeResultCount = uint64(1000) +) diff --git a/processor/evm.go b/processor/evm.go index f818883c..1fa7a693 100644 --- a/processor/evm.go +++ b/processor/evm.go @@ -73,12 +73,12 @@ type ChainContext interface { } type EgnineContext interface { - // Author retrieves the address of the account that minted the given + // Author retrieves the name of the account that minted the given // block, which may be different from the header's coinbase if a consensus // engine is based on signatures. Author(header *types.Header) (common.Name, error) - ProcessAction(height uint64, chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) + ProcessAction(number uint64, chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) GetDelegatedByTime(state *state.StateDB, candidate string, timestamp uint64) (stake *big.Int, err error) diff --git a/processor/processor.go b/processor/processor.go index 8cc0c00a..372bccab 100644 --- a/processor/processor.go +++ b/processor/processor.go @@ -106,7 +106,6 @@ func (p *StateProcessor) ApplyTransaction(author *common.Name, gp *common.GasPoo detailTx := &types.DetailTx{} var detailActions []*types.DetailAction for i, action := range tx.GetActions() { - // if needCheckSign(accountDB, action) { if err := accountDB.RecoverTx(types.NewSigner(config.ChainID), tx); err != nil { return nil, 0, err diff --git a/processor/validator.go b/processor/validator.go index 071fa545..3dd754ac 100644 --- a/processor/validator.go +++ b/processor/validator.go @@ -131,6 +131,10 @@ func (v *BlockValidator) ValidateHeader(header *types.Header, seal bool) error { // ValidateBody verifies the the block header's transaction roots. // The headers are assumed to be already validated at this point. func (v *BlockValidator) ValidateBody(block *types.Block) error { + if err := block.Check(); err != nil { + return err + } + // Header validity is known at this point, check the uncles and transactions if hash := types.DeriveTxsMerkleRoot(block.Txs); hash != block.TxHash() { return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, block.TxHash()) diff --git a/processor/vm/common_test.go b/processor/vm/common_test.go new file mode 100644 index 00000000..0ebbe390 --- /dev/null +++ b/processor/vm/common_test.go @@ -0,0 +1,127 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +package vm + +import ( + "bytes" + "math/big" + "testing" +) + +func TestCalcMemSize(t *testing.T) { + tests := []struct { + off *big.Int + l *big.Int + which *big.Int + }{ + {big.NewInt(0), big.NewInt(0), big.NewInt(0)}, + {big.NewInt(10), big.NewInt(0), big.NewInt(0)}, + {big.NewInt(0), big.NewInt(10), big.NewInt(10)}, + {big.NewInt(10), big.NewInt(100), big.NewInt(110)}, + } + for _, test := range tests { + ret := calcMemSize(test.off, test.l) + if ret.Cmp(test.which) != 0 { + t.Fatalf("expected %x, got %02x", test.which, ret) + } + } +} + +func TestGetData(t *testing.T) { + tests := []struct { + data []byte + start uint64 + size uint64 + res []byte + }{ + {[]byte{}, 0, 0, []byte{}}, + {[]byte{}, 1, 0, []byte{}}, + {[]byte{}, 0, 1, []byte{0}}, + {[]byte{}, 1, 1, []byte{0}}, + {[]byte{1, 2, 3, 4, 5}, 0, 0, []byte{}}, + {[]byte{1, 2, 3, 4, 5}, 1, 0, []byte{}}, + {[]byte{1, 2, 3, 4, 5}, 0, 1, []byte{1}}, + {[]byte{1, 2, 3, 4, 5}, 1, 1, []byte{2}}, + {[]byte{1, 2, 3, 4, 5}, 1, 10, []byte{2, 3, 4, 5, 0, 0, 0, 0, 0, 0}}, + {[]byte{1, 2, 3, 4, 5, 0, 0, 0}, 1, 10, []byte{2, 3, 4, 5, 0, 0, 0, 0, 0, 0}}, + } + for _, test := range tests { + ret := getData(test.data, test.start, test.size) + if !bytes.Equal(ret, test.res) { + t.Fatal("expected ,", test.res, " got ", ret) + } + } +} + +func TestGetDataBig(t *testing.T) { + tests := []struct { + data []byte + start *big.Int + size *big.Int + res []byte + }{ + {[]byte{}, big.NewInt(0), big.NewInt(0), []byte{}}, + {[]byte{}, big.NewInt(1), big.NewInt(0), []byte{}}, + {[]byte{}, big.NewInt(0), big.NewInt(1), []byte{0}}, + {[]byte{}, big.NewInt(1), big.NewInt(1), []byte{0}}, + {[]byte{1, 2, 3, 4, 5}, big.NewInt(0), big.NewInt(0), []byte{}}, + {[]byte{1, 2, 3, 4, 5}, big.NewInt(1), big.NewInt(0), []byte{}}, + {[]byte{1, 2, 3, 4, 5}, big.NewInt(0), big.NewInt(1), []byte{1}}, + {[]byte{1, 2, 3, 4, 5}, big.NewInt(1), big.NewInt(1), []byte{2}}, + {[]byte{1, 2, 3, 4, 5}, big.NewInt(1), big.NewInt(10), []byte{2, 3, 4, 5, 0, 0, 0, 0, 0, 0}}, + {[]byte{1, 2, 3, 4, 5, 0, 0, 0}, big.NewInt(1), big.NewInt(10), []byte{2, 3, 4, 5, 0, 0, 0, 0, 0, 0}}, + } + for _, test := range tests { + ret := getDataBig(test.data, test.start, test.size) + if !bytes.Equal(ret, test.res) { + t.Fatal("expected ,", test.res, " got ", ret) + } + } +} + +func TestToWordSize(t *testing.T) { + tests := []struct { + size uint64 + res uint64 + }{ + {31, 1}, + {32, 1}, + {33, 2}, + {0, 0}, + } + for _, test := range tests { + ret := toWordSize(test.size) + if ret != test.res { + t.Fatalf("expected %x, got %02x", test.res, ret) + } + } +} + +func TestAllZero(t *testing.T) { + tests := []struct { + data []byte + res bool + }{ + {[]byte{0, 0, 0}, true}, + {[]byte{0, 0, 1}, false}, + } + for _, test := range tests { + ret := allZero(test.data) + if ret != test.res { + t.Fatal("expected ,", test.res, " got ", ret) + } + } +} diff --git a/processor/vm/contract.go b/processor/vm/contract.go index 08ec6141..ca6e93c0 100644 --- a/processor/vm/contract.go +++ b/processor/vm/contract.go @@ -30,7 +30,7 @@ type ContractRef interface { // AccountRef implements ContractRef. // // Account references are used during EVM initialisation and -// it's primary use is to fetch addresses. Removing this object +// it's primary use is to fetch names. Removing this object // proves difficult because of the cached jump destinations which // are fetched from the parent contract (i.e. the caller), which // is a ContractRef. @@ -140,15 +140,14 @@ func (c *Contract) Value() *big.Int { } // SetCode sets the code to the contract -func (self *Contract) SetCode(hash common.Hash, code []byte) { - self.Code = code - self.CodeHash = hash +func (c *Contract) SetCode(hash common.Hash, code []byte) { + c.Code = code + c.CodeHash = hash } -// SetCallCode sets the code of the contract and address of the backing data -// object -func (self *Contract) SetCallCode(name *common.Name, hash common.Hash, code []byte) { - self.Code = code - self.CodeHash = hash - self.CodeName = name +// SetCallCode sets the code of the contract and name of the backing dataobject +func (c *Contract) SetCallCode(name *common.Name, hash common.Hash, code []byte) { + c.Code = code + c.CodeHash = hash + c.CodeName = name } diff --git a/processor/vm/contracts.go b/processor/vm/contracts.go index 1c8dc0b9..27d9fbdb 100644 --- a/processor/vm/contracts.go +++ b/processor/vm/contracts.go @@ -87,7 +87,7 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) { v := input[63] - 27 // tighter sig s values input homestead only apply to tx sigs - if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) { + if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s) { return nil, nil } // v needs to be at the end for libsecp256k1 diff --git a/processor/vm/vm.go b/processor/vm/vm.go index 0476bec8..037b3596 100644 --- a/processor/vm/vm.go +++ b/processor/vm/vm.go @@ -542,8 +542,7 @@ func (evm *EVM) Create(caller ContractRef, action *types.Action, gas uint64) (re } // check whether the max code size has been exceeded - //maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize - maxCodeSizeExceeded := len(ret) > params.MaxCodeSize + maxCodeSizeExceeded := len(ret) > int(params.MaxCodeSize) // if the contract creation ran successfully and no errors were returned // calculate the gas required to store the code. If the code could not // be stored due to not enough gas set an error and let it be handled diff --git a/rpc/types.go b/rpc/types.go index dfeba6de..0d71a9cb 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -119,13 +119,12 @@ type ServerCodec interface { type BlockNumber int64 const ( - PendingBlockNumber = BlockNumber(-2) LatestBlockNumber = BlockNumber(-1) EarliestBlockNumber = BlockNumber(0) ) // UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: -// - "latest", "earliest" or "pending" as string arguments +// - "latest", "earliest" as string arguments // - the block number // Returned errors: // - an invalid block number error when the given argument isn't a known strings @@ -143,9 +142,6 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error { case "latest": *bn = LatestBlockNumber return nil - case "pending": - *bn = PendingBlockNumber - return nil } blckNum, err := strconv.ParseInt(input, 10, 64) diff --git a/rpcapi/account.go b/rpcapi/account.go index 4df2d0b3..6875d844 100644 --- a/rpcapi/account.go +++ b/rpcapi/account.go @@ -18,7 +18,6 @@ package rpcapi import ( "context" - "errors" "math/big" "github.com/ethereum/go-ethereum/common/hexutil" @@ -35,67 +34,50 @@ func NewAccountAPI(b Backend) *AccountAPI { return &AccountAPI{b} } -var ( - ErrGetAccounManagerErr = errors.New("get account manager failure") -) - //AccountIsExist -func (aapi *AccountAPI) AccountIsExist(ctx context.Context, acctName common.Name) (bool, error) { +func (aapi *AccountAPI) AccountIsExist(acctName common.Name) (bool, error) { acct, err := aapi.b.GetAccountManager() if err != nil { return false, err } - if acct == nil { - return false, ErrGetAccounManagerErr - } return acct.AccountIsExist(acctName) } //GetAccountByID -func (aapi *AccountAPI) GetAccountByID(ctx context.Context, accountID uint64) (*accountmanager.Account, error) { +func (aapi *AccountAPI) GetAccountByID(accountID uint64) (*accountmanager.Account, error) { am, err := aapi.b.GetAccountManager() if err != nil { return nil, err } - if am == nil { - return nil, ErrGetAccounManagerErr - } + return am.GetAccountById(accountID) } //GetAccountByName -func (aapi *AccountAPI) GetAccountByName(ctx context.Context, accountName common.Name) (*accountmanager.Account, error) { +func (aapi *AccountAPI) GetAccountByName(accountName common.Name) (*accountmanager.Account, error) { am, err := aapi.b.GetAccountManager() if err != nil { return nil, err } - if am == nil { - return nil, ErrGetAccounManagerErr - } + return am.GetAccountByName(accountName) } //GetAccountBalanceByID -func (aapi *AccountAPI) GetAccountBalanceByID(ctx context.Context, accountName common.Name, assetID uint64, typeID uint64) (*big.Int, error) { +func (aapi *AccountAPI) GetAccountBalanceByID(accountName common.Name, assetID uint64, typeID uint64) (*big.Int, error) { am, err := aapi.b.GetAccountManager() if err != nil { return nil, err } - if am == nil { - return nil, ErrGetAccounManagerErr - } return am.GetAccountBalanceByID(accountName, assetID, typeID) } //GetCode -func (aapi *AccountAPI) GetCode(ctx context.Context, accountName common.Name) (hexutil.Bytes, error) { +func (aapi *AccountAPI) GetCode(accountName common.Name) (hexutil.Bytes, error) { acct, err := aapi.b.GetAccountManager() if err != nil { return nil, err } - if acct == nil { - return nil, ErrGetAccounManagerErr - } result, err := acct.GetCode(accountName) if err != nil { @@ -106,14 +88,12 @@ func (aapi *AccountAPI) GetCode(ctx context.Context, accountName common.Name) (h } //GetNonce -func (aapi *AccountAPI) GetNonce(ctx context.Context, accountName common.Name) (uint64, error) { +func (aapi *AccountAPI) GetNonce(accountName common.Name) (uint64, error) { acct, err := aapi.b.GetAccountManager() if err != nil { return 0, err } - if acct == nil { - return 0, ErrGetAccounManagerErr - } + return acct.GetNonce(accountName) } @@ -124,57 +104,43 @@ func (aapi *AccountAPI) GetAssetInfoByName(ctx context.Context, assetName string if err != nil { return nil, err } - if acct == nil { - return nil, ErrGetAccounManagerErr - } return acct.GetAssetInfoByName(assetName) } //GetAssetInfoByID -func (aapi *AccountAPI) GetAssetInfoByID(ctx context.Context, assetID uint64) (*asset.AssetObject, error) { +func (aapi *AccountAPI) GetAssetInfoByID(assetID uint64) (*asset.AssetObject, error) { acct, err := aapi.b.GetAccountManager() if err != nil { return nil, err } - if acct == nil { - return nil, ErrGetAccounManagerErr - } return acct.GetAssetInfoByID(assetID) } //GetAssetAmountByTime -func (aapi *AccountAPI) GetAssetAmountByTime(ctx context.Context, assetID uint64, time uint64) (*big.Int, error) { +func (aapi *AccountAPI) GetAssetAmountByTime(assetID uint64, time uint64) (*big.Int, error) { am, err := aapi.b.GetAccountManager() if err != nil { return nil, err } - if am == nil { - return nil, ErrGetAccounManagerErr - } return am.GetAssetAmountByTime(assetID, time) } //GetAccountBalanceByTime -func (aapi *AccountAPI) GetAccountBalanceByTime(ctx context.Context, accountName common.Name, assetID uint64, typeID uint64, time uint64) (*big.Int, error) { +func (aapi *AccountAPI) GetAccountBalanceByTime(accountName common.Name, assetID uint64, typeID uint64, time uint64) (*big.Int, error) { am, err := aapi.b.GetAccountManager() if err != nil { return nil, err } - if am == nil { - return nil, ErrGetAccounManagerErr - } return am.GetBalanceByTime(accountName, assetID, typeID, time) } //GetSnapshotLast get last snapshot time -func (aapi *AccountAPI) GetSnapshotLast(ctx context.Context) (uint64, error) { +func (aapi *AccountAPI) GetSnapshotLast() (uint64, error) { am, err := aapi.b.GetAccountManager() if err != nil { return 0, err } - if am == nil { - return 0, ErrGetAccounManagerErr - } + return am.GetSnapshotTime(0, 0) } @@ -184,8 +150,5 @@ func (aapi *AccountAPI) GetSnapshotTime(ctx context.Context, m uint64, time uint if err != nil { return 0, err } - if am == nil { - return 0, ErrGetAccounManagerErr - } return am.GetSnapshotTime(m, time) } diff --git a/rpcapi/addrlock.go b/rpcapi/addrlock.go deleted file mode 100644 index 00824de9..00000000 --- a/rpcapi/addrlock.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package rpcapi - -import ( - "sync" - - "github.com/fractalplatform/fractal/common" -) - -type AddrLocker struct { - mu sync.Mutex - locks map[common.Address]*sync.Mutex -} - -// lock returns the lock of the given address. -func (l *AddrLocker) lock(address common.Address) *sync.Mutex { - l.mu.Lock() - defer l.mu.Unlock() - if l.locks == nil { - l.locks = make(map[common.Address]*sync.Mutex) - } - if _, ok := l.locks[address]; !ok { - l.locks[address] = new(sync.Mutex) - } - return l.locks[address] -} - -// LockAddr locks an account's mutex. This is used to prevent another tx getting the -// same nonce until the lock is released. The mutex prevents the (an identical nonce) from -// being read again during the time that the first transaction is being signed. -func (l *AddrLocker) LockAddr(address common.Address) { - l.lock(address).Lock() -} - -// UnlockAddr unlocks the mutex of the given account. -func (l *AddrLocker) UnlockAddr(address common.Address) { - l.lock(address).Unlock() -} diff --git a/rpcapi/backend.go b/rpcapi/backend.go index 53e8b2b9..3d589d83 100644 --- a/rpcapi/backend.go +++ b/rpcapi/backend.go @@ -61,16 +61,14 @@ type Backend interface { // TxPool API SendTx(ctx context.Context, signedTx *types.Transaction) error - GetPoolTransactions() ([]*types.Transaction, error) GetPoolTransaction(txHash common.Hash) *types.Transaction Stats() (pending int, queued int) TxPoolContent() (map[common.Name][]*types.Transaction, map[common.Name][]*types.Transaction) + SetGasPrice(gasPrice *big.Int) bool //Account API GetAccountManager() (*accountmanager.AccountManager, error) - SetGasPrice(gasPrice *big.Int) bool - //fee manager GetFeeManager() (*feemanager.FeeManager, error) GetFeeManagerByTime(time uint64) (*feemanager.FeeManager, error) @@ -85,6 +83,7 @@ type Backend interface { BadNodesCount() int BadNodes() []string AddBadNode(url string) error + RemoveBadNode(url string) error SelfNode() string Engine() consensus.IEngine APIs() []rpc.API @@ -95,9 +94,14 @@ func GetAPIs(apiBackend Backend) []rpc.API { { Namespace: "txpool", Version: "1.0", - Service: NewPublicTxPoolAPI(apiBackend), - Public: true, - }, { + Service: NewPrivateTxPoolAPI(apiBackend), + }, + { + Namespace: "ft", + Version: "1.0", + Service: NewPrivateBlockChainAPI(apiBackend), + }, + { Namespace: "ft", Version: "1.0", Service: NewPublicBlockChainAPI(apiBackend), @@ -113,22 +117,21 @@ func GetAPIs(apiBackend Backend) []rpc.API { Version: "1.0", Service: NewAccountAPI(apiBackend), Public: true, - }, { - Namespace: "p2p", - Version: "1.0", - Service: NewPrivateP2pAPI(apiBackend), - Public: true, }, { Namespace: "fee", Version: "1.0", Service: NewFeeAPI(apiBackend), Public: true, }, + { + Namespace: "p2p", + Version: "1.0", + Service: NewPrivateP2pAPI(apiBackend), + }, { Namespace: "debug", Version: "1.0", Service: debug.Handler, - Public: true, // todo private }, } return append(apis, apiBackend.APIs()...) diff --git a/rpcapi/blockchain.go b/rpcapi/blockchain.go index 5c63812c..565af209 100644 --- a/rpcapi/blockchain.go +++ b/rpcapi/blockchain.go @@ -18,7 +18,6 @@ package rpcapi import ( "context" - "encoding/json" "fmt" "math" "math/big" @@ -49,9 +48,7 @@ func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { // GetCurrentBlock returns cureent block. func (s *PublicBlockChainAPI) GetCurrentBlock(fullTx bool) map[string]interface{} { - block := s.b.CurrentBlock() - response := s.rpcOutputBlock(s.b.ChainConfig().ChainID, block, true, fullTx) - return response + return s.rpcOutputBlock(s.b.ChainConfig().ChainID, s.b.CurrentBlock(), true, fullTx) } // GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full @@ -70,12 +67,6 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc. block, err := s.b.BlockByNumber(ctx, blockNr) if block != nil { response := s.rpcOutputBlock(s.b.ChainConfig().ChainID, block, true, fullTx) - if blockNr == rpc.PendingBlockNumber { - // Pending blocks need to nil out a few fields - for _, field := range []string{"hash", "nonce", "miner"} { - response[field] = nil - } - } return response, err } return nil, err @@ -118,7 +109,6 @@ func (s *PublicBlockChainAPI) GetTransactionReceipt(ctx context.Context, hash co return nil, nil } receipt := receipts[index] - return receipt.NewRPCReceipt(blockHash, blockNumber, index, tx), nil } @@ -132,40 +122,51 @@ func (s *PublicBlockChainAPI) GetBlockAndResultByNumber(ctx context.Context, blo return r, err } +// GetTxsByAccount return all txs, sent from or received by a specific account +// the range is indicate by blockNr and lookbackNum, +// from blocks with number from blockNr-lookbackNum to blockNr func (s *PublicBlockChainAPI) GetTxsByAccount(ctx context.Context, acctName common.Name, blockNr rpc.BlockNumber, lookbackNum uint64) ([]common.Hash, error) { filterFn := func(name common.Name) bool { return name == acctName } - return s.b.GetTxsByFilter(ctx, filterFn, blockNr, lookbackNum), nil } +// GetTxsByBloom return all txs, filtered by a bloomByte +// bloomByte is constructed by some quantities of account names +// the range is indicate by blockNr and lookbackNum, +// from blocks with number from blockNr-lookbackNum to blockNr func (s *PublicBlockChainAPI) GetTxsByBloom(ctx context.Context, bloomByte hexutil.Bytes, blockNr rpc.BlockNumber, lookbackNum uint64) ([]common.Hash, error) { bloom := types.BytesToBloom(bloomByte) - filterFn := func(name common.Name) bool { return bloom.TestBytes([]byte(name)) } return s.b.GetTxsByFilter(ctx, filterFn, blockNr, lookbackNum), nil } +// GetInternalTxByAccount return all logs of interal txs, sent from or received by a specific account +// the range is indicate by blockNr and lookbackNum, +// from blocks with number from blockNr-lookbackNum to blockNr func (s *PublicBlockChainAPI) GetInternalTxByAccount(ctx context.Context, acctName common.Name, blockNr rpc.BlockNumber, lookbackNum uint64) ([]*types.DetailTx, error) { filterFn := func(name common.Name) bool { return name == acctName } - return s.b.GetDetailTxByFilter(ctx, filterFn, blockNr, lookbackNum), nil } +// GetInternalTxByBloom return all logs of interal txs, filtered by a bloomByte +// bloomByte is constructed by some quantities of account names +// the range is indicate by blockNr and lookbackNum, +// from blocks with number from blockNr-lookbackNum to blockNr func (s *PublicBlockChainAPI) GetInternalTxByBloom(ctx context.Context, bloomByte hexutil.Bytes, blockNr rpc.BlockNumber, lookbackNum uint64) ([]*types.DetailTx, error) { bloom := types.BytesToBloom(bloomByte) - filterFn := func(name common.Name) bool { return bloom.TestBytes([]byte(name)) } return s.b.GetDetailTxByFilter(ctx, filterFn, blockNr, lookbackNum), nil } +// GetInternalTxByHash return logs of interal txs include by a transcastion func (s *PublicBlockChainAPI) GetInternalTxByHash(ctx context.Context, hash common.Hash) (*types.DetailTx, error) { tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.b.ChainDb(), hash) if tx == nil { @@ -183,13 +184,11 @@ func (s *PublicBlockChainAPI) GetInternalTxByHash(ctx context.Context, hash comm func (s *PublicBlockChainAPI) GetBadBlocks(ctx context.Context, fullTx bool) ([]map[string]interface{}, error) { blocks, err := s.b.GetBadBlocks(ctx) if len(blocks) != 0 { - ret_block := make([]map[string]interface{}, len(blocks)) - + badBlocks := make([]map[string]interface{}, len(blocks)) for i, b := range blocks { - ret_block[i] = s.rpcOutputBlock(s.b.ChainConfig().ChainID, b, true, fullTx) + badBlocks[i] = s.rpcOutputBlock(s.b.ChainConfig().ChainID, b, true, fullTx) } - - return ret_block, nil + return badBlocks, nil } return nil, err } @@ -276,7 +275,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h hi = uint64(args.Gas) } else { // Retrieve the current pending block to act as the gas ceiling - block, err := s.b.BlockByNumber(ctx, rpc.PendingBlockNumber) + block, err := s.b.BlockByNumber(ctx, rpc.LatestBlockNumber) if err != nil { return 0, err } @@ -287,8 +286,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h // Create a helper to check if a gas allowance results in an executable transaction executable := func(gas uint64) bool { args.Gas = gas - - _, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}, 0) + _, _, failed, err := s.doCall(ctx, args, rpc.LatestBlockNumber, vm.Config{}, 0) if err != nil || failed { return false } @@ -313,30 +311,27 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h } // GetChainConfig returns chain config. -func (s *PublicBlockChainAPI) GetChainConfig() map[string]interface{} { - ret := map[string]interface{}{} - g, err := s.b.BlockByNumber(context.Background(), 0) +func (s *PublicBlockChainAPI) GetChainConfig(ctx context.Context) (*params.ChainConfig, error) { + g, err := s.b.BlockByNumber(ctx, 0) if err != nil { - return ret + return nil, err } - cfg := rawdb.ReadChainConfig(s.b.ChainDb(), g.Hash()) - bts, _ := json.Marshal(cfg) - json.Unmarshal(bts, &ret) - return ret + return rawdb.ReadChainConfig(s.b.ChainDb(), g.Hash()), nil } -// GetGenesis returns genesis config. -func (s *PublicBlockChainAPI) GetGenesis() map[string]interface{} { - ret := map[string]interface{}{} - g, err := s.b.BlockByNumber(context.Background(), 0) - if err != nil { - return ret - } - json.Unmarshal(g.Head.Extra, &ret) - return ret +// PrivateBlockChainAPI provides an API to access the blockchain. +// It offers only methods that operate on private data that is freely available to anyone. +type PrivateBlockChainAPI struct { + b Backend +} + +// NewPrivateBlockChainAPI creates a new blockchain API. +func NewPrivateBlockChainAPI(b Backend) *PrivateBlockChainAPI { + return &PrivateBlockChainAPI{b} } -func (s *PublicBlockChainAPI) SetStatePruning(enable bool) types.BlockState { +// SetStatePruning start blockchain state prune +func (s *PrivateBlockChainAPI) SetStatePruning(enable bool) types.BlockState { prestatus, number := s.b.SetStatePruning(enable) return types.BlockState{PreStatePruning: prestatus, CurrentNumber: number} } diff --git a/rpcapi/fee.go b/rpcapi/fee.go index 4b9b6450..73d8f16a 100644 --- a/rpcapi/fee.go +++ b/rpcapi/fee.go @@ -18,26 +18,24 @@ package rpcapi import ( "context" - "fmt" "github.com/fractalplatform/fractal/feemanager" + "github.com/fractalplatform/fractal/params" ) type FeeAPI struct { b Backend } -const ( - MaxIDCount = uint64(1000) -) - func NewFeeAPI(b Backend) *FeeAPI { return &FeeAPI{b} } //GetObjectFeeByName get object fee by name -func (aapi *FeeAPI) GetObjectFeeByName(ctx context.Context, objectName string, objectType uint64) (*feemanager.ObjectFee, error) { - fm, err := aapi.b.GetFeeManager() +//objectName: Asset Name, Contract Name, Coinbase Name +//objectType: Asset Type(0),Contract Type(1),Coinbase Type(2) +func (fapi *FeeAPI) GetObjectFeeByName(ctx context.Context, objectName string, objectType uint64) (*feemanager.ObjectFee, error) { + fm, err := fapi.b.GetFeeManager() if err != nil { return nil, err } @@ -45,19 +43,29 @@ func (aapi *FeeAPI) GetObjectFeeByName(ctx context.Context, objectName string, o return fm.GetObjectFeeByName(objectName, objectType) } -//GetObjectFeeByIDRange get object fee by name -func (aapi *FeeAPI) GetObjectFeeResult(ctx context.Context, startObjectFeeID uint64, count uint64) (*feemanager.ObjectFeeResult, error) { - if count > MaxIDCount { - return nil, fmt.Errorf("count can not over %d", MaxIDCount) +//GetObjectFeeResult get object fee infomation +//startObjectFeeID: object fee id, start from 1 +//count: The count of results obtained at one time, If it's more than 1,000, it's 1,000 +//time: snapshot time +func (fapi *FeeAPI) GetObjectFeeResult(ctx context.Context, startObjectFeeID uint64, count uint64, time uint64) (*feemanager.ObjectFeeResult, error) { + var fm *feemanager.FeeManager + var err error + var bContinue bool + + if count > params.MaxFeeResultCount { + count = params.MaxFeeResultCount } - fm, err := aapi.b.GetFeeManager() + if time == 0 { + fm, err = fapi.b.GetFeeManager() + } else { + fm, err = fapi.b.GetFeeManagerByTime(time) + } if err != nil { return nil, err } feeCounter, err := fm.GetFeeCounter() - if err != nil { return nil, err } @@ -66,70 +74,24 @@ func (aapi *FeeAPI) GetObjectFeeResult(ctx context.Context, startObjectFeeID uin return nil, nil } - objectFeeResult := &feemanager.ObjectFeeResult{Continue: false, - ObjectFees: make([]*feemanager.ObjectFee, 0)} - for index := uint64(0); index < count; index++ { - objectFeeID := startObjectFeeID + index - - if objectFeeID <= feeCounter { - objectFee, err := fm.GetObjectFeeByID(objectFeeID) - - if err != nil { - return nil, err - } - if objectFee != nil { - objectFeeResult.ObjectFees = append(objectFeeResult.ObjectFees, objectFee) - } - } - } - - if (startObjectFeeID + count) <= feeCounter { - objectFeeResult.Continue = true + if count <= (feeCounter - startObjectFeeID) { + bContinue = true + } else { + count = feeCounter - startObjectFeeID + 1 + bContinue = false } - return objectFeeResult, nil -} - -//GetFeeManagerByTime -func (aapi *FeeAPI) GetObjectFeeResultByTime(ctx context.Context, time uint64, startObjectFeeID uint64, count uint64) (*feemanager.ObjectFeeResult, error) { - if count > MaxIDCount { - return nil, fmt.Errorf("count can not over %d", MaxIDCount) - } - - fm, err := aapi.b.GetFeeManagerByTime(time) - if err != nil { - return nil, err - } - - feeCounter, err := fm.GetFeeCounter() - - if err != nil { - return nil, err - } - - if feeCounter == 0 || feeCounter < startObjectFeeID { - return nil, nil - } - - objectFeeResult := &feemanager.ObjectFeeResult{Continue: false, - ObjectFees: make([]*feemanager.ObjectFee, 0)} + objectFeeResult := &feemanager.ObjectFeeResult{Continue: bContinue, + ObjectFees: make([]*feemanager.ObjectFee, 0, count)} for index := uint64(0); index < count; index++ { objectFeeID := startObjectFeeID + index - - if objectFeeID <= feeCounter { - objectFee, err := fm.GetObjectFeeByID(objectFeeID) - - if err != nil { - return nil, err - } - if objectFee != nil { - objectFeeResult.ObjectFees = append(objectFeeResult.ObjectFees, objectFee) - } + objectFee, err := fm.GetObjectFeeByID(objectFeeID) + if err != nil { + return nil, err + } + if objectFee != nil { + objectFeeResult.ObjectFees = append(objectFeeResult.ObjectFees, objectFee) } - } - - if (startObjectFeeID + count) <= feeCounter { - objectFeeResult.Continue = true } return objectFeeResult, nil diff --git a/rpcapi/p2p.go b/rpcapi/p2p.go index bc7ce531..ea0d7dad 100644 --- a/rpcapi/p2p.go +++ b/rpcapi/p2p.go @@ -136,6 +136,11 @@ func (api *PrivateP2pAPI) AddBadNode(url string) error { return api.b.AddBadNode(url) } +// RemoveBadNode remove a bad node +func (api *PrivateP2pAPI) RemoveBadNode(url string) error { + return api.b.RemoveBadNode(url) +} + // SelfNode return self enode url func (api *PrivateP2pAPI) SelfNode() string { return api.b.SelfNode() diff --git a/rpcapi/txpool.go b/rpcapi/txpool.go index d36bea81..dc217311 100644 --- a/rpcapi/txpool.go +++ b/rpcapi/txpool.go @@ -2,16 +2,16 @@ // This file is part of the fractal project. // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by +// it under the terms of the GNU General Private License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// GNU General Private License for more details. // -// You should have received a copy of the GNU General Public License +// You should have received a copy of the GNU General Private License // along with this program. If not, see . package rpcapi @@ -24,18 +24,18 @@ import ( "github.com/fractalplatform/fractal/types" ) -// PublicTxPoolAPI offers and API for the transaction pool. It only operates on data that is non confidential. -type PublicTxPoolAPI struct { +// PrivateTxPoolAPI offers and API for the transaction pool. It only operates on data that is non confidential. +type PrivateTxPoolAPI struct { b Backend } -// NewPublicTxPoolAPI creates a new tx pool service that gives information about the transaction pool. -func NewPublicTxPoolAPI(b Backend) *PublicTxPoolAPI { - return &PublicTxPoolAPI{b} +// NewPrivateTxPoolAPI creates a new tx pool service that gives information about the transaction pool. +func NewPrivateTxPoolAPI(b Backend) *PrivateTxPoolAPI { + return &PrivateTxPoolAPI{b} } // Status returns the number of pending and queued transaction in the pool. -func (s *PublicTxPoolAPI) Status() map[string]int { +func (s *PrivateTxPoolAPI) Status() map[string]int { pending, queue := s.b.Stats() return map[string]int{ "pending": pending, @@ -44,13 +44,12 @@ func (s *PublicTxPoolAPI) Status() map[string]int { } // Content returns the transactions contained within the transaction pool. -func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*types.RPCTransaction { +func (s *PrivateTxPoolAPI) Content() map[string]map[string]map[string]*types.RPCTransaction { content := map[string]map[string]map[string]*types.RPCTransaction{ "pending": make(map[string]map[string]*types.RPCTransaction), "queued": make(map[string]map[string]*types.RPCTransaction), } pending, queue := s.b.TxPoolContent() - // Flatten the pending transactions for account, txs := range pending { dump := make(map[string]*types.RPCTransaction) @@ -70,7 +69,7 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*types.RPCT return content } -// SetGasPrice set gas price limit -func (s *PublicTxPoolAPI) SetGasPrice(gasprice *big.Int) bool { - return s.b.SetGasPrice(gasprice) +// SetGasPrice set gas price +func (s *PrivateTxPoolAPI) SetGasPrice(gasprice uint64) bool { + return s.b.SetGasPrice(big.NewInt(int64(gasprice))) } diff --git a/rpcapi/utils.go b/rpcapi/utils.go index ed1899d1..dd8f2c4f 100644 --- a/rpcapi/utils.go +++ b/rpcapi/utils.go @@ -41,20 +41,22 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c func RPCMarshalBlock(chainID *big.Int, b *types.Block, inclTx bool, fullTx bool) map[string]interface{} { head := b.Header() // copies the header once fields := map[string]interface{}{ - "number": head.Number, - "hash": b.Hash(), - "parentHash": head.ParentHash, - "logsBloom": head.Bloom, - "stateRoot": head.Root, - "miner": head.Coinbase, - "difficulty": head.Difficulty, - "extraData": hexutil.Bytes(head.Extra), - "size": b.Size(), - "gasLimit": head.GasLimit, - "gasUsed": head.GasUsed, - "timestamp": head.Time, - "transactionsRoot": head.TxsRoot, - "receiptsRoot": head.ReceiptsRoot, + "number": head.Number, + "hash": b.Hash(), + "proposedIrreversible": head.ProposedIrreversible, + "parentHash": head.ParentHash, + "logsBloom": head.Bloom, + "stateRoot": head.Root, + "miner": head.Coinbase, + "difficulty": head.Difficulty, + "extraData": hexutil.Bytes(head.Extra), + "size": b.Size(), + "gasLimit": head.GasLimit, + "gasUsed": head.GasUsed, + "timestamp": head.Time, + "transactionsRoot": head.TxsRoot, + "receiptsRoot": head.ReceiptsRoot, + "forkID": head.ForkID, } if inclTx { diff --git a/sdk/api_dpos.go b/sdk/api_dpos.go index 335821a3..67e28e78 100644 --- a/sdk/api_dpos.go +++ b/sdk/api_dpos.go @@ -51,10 +51,10 @@ func (api *API) DposVotersByCandidate(candidate string, detail bool) (map[string return info, err } -// DposVotersByCandidateByHeight get voters info of candidate -func (api *API) DposVotersByCandidateByHeight(height uint64, candidate string, detail bool) (map[string]interface{}, error) { +// DposVotersByCandidateByNumber get voters info of candidate +func (api *API) DposVotersByCandidateByNumber(number uint64, candidate string, detail bool) (map[string]interface{}, error) { info := map[string]interface{}{} - err := api.client.Call(&info, "dpos_votersByCandidateByHeight", height, candidate, detail) + err := api.client.Call(&info, "dpos_votersByCandidateByNumber", number, candidate, detail) return info, err } @@ -65,10 +65,10 @@ func (api *API) DposVotersByVoter(voter string, detail bool) (interface{}, error return info, err } -// DposVotersByVoterByHeight get voters info of voter -func (api *API) DposVotersByVoterByHeight(height uint64, voter string, detail bool) (interface{}, error) { +// DposVotersByVoterByNumber get voters info of voter +func (api *API) DposVotersByVoterByNumber(number uint64, voter string, detail bool) (interface{}, error) { info := map[string]interface{}{} - err := api.client.Call(&info, "dpos_votersByVoterByHeight", height, voter, detail) + err := api.client.Call(&info, "dpos_votersByVoterByNumber", number, voter, detail) return info, err } @@ -79,10 +79,10 @@ func (api *API) DposAvailableStake(name string) (map[string]interface{}, error) return info, err } -// DposAvailableStakeByHeight state info -func (api *API) DposAvailableStakeByHeight(height uint64, name string) (map[string]interface{}, error) { +// DposAvailableStakeByNumber state info +func (api *API) DposAvailableStakeByNumber(number uint64, name string) (map[string]interface{}, error) { info := map[string]interface{}{} - err := api.client.Call(&info, "dpos_availableStakeByHeight", height, name) + err := api.client.Call(&info, "dpos_availableStakeByNumber", number, name) return info, err } @@ -93,10 +93,10 @@ func (api *API) DposValidCandidates() (map[string]interface{}, error) { return info, err } -// DposValidCandidatesByHeight dpos candidate info -func (api *API) DposValidCandidatesByHeight(height uint64) (map[string]interface{}, error) { +// DposValidCandidatesByNumber dpos candidate info +func (api *API) DposValidCandidatesByNumber(number uint64) (map[string]interface{}, error) { info := map[string]interface{}{} - err := api.client.Call(&info, "dpos_validCandidatesByHeight", height) + err := api.client.Call(&info, "dpos_validCandidatesByNumber", number) return info, err } @@ -107,10 +107,10 @@ func (api *API) DposNextValidCandidates() (map[string]interface{}, error) { return info, err } -// DposNextValidCandidatesByHeight dpos candidate info -func (api *API) DposNextValidCandidatesByHeight(height uint64) (map[string]interface{}, error) { +// DposNextValidCandidatesByNumber dpos candidate info +func (api *API) DposNextValidCandidatesByNumber(number uint64) (map[string]interface{}, error) { info := map[string]interface{}{} - err := api.client.Call(&info, "dpos_nextValidCandidatesByHeight", height) + err := api.client.Call(&info, "dpos_nextValidCandidatesByNumber", number) return info, err } @@ -121,9 +121,9 @@ func (api *API) DposSnapShotTime() (map[string]interface{}, error) { return info, err } -// DposSnapShotTimeByHeight dpos snapshot time info -func (api *API) DposSnapShotTimeByHeight(height uint64) (map[string]interface{}, error) { +// DposSnapShotTimeByNumber dpos snapshot time info +func (api *API) DposSnapShotTimeByNumber(number uint64) (map[string]interface{}, error) { info := map[string]interface{}{} - err := api.client.Call(&info, "dpos_snapShotTimeByHeight", height) + err := api.client.Call(&info, "dpos_snapShotTimeByNumber", number) return info, err } diff --git a/sdk/api_dpos_test.go b/sdk/api_dpos_test.go index 558195a9..a032f31f 100644 --- a/sdk/api_dpos_test.go +++ b/sdk/api_dpos_test.go @@ -64,10 +64,10 @@ func TestDposVotersByVoter(t *testing.T) { }) } -func TestDposVotersByVoterByHeight(t *testing.T) { - Convey("dpos_votersByVoterByHeight", t, func() { +func TestDposVotersByVoterByNumber(t *testing.T) { + Convey("dpos_votersByVoterByNumber", t, func() { api := NewAPI(rpchost) - info, err := api.DposVotersByVoterByHeight(0, systemaccount, true) + info, err := api.DposVotersByVoterByNumber(0, systemaccount, true) So(err, ShouldBeNil) _ = info //So(info, ShouldNotBeEmpty) @@ -82,10 +82,10 @@ func TestDposVotersByCandidate(t *testing.T) { //So(info, ShouldNotBeEmpty) }) } -func TestDposVotersByCandidateByHeight(t *testing.T) { - Convey("dpos_votersByCandidateByHeight", t, func() { +func TestDposVotersByCandidateByNumber(t *testing.T) { + Convey("dpos_votersByCandidateByNumber", t, func() { api := NewAPI(rpchost) - info, err := api.DposVotersByCandidateByHeight(0, systemaccount, true) + info, err := api.DposVotersByCandidateByNumber(0, systemaccount, true) So(err, ShouldBeNil) _ = info //So(info, ShouldNotBeEmpty) @@ -100,10 +100,10 @@ func TestDposAvailableStake(t *testing.T) { }) } -func TestDposAvailableStakebyHeight(t *testing.T) { - SkipConvey("dpos_availableStakeByHeight", t, func() { +func TestDposAvailableStakebyNumber(t *testing.T) { + SkipConvey("dpos_availableStakeByNumber", t, func() { api := NewAPI(rpchost) - info, err := api.DposAvailableStakeByHeight(0, systemaccount) + info, err := api.DposAvailableStakeByNumber(0, systemaccount) So(err, ShouldBeNil) So(info, ShouldNotBeEmpty) }) @@ -117,10 +117,10 @@ func TestDposValidCandidates(t *testing.T) { So(info, ShouldNotBeEmpty) }) } -func TestDposValidCandidatesByHeight(t *testing.T) { - Convey("dpos_validCandidatesByHeight", t, func() { +func TestDposValidCandidatesByNumber(t *testing.T) { + Convey("dpos_validCandidatesByNumber", t, func() { api := NewAPI(rpchost) - info, err := api.DposValidCandidatesByHeight(0) + info, err := api.DposValidCandidatesByNumber(0) So(err, ShouldBeNil) So(info, ShouldNotBeEmpty) }) @@ -134,10 +134,10 @@ func TestDposNextValidCandidates(t *testing.T) { So(info, ShouldNotBeEmpty) }) } -func TestDposNextValidCandidatesByHeight(t *testing.T) { - Convey("dpos_nextValidCandidatesByHeight", t, func() { +func TestDposNextValidCandidatesByNumber(t *testing.T) { + Convey("dpos_nextValidCandidatesByNumber", t, func() { api := NewAPI(rpchost) - info, err := api.DposNextValidCandidatesByHeight(0) + info, err := api.DposNextValidCandidatesByNumber(0) So(err, ShouldBeNil) So(info, ShouldNotBeEmpty) }) @@ -151,10 +151,10 @@ func TestDposSnapShotTime(t *testing.T) { So(info, ShouldNotBeEmpty) }) } -func TestDposSnapShotTimeByHeight(t *testing.T) { - Convey("dpos_snapShotTimeByHeight", t, func() { +func TestDposSnapShotTimeByNumber(t *testing.T) { + Convey("dpos_snapShotTimeByNumber", t, func() { api := NewAPI(rpchost) - info, err := api.DposSnapShotTimeByHeight(0) + info, err := api.DposSnapShotTimeByNumber(0) So(err, ShouldBeNil) So(info, ShouldNotBeEmpty) }) diff --git a/sdk/api_fee.go b/sdk/api_fee.go index b0409acd..415268ba 100644 --- a/sdk/api_fee.go +++ b/sdk/api_fee.go @@ -25,14 +25,9 @@ func (api *API) FeeInfo(name string, objectType uint64) (map[string]interface{}, return feeInfo, err } -func (api *API) FeeInfoByID(startFeeID uint64, count uint64) (*feemanager.ObjectFeeResult, error) { +//FeeInfoByID get object fee by id +func (api *API) FeeInfoByID(startFeeID uint64, count uint64, time uint64) (*feemanager.ObjectFeeResult, error) { feeInfo := &feemanager.ObjectFeeResult{} - err := api.client.Call(feeInfo, "fee_getObjectFeeResult", startFeeID, count) - return feeInfo, err -} - -func (api *API) FeeInfoByTime(time uint64, startFeeID uint64, count uint64) (*feemanager.ObjectFeeResult, error) { - feeInfo := &feemanager.ObjectFeeResult{} - err := api.client.Call(feeInfo, "fee_getObjectFeeResultByTime", time, startFeeID, count) + err := api.client.Call(feeInfo, "fee_getObjectFeeResult", startFeeID, count, time) return feeInfo, err } diff --git a/sdk/api_fee_test.go b/sdk/api_fee_test.go index 20cd5aaf..bd270ced 100644 --- a/sdk/api_fee_test.go +++ b/sdk/api_fee_test.go @@ -37,7 +37,7 @@ func TestFeeInfo(t *testing.T) { func TestFeeInfoByID(t *testing.T) { Convey("fee_getObjectFeeResult", t, func() { api := NewAPI(rpchost) - objFee, err := api.FeeInfoByID(1, 1) + objFee, err := api.FeeInfoByID(1, 1, 0) So(err, ShouldBeNil) So(objFee, ShouldNotBeNil) }) diff --git a/sdk/api_miner.go b/sdk/api_miner.go deleted file mode 100644 index eefde0ce..00000000 --- a/sdk/api_miner.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package sdk - -// MinerStart start -func (api *API) MinerStart() (bool, error) { - ret := false - err := api.client.Call(&ret, "miner_start") - return ret, err -} - -// MinerStop stop -func (api *API) MinerStop() (bool, error) { - ret := false - err := api.client.Call(&ret, "miner_stop") - return ret, err -} - -// MinerMining mining -func (api *API) MinerMining() (bool, error) { - ret := false - err := api.client.Call(&ret, "miner_mining") - return ret, err -} - -// MinerSetExtra extra -func (api *API) MinerSetExtra(extra []byte) (bool, error) { - ret := true - err := api.client.Call(&ret, "miner_setExtra", extra) - return ret, err -} - -// MinerSetCoinbase coinbase -func (api *API) MinerSetCoinbase(name string, privKeys []string) (bool, error) { - ret := true - err := api.client.Call(&ret, "miner_setCoinbase", name, privKeys) - return ret, err -} diff --git a/sdk/api_miner_test.go b/sdk/api_miner_test.go deleted file mode 100644 index 75ee68ec..00000000 --- a/sdk/api_miner_test.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package sdk - -import ( - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func TestMinerMining(t *testing.T) { - Convey("miner_mining", t, func() { - api := NewAPI(rpchost) - _, err := api.MinerMining() - So(err, ShouldBeNil) - }) -} -func TestMinerStart(t *testing.T) { - Convey("miner_start", t, func() { - api := NewAPI(rpchost) - mining, _ := api.MinerMining() - ret, err := api.MinerStart() - So(err, ShouldBeNil) - if mining { - So(ret, ShouldBeFalse) - } else { - So(ret, ShouldBeTrue) - } - }) -} -func TestMinerStop(t *testing.T) { - Convey("miner_stop", t, func() { - api := NewAPI(rpchost) - mining, _ := api.MinerMining() - ret, err := api.MinerStop() - So(err, ShouldBeNil) - if mining { - So(ret, ShouldBeTrue) - } else { - So(ret, ShouldBeFalse) - } - }) -} -func TestMinerSetExtra(t *testing.T) { - Convey("miner_setExtra", t, func() { - api := NewAPI(rpchost) - ret, err := api.MinerSetExtra([]byte("testextra")) - So(err, ShouldBeNil) - So(ret, ShouldBeTrue) - }) -} -func TestMinerSetCoinbase(t *testing.T) { - Convey("miner_setCoinbase", t, func() { - api := NewAPI(rpchost) - ret, err := api.MinerSetCoinbase(systemaccount, []string{systemprivkey}) - So(err, ShouldBeNil) - So(ret, ShouldBeTrue) - }) -} -func TestMinerSetExtraTooLong(t *testing.T) { - Convey("miner_setExtra", t, func() { - api := NewAPI(rpchost) - ret, err := api.MinerSetExtra([]byte("testextratestextratestextratestextratestextratestextra")) - So(err, ShouldNotBeNil) - So(ret, ShouldBeTrue) - }) -} diff --git a/test/common/blockchain.go b/test/common/blockchain.go index fcd7215d..d103a0f9 100644 --- a/test/common/blockchain.go +++ b/test/common/blockchain.go @@ -29,9 +29,9 @@ func GetCurrentBlock(fullTx bool) (map[string]interface{}, error) { } //GetBlockByNumber returns the requested block. -func GetBlockByNumber(height uint64, fullTx bool) (map[string]interface{}, error) { +func GetBlockByNumber(number uint64, fullTx bool) (map[string]interface{}, error) { result := make(map[string]interface{}) - err := ClientCall("ft_getBlockByNumber", &result, height, fullTx) + err := ClientCall("ft_getBlockByNumber", &result, number, fullTx) return result, err } diff --git a/test/common/utils.go b/test/common/utils.go index f62874c0..37dac888 100644 --- a/test/common/utils.go +++ b/test/common/utils.go @@ -78,14 +78,14 @@ func ClientCall(method string, result interface{}, args ...interface{}) error { return err } -// GetNonce get nonce by address and block number. +// GetNonce get nonce by name and block number. func GetNonce(accountname common.Name) (uint64, error) { nonce := new(uint64) err := ClientCall("account_getNonce", nonce, accountname) return *nonce, err } -// GetAccountBalanceByID get balance by address ,assetID and number. +// GetAccountBalanceByID get balance by name ,assetID and number. func GetAccountBalanceByID(accountName common.Name, assetID uint64) (*big.Int, error) { balance := big.NewInt(0) err := ClientCall("account_getAccountBalanceByID", balance, accountName, assetID, 1) @@ -132,15 +132,9 @@ func GetObjectFeeByName(objectName common.Name, objectType uint64) (*feemanager. return objectFee, err } -func GetObjectFeeResult(startObjectFeeID uint64, count uint64) (*feemanager.ObjectFeeResult, error) { +func GetObjectFeeResult(startObjectFeeID uint64, count uint64, time uint64) (*feemanager.ObjectFeeResult, error) { objectFeeResult := &feemanager.ObjectFeeResult{} - err := ClientCall("fee_getObjectFeeResult", objectFeeResult, startObjectFeeID, count) - return objectFeeResult, err -} - -func GetObjectFeeResultByTime(time uint64, startObjectFeeID uint64, count uint64) (*feemanager.ObjectFeeResult, error) { - objectFeeResult := &feemanager.ObjectFeeResult{} - err := ClientCall("fee_getObjectFeeResultByTime", objectFeeResult, time, startObjectFeeID, count) + err := ClientCall("fee_getObjectFeeResult", objectFeeResult, startObjectFeeID, count, time) return objectFeeResult, err } diff --git a/test/fee/transaction_contract.go b/test/fee/transaction_contract.go index fc0a6ed2..47cae453 100644 --- a/test/fee/transaction_contract.go +++ b/test/fee/transaction_contract.go @@ -182,7 +182,7 @@ func generateAccount() { contract_b = common.Name(fmt.Sprintf("contractb%d", nonce)) key := types.MakeKeyPair(privateKey, []uint64{0}) - acct := &accountmanager.AccountAction{ + acct := &accountmanager.CreateAccountAction{ AccountName: normal_a, Founder: normal_a, PublicKey: pubKey_a, @@ -190,7 +190,7 @@ func generateAccount() { b, _ := rlp.EncodeToBytes(acct) sendTransferTx(types.CreateAccount, adminAccount, accAccount, nonce, assetID, balance, b, []*types.KeyPair{key}) - acct = &accountmanager.AccountAction{ + acct = &accountmanager.CreateAccountAction{ AccountName: normal_b, Founder: normal_b, PublicKey: pubKey_b, @@ -198,7 +198,7 @@ func generateAccount() { b, _ = rlp.EncodeToBytes(acct) sendTransferTx(types.CreateAccount, adminAccount, accAccount, nonce+1, assetID, balance, b, []*types.KeyPair{key}) - acct = &accountmanager.AccountAction{ + acct = &accountmanager.CreateAccountAction{ AccountName: contract_a, Founder: contract_a, PublicKey: pubKey_c, @@ -206,7 +206,7 @@ func generateAccount() { b, _ = rlp.EncodeToBytes(acct) sendTransferTx(types.CreateAccount, adminAccount, accAccount, nonce+2, assetID, big.NewInt(1000000000000), b, []*types.KeyPair{key}) - acct = &accountmanager.AccountAction{ + acct = &accountmanager.CreateAccountAction{ AccountName: contract_b, Founder: contract_b, PublicKey: pubKey_c, diff --git a/txpool/accountset.go b/txpool/accountset.go index e84d4b57..70747917 100644 --- a/txpool/accountset.go +++ b/txpool/accountset.go @@ -21,11 +21,9 @@ import ( "github.com/fractalplatform/fractal/types" ) -// accountSet is simply a set of name to check for existence, and a signer -// capable of deriving name from transactions. +// accountSet is simply a set of name to check for existence type accountSet struct { accounts map[common.Name]struct{} - signer types.Signer } // newAccountSet creates a new name set with an associated signer for sender @@ -33,7 +31,6 @@ type accountSet struct { func newAccountSet(signer types.Signer) *accountSet { return &accountSet{ accounts: make(map[common.Name]struct{}), - signer: signer, } } @@ -43,9 +40,8 @@ func (as *accountSet) contains(name common.Name) bool { return exist } -// containsTx checks if the sender of a given tx is within the set. If the sender -// cannot be derived, this method returns false. -func (as *accountSet) containsTx(tx *types.Transaction) bool { +// containsName checks if the sender of a given tx is within the set. +func (as *accountSet) containsName(tx *types.Transaction) bool { // todo every action return as.contains(tx.GetActions()[0].Sender()) } diff --git a/txpool/error.go b/txpool/error.go index 58225638..abb05b26 100644 --- a/txpool/error.go +++ b/txpool/error.go @@ -55,15 +55,4 @@ var ( // ErrNegativeValue is a sanity error to ensure noone is able to specify a // transaction with a negative value. ErrNegativeValue = errors.New("negative value") - - // ErrOversizedData is returned if the input data of a transaction is greater - // than some meaningful limit a user might use. This is not a consensus error - // making the transaction invalid, rather a DOS protection. - ErrOversizedData = errors.New("oversized data") - - // ErrEmptyActions transaction no actions - ErrEmptyActions = errors.New("transaction no actions") - - // ErrInvalidAction action have field invalid - ErrInvalidAction = errors.New("action field invalid") ) diff --git a/txpool/pricedlsit.go b/txpool/pricedlsit.go index edad2899..97f67e9a 100644 --- a/txpool/pricedlsit.go +++ b/txpool/pricedlsit.go @@ -85,7 +85,7 @@ func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) []*types.Trans break } // Non stale transaction found, discard unless local - if local.containsTx(tx) { + if local.containsName(tx) { save = append(save, tx) } else { drop = append(drop, tx) @@ -101,7 +101,7 @@ func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) []*types.Trans // lowest priced transaction currently being tracked. func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) bool { // Local transactions cannot be underpriced - if local.containsTx(tx) { + if local.containsName(tx) { return false } // Discard stale price points if found at the heap start @@ -137,7 +137,7 @@ func (l *txPricedList) Discard(count int, local *accountSet) []*types.Transactio continue } // Non stale transaction found, discard unless local - if local.containsTx(tx) { + if local.containsName(tx) { save = append(save, tx) } else { drop = append(drop, tx) diff --git a/txpool/priceheap_test.go b/txpool/priceheap_test.go index ea8498ae..1a30aa1d 100644 --- a/txpool/priceheap_test.go +++ b/txpool/priceheap_test.go @@ -17,8 +17,8 @@ package txpool import ( + "container/heap" "math/big" - "sort" "testing" "github.com/fractalplatform/fractal/common" @@ -41,33 +41,34 @@ func getPriceTx(price *big.Int, nonce uint64) *types.Transaction { } func TestPriceHeap(t *testing.T) { - var ph priceHeap + var ph = new(priceHeap) + heap.Init(ph) + txs := []*types.Transaction{ getPriceTx(big.NewInt(200), 2), getPriceTx(big.NewInt(200), 1), - getPriceTx(big.NewInt(400), 4), + getPriceTx(big.NewInt(500), 4), getPriceTx(big.NewInt(100), 3), - } - for _, v := range txs { - ph.Push(v) - } - for i := 0; i < 4; i++ { - assert.Equal(t, txs[3-i], ph.Pop().(*types.Transaction)) + getPriceTx(big.NewInt(500), 5), + getPriceTx(big.NewInt(100), 6), } //test sort,first sort by price,if the price is equal,sort by nonce,high nonce is worse. sortTxs := []*types.Transaction{ - getPriceTx(big.NewInt(400), 4), - getPriceTx(big.NewInt(200), 1), - getPriceTx(big.NewInt(200), 2), + getPriceTx(big.NewInt(100), 6), getPriceTx(big.NewInt(100), 3), + getPriceTx(big.NewInt(200), 2), + getPriceTx(big.NewInt(200), 1), + getPriceTx(big.NewInt(500), 5), + getPriceTx(big.NewInt(500), 4), } for _, v := range txs { - ph.Push(v) + heap.Push(ph, v) } - sort.Sort(ph) - for i := 0; i < 4; i++ { - assert.Equal(t, sortTxs[i].Hash(), ph.Pop().(*types.Transaction).Hash()) + + for i := 0; i < len(txs); i++ { + tx := heap.Pop(ph).(*types.Transaction) + assert.Equal(t, sortTxs[i].Hash(), tx.Hash()) } } diff --git a/txpool/test_utils.go b/txpool/test_utils.go index de629e27..4451f43b 100644 --- a/txpool/test_utils.go +++ b/txpool/test_utils.go @@ -74,7 +74,9 @@ func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { } func (bc *testBlockChain) Config() *params.ChainConfig { - return nil + cfg := params.DefaultChainconfig + cfg.SysTokenID = 0 + return cfg } func transaction(nonce uint64, from, to common.Name, gaslimit uint64, key *ecdsa.PrivateKey) *types.Transaction { @@ -129,7 +131,7 @@ func validateTxPoolInternals(pool *TxPool) error { } // Ensure the next nonce to assign is the correct one - for addr, list := range pool.pending { + for name, list := range pool.pending { // Find the last transaction var last uint64 for nonce := range list.txs.items { @@ -138,7 +140,7 @@ func validateTxPoolInternals(pool *TxPool) error { } } - nonce, err := pool.pendingAccountManager.GetNonce(addr) + nonce, err := pool.pendingAccountManager.GetNonce(name) if err != nil { return err } @@ -156,7 +158,7 @@ func validateEvents(events chan *event.Event, count int) error { for len(received) < count { select { case ev := <-events: - if ev.Typecode == event.TxEv { + if ev.Typecode == event.NewTxs { received = append(received, ev.Data.([]*types.Transaction)...) } case <-time.After(time.Second): diff --git a/txpool/txpool.go b/txpool/txpool.go index 3dd08ffe..1348d260 100644 --- a/txpool/txpool.go +++ b/txpool/txpool.go @@ -181,14 +181,14 @@ func (tp *TxPool) loop() { // Handle inactive account transaction eviction case <-evict.C: tp.mu.Lock() - for addr := range tp.queue { + for name := range tp.queue { // Skip local transactions from the eviction mechanism - if tp.locals.contains(addr) { + if tp.locals.contains(name) { continue } // Any non-locals old enough should be removed - if time.Since(tp.beats[addr]) > tp.config.Lifetime { - for _, tx := range tp.queue[addr].Flatten() { + if time.Since(tp.beats[name]) > tp.config.Lifetime { + for _, tx := range tp.queue[name].Flatten() { tp.removeTx(tx.Hash(), true) } } @@ -301,7 +301,6 @@ func (tp *TxPool) reset(oldHead, newHead *types.Header) { txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway // todo change transaction action nonce if err := tp.pendingAccountManager.SetNonce(name, txs[len(txs)-1].GetActions()[0].Nonce()+1); err != nil { - if err != am.ErrAccountIsDestroy { log.Error("Failed to pendingAccountManager SetNonce", "err", err) return @@ -312,6 +311,7 @@ func (tp *TxPool) reset(oldHead, newHead *types.Header) { log.Debug("Remove all destory account ", "name", name) } } + // Check the queue and move transactions over to the pending if possible // or remove those that have become invalid tp.promoteExecutables(nil) @@ -386,12 +386,12 @@ func (tp *TxPool) Content() (map[common.Name][]*types.Transaction, map[common.Na defer tp.mu.Unlock() pending := make(map[common.Name][]*types.Transaction) - for addr, list := range tp.pending { - pending[addr] = list.Flatten() + for name, list := range tp.pending { + pending[name] = list.Flatten() } queued := make(map[common.Name][]*types.Transaction) - for addr, list := range tp.queue { - queued[addr] = list.Flatten() + for name, list := range tp.queue { + queued[name] = list.Flatten() } return pending, queued } @@ -402,10 +402,9 @@ func (tp *TxPool) Content() (map[common.Name][]*types.Transaction, map[common.Na func (tp *TxPool) Pending() (map[common.Name][]*types.Transaction, error) { tp.mu.Lock() defer tp.mu.Unlock() - pending := make(map[common.Name][]*types.Transaction) - for addr, list := range tp.pending { - pending[addr] = list.Flatten() + for name, list := range tp.pending { + pending[name] = list.Flatten() } return pending, nil } @@ -415,12 +414,12 @@ func (tp *TxPool) Pending() (map[common.Name][]*types.Transaction, error) { // freely modified by calling code. func (tp *TxPool) local() map[common.Name][]*types.Transaction { txs := make(map[common.Name][]*types.Transaction) - for addr := range tp.locals.accounts { - if list := tp.pending[addr]; list != nil { - txs[addr] = append(txs[addr], list.Flatten()...) + for name := range tp.locals.accounts { + if list := tp.pending[name]; list != nil { + txs[name] = append(txs[name], list.Flatten()...) } - if list := tp.queue[addr]; list != nil { - txs[addr] = append(txs[addr], list.Flatten()...) + if list := tp.queue[name]; list != nil { + txs[name] = append(txs[name], list.Flatten()...) } } return txs @@ -472,10 +471,6 @@ func (tp *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrInsufficientFundsForValue } - if err := action.CheckValid(tp.chain.Config()); err != nil { - return err - } - intrGas, err := IntrinsicGas(action) if err != nil { return err @@ -488,15 +483,6 @@ func (tp *TxPool) validateTx(tx *types.Transaction, local bool) error { } - // Heuristic limit, reject transactions over 32KB to prfeed DOS attacks - if tx.Size() > 32*1024 { - return ErrOversizedData - } - - if tp.config.GasAssetID != tx.GasAssetID() { - return fmt.Errorf("only support system asset %d as tx fee", tp.config.GasAssetID) - } - // Make sure the transaction is signed properly if err := tp.curAccountManager.RecoverTx(tp.signer, tx); err != nil { log.Error("account Manager reocver faild ", "err", err) @@ -519,7 +505,6 @@ func (tp *TxPool) validateTx(tx *types.Transaction, local bool) error { if tp.currentMaxGas < allgas { return ErrGasLimit } - return nil } @@ -572,7 +557,6 @@ func (tp *TxPool) add(tx *types.Transaction, local bool) (bool, error) { // We've directly injected a replacement transaction, notify subsystems events := []*event.Event{ - {Typecode: event.TxEv, Data: []*types.Transaction{tx}}, {Typecode: event.NewTxs, Data: []*types.Transaction{tx}}, } go event.SendEvents(events) @@ -584,7 +568,7 @@ func (tp *TxPool) add(tx *types.Transaction, local bool) (bool, error) { if err != nil { return false, err } - // Mark local addresses and journal local transactions + // Mark local names and journal local transactions if local { tp.locals.add(from) } @@ -600,7 +584,6 @@ func (tp *TxPool) add(tx *types.Transaction, local bool) (bool, error) { func (tp *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, error) { // Try to insert the transaction into the future queue from := tx.GetActions()[0].Sender() - if tp.queue[from] == nil { tp.queue[from] = newTxList(false) } @@ -699,10 +682,9 @@ func (tp *TxPool) AddRemotes(txs []*types.Transaction) []error { // addTx enqueues a single transaction into the pool if it is valid. func (tp *TxPool) addTx(tx *types.Transaction, local bool) error { - if len(tx.GetActions()) == 0 { - return ErrEmptyActions + if err := tx.Check(tp.chain.Config()); err != nil { + return err } - // Cache senders in transactions before obtaining lock for _, action := range tx.GetActions() { _, err := types.RecoverMultiKey(tp.signer, action, tx) @@ -736,8 +718,8 @@ func (tp *TxPool) addTxs(txs []*types.Transaction, local bool) []error { isErr bool ) for _, tx := range txs { - if len(tx.GetActions()) == 0 { - errs = append(errs, fmt.Errorf(ErrEmptyActions.Error()+" hash %v", tx.Hash())) + if err := tx.Check(tp.chain.Config()); err != nil { + errs = append(errs, fmt.Errorf(err.Error()+" hash %v", tx.Hash())) isErr = true continue } @@ -775,11 +757,11 @@ func (tp *TxPool) addTxsLocked(txs []*types.Transaction, local bool) []error { } // Only reprocess the internal state if something was actually added if len(dirty) > 0 { - addrs := make([]common.Name, 0, len(dirty)) - for addr := range dirty { - addrs = append(addrs, addr) + names := make([]common.Name, 0, len(dirty)) + for name := range dirty { + names = append(names, name) } - tp.promoteExecutables(addrs) + tp.promoteExecutables(names) } return errs } @@ -873,20 +855,20 @@ func (tp *TxPool) promoteExecutables(accounts []common.Name) { // Gather all the accounts potentially needing updates if accounts == nil { accounts = make([]common.Name, 0, len(tp.queue)) - for addr := range tp.queue { - accounts = append(accounts, addr) + for name := range tp.queue { + accounts = append(accounts, name) } } // Iterate over all accounts and promote any executable transactions - for _, addr := range accounts { - list := tp.queue[addr] + for _, name := range accounts { + list := tp.queue[name] if list == nil { continue // Just in case someone calls with a non existing account } // Drop all transactions that are deemed too old (low nonce) - nonce, err := tp.curAccountManager.GetNonce(addr) + nonce, err := tp.curAccountManager.GetNonce(name) if err != nil { - log.Error("promoteExecutables current account manager get nonce err", "name", addr, "err", err) + log.Error("promoteExecutables current account manager get nonce err", "name", name, "err", err) } for _, tx := range list.Forward(nonce) { hash := tx.Hash() @@ -896,9 +878,9 @@ func (tp *TxPool) promoteExecutables(accounts []common.Name) { } // Drop all transactions that are too costly (low balance or out of gas or no permissions) // todo assetID - balance, err := tp.curAccountManager.GetAccountBalanceByID(addr, tp.config.GasAssetID, 0) + balance, err := tp.curAccountManager.GetAccountBalanceByID(name, tp.config.GasAssetID, 0) if err != nil { - log.Error("promoteExecutables current account manager get balance err ", "name", addr, "assetID", tp.config.GasAssetID, "err", err) + log.Error("promoteExecutables current account manager get balance err ", "name", name, "assetID", tp.config.GasAssetID, "err", err) } drops, _ := list.Filter(balance, tp.currentMaxGas, tp.signer, tp.curAccountManager.GetAccountBalanceByID, tp.curAccountManager.RecoverTx) for _, tx := range drops { @@ -909,19 +891,19 @@ func (tp *TxPool) promoteExecutables(accounts []common.Name) { } // Gather all executable transactions and promote them - nonce, err = tp.pendingAccountManager.GetNonce(addr) + nonce, err = tp.pendingAccountManager.GetNonce(name) if err != nil && err != am.ErrAccountNotExist { - log.Error("promoteExecutables pending account manager get nonce err ", "name", addr, "err", err) + log.Error("promoteExecutables pending account manager get nonce err ", "name", name, "err", err) } for _, tx := range list.Ready(nonce) { hash := tx.Hash() - if tp.promoteTx(addr, hash, tx) { + if tp.promoteTx(name, hash, tx) { log.Trace("Promoting queued transaction", "hash", hash) promoted = append(promoted, tx) } } // Drop all transactions over the allowed limit - if !tp.locals.contains(addr) { + if !tp.locals.contains(name) { for _, tx := range list.Cap(int(tp.config.AccountQueue)) { hash := tx.Hash() tp.all.Remove(hash) @@ -931,14 +913,12 @@ func (tp *TxPool) promoteExecutables(accounts []common.Name) { } // Delete the entire queue entry if it became empty. if list.Empty() { - delete(tp.queue, addr) + delete(tp.queue, name) } } // Notify subsystem for new promoted transactions. if len(promoted) > 0 { - // go event.SendEvent(&event.Event{Typecode: event.TxEv, Data: promoted}) events := []*event.Event{ - {Typecode: event.TxEv, Data: promoted}, {Typecode: event.NewTxs, Data: promoted}, } go event.SendEvents(events) @@ -951,16 +931,16 @@ func (tp *TxPool) promoteExecutables(accounts []common.Name) { if pending > tp.config.GlobalSlots { // Assemble a spam order to penalize large transactors first spammers := prque.New() - for addr, list := range tp.pending { + for name, list := range tp.pending { // Only evict transactions from high rollers - if !tp.locals.contains(addr) && uint64(list.Len()) > tp.config.AccountSlots { - spammers.Push(addr, float32(list.Len())) + if !tp.locals.contains(name) && uint64(list.Len()) > tp.config.AccountSlots { + spammers.Push(name, float32(list.Len())) } } // Gradually drop transactions from offenders offenders := []common.Name{} for pending > tp.config.GlobalSlots && !spammers.Empty() { - // Retrieve the next offender if not local address + // Retrieve the next offender if not local name offender, _ := spammers.Pop() offenders = append(offenders, offender.(common.Name)) @@ -1000,8 +980,8 @@ func (tp *TxPool) promoteExecutables(accounts []common.Name) { // If still above threshold, reduce to limit or min allowance if pending > tp.config.GlobalSlots && len(offenders) > 0 { for pending > tp.config.GlobalSlots && uint64(tp.pending[offenders[len(offenders)-1]].Len()) > tp.config.AccountSlots { - for _, addr := range offenders { - list := tp.pending[addr] + for _, name := range offenders { + list := tp.pending[name] for _, tx := range list.Cap(list.Len() - 1) { // Drop the transaction from the global pools too hash := tx.Hash() @@ -1009,12 +989,12 @@ func (tp *TxPool) promoteExecutables(accounts []common.Name) { tp.priced.Removed() // Update the account nonce to the dropped transaction - pnonce, err := tp.pendingAccountManager.GetNonce(addr) + pnonce, err := tp.pendingAccountManager.GetNonce(name) if err != nil && err != am.ErrAccountNotExist { - log.Error("promoteExecutables pending account manager get nonce err ", "name", addr, "err", err) + log.Error("promoteExecutables pending account manager get nonce err ", "name", name, "err", err) } if nonce := tx.GetActions()[0].Nonce(); pnonce > nonce { - tp.pendingAccountManager.SetNonce(addr, nonce) + tp.pendingAccountManager.SetNonce(name, nonce) } log.Trace("Removed fairness-exceeding pending transaction", "hash", hash) } @@ -1030,20 +1010,20 @@ func (tp *TxPool) promoteExecutables(accounts []common.Name) { } if queued > tp.config.GlobalQueue { // Sort all accounts with queued transactions by heartbeat - addresses := make(namesByHeartbeat, 0, len(tp.queue)) - for addr := range tp.queue { - if !tp.locals.contains(addr) { // don't drop locals - addresses = append(addresses, nameByHeartbeat{addr, tp.beats[addr]}) + names := make(namesByHeartbeat, 0, len(tp.queue)) + for name := range tp.queue { + if !tp.locals.contains(name) { // don't drop locals + names = append(names, nameByHeartbeat{name, tp.beats[name]}) } } - sort.Sort(addresses) + sort.Sort(names) // Drop transactions until the total is below the limit or only locals remain - for drop := queued - tp.config.GlobalQueue; drop > 0 && len(addresses) > 0; { - addr := addresses[len(addresses)-1] - list := tp.queue[addr.name] + for drop := queued - tp.config.GlobalQueue; drop > 0 && len(names) > 0; { + nameBeat := names[len(names)-1] + list := tp.queue[nameBeat.name] - addresses = addresses[:len(addresses)-1] + names = names[:len(names)-1] // Drop all transactions if they are less than the overflow if size := uint64(list.Len()); size <= drop { @@ -1068,10 +1048,10 @@ func (tp *TxPool) promoteExecutables(accounts []common.Name) { // are moved back into the future queue. func (tp *TxPool) demoteUnexecutables() { // Iterate over all accounts and demote any non-executable transactions - for addr, list := range tp.pending { - nonce, err := tp.curAccountManager.GetNonce(addr) + for name, list := range tp.pending { + nonce, err := tp.curAccountManager.GetNonce(name) if err != nil && err != am.ErrAccountNotExist { - log.Error("promoteExecutables current account manager get nonce err ", "name", addr, "err", err) + log.Error("promoteExecutables current account manager get nonce err ", "name", name, "err", err) } // Drop all transactions that are deemed too old (low nonce) @@ -1083,9 +1063,9 @@ func (tp *TxPool) demoteUnexecutables() { } // Drop all transactions that are too costly (low balance or out of gas or no permissions), and queue any invalids back for later - gasBalance, err := tp.curAccountManager.GetAccountBalanceByID(addr, tp.config.GasAssetID, 0) + gasBalance, err := tp.curAccountManager.GetAccountBalanceByID(name, tp.config.GasAssetID, 0) if err != nil && err != am.ErrAccountNotExist { - log.Error("promoteExecutables current account manager get balance err ", "name", addr, "assetID", tp.config.GasAssetID, "err", err) + log.Error("promoteExecutables current account manager get balance err ", "name", name, "assetID", tp.config.GasAssetID, "err", err) } drops, invalids := list.Filter(gasBalance, tp.currentMaxGas, tp.signer, tp.curAccountManager.GetAccountBalanceByID, tp.curAccountManager.RecoverTx) @@ -1111,8 +1091,8 @@ func (tp *TxPool) demoteUnexecutables() { } // Delete the entire queue entry if it became empty. if list.Empty() { - delete(tp.pending, addr) - delete(tp.beats, addr) + delete(tp.pending, name) + delete(tp.beats, name) } } } diff --git a/txpool/txpool_test.go b/txpool/txpool_test.go index 6f259d76..0d003768 100644 --- a/txpool/txpool_test.go +++ b/txpool/txpool_test.go @@ -664,7 +664,7 @@ func TestTransactionGapFilling(t *testing.T) { // Keep track of transaction events to ensure all executables get announced events := make(chan *event.Event, testTxPoolConfig.AccountQueue+5) - sub := event.Subscribe(nil, events, event.TxEv, []*types.Transaction{}) + sub := event.Subscribe(nil, events, event.NewTxs, []*types.Transaction{}) defer sub.Unsubscribe() // Create a pending and a queued transaction with a nonce-gap in between @@ -806,9 +806,9 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) { pool.AddRemotes(txs) queued := 0 - for addr, list := range pool.queue { + for name, list := range pool.queue { if list.Len() > int(config.AccountQueue) { - t.Fatalf("addr %x: queued accounts overflown allowance: %d > %d", addr, list.Len(), config.AccountQueue) + t.Fatalf("name %x: queued accounts overflown allowance: %d > %d", name, list.Len(), config.AccountQueue) } queued += list.Len() } @@ -825,9 +825,9 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) { // If locals are disabled, the previous eviction algorithm should apply here too if nolocals { queued := 0 - for addr, list := range pool.queue { + for name, list := range pool.queue { if list.Len() > int(config.AccountQueue) { - t.Fatalf("addr %x: queued accounts overflown allowance: %d > %d", addr, list.Len(), config.AccountQueue) + t.Fatalf("name %x: queued accounts overflown allowance: %d > %d", name, list.Len(), config.AccountQueue) } queued += list.Len() } @@ -945,7 +945,7 @@ func TestTransactionPendingLimiting(t *testing.T) { // Keep track of transaction events to ensure all executables get announced events := make(chan *event.Event, testTxPoolConfig.AccountQueue+5) - sub := event.Subscribe(nil, events, event.TxEv, []*types.Transaction{}) + sub := event.Subscribe(nil, events, event.NewTxs, []*types.Transaction{}) defer sub.Unsubscribe() // Keep queuing up transactions and make sure all above a limit are dropped @@ -988,7 +988,7 @@ func TestTransactionPoolRepricing(t *testing.T) { // Keep track of transaction events to ensure all executables get announced events := make(chan *event.Event, 32) - sub := event.Subscribe(nil, events, event.TxEv, []*types.Transaction{}) + sub := event.Subscribe(nil, events, event.NewTxs, []*types.Transaction{}) defer sub.Unsubscribe() manager, _ := am.NewAccountManager(statedb) @@ -1200,7 +1200,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { // Keep track of transaction events to ensure all executables get announced events := make(chan *event.Event, 32) - sub := event.Subscribe(nil, events, event.TxEv, []*types.Transaction{}) + sub := event.Subscribe(nil, events, event.NewTxs, []*types.Transaction{}) defer sub.Unsubscribe() // Create a number of test accounts and fund them @@ -1314,7 +1314,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) { // Keep track of transaction events to ensure all executables get announced events := make(chan *event.Event, 32) - sub := event.Subscribe(nil, events, event.TxEv, []*types.Transaction{}) + sub := event.Subscribe(nil, events, event.NewTxs, []*types.Transaction{}) defer sub.Unsubscribe() // Create a number of test accounts and fund them @@ -1390,7 +1390,7 @@ func TestTransactionReplacement(t *testing.T) { // Keep track of transaction events to ensure all executables get announced events := make(chan *event.Event, 32) - sub := event.Subscribe(nil, events, event.TxEv, []*types.Transaction{}) + sub := event.Subscribe(nil, events, event.NewTxs, []*types.Transaction{}) defer sub.Unsubscribe() // Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too) @@ -1655,7 +1655,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { for name, list := range pool.pending { if list.Len() != int(testTxPoolConfig.AccountSlots) { - t.Fatalf("addr %s: total pending transactions mismatch: have %d, want %d", name, list.Len(), testTxPoolConfig.AccountSlots) + t.Fatalf("name %s: total pending transactions mismatch: have %d, want %d", name, list.Len(), testTxPoolConfig.AccountSlots) } } if err := validateTxPoolInternals(pool); err != nil { diff --git a/types/action.go b/types/action.go index bee50cb4..f51b5bc2 100644 --- a/types/action.go +++ b/types/action.go @@ -36,7 +36,6 @@ var ErrInvalidSig = errors.New("invalid action v, r, s values") type ActionType uint64 const ( - // CallContract represents the call contract action. CallContract ActionType = iota // CreateContract repesents the create contract action. @@ -44,13 +43,13 @@ const ( ) const ( - //CreateAccount repesents the create account. + // CreateAccount repesents the create account. CreateAccount ActionType = 0x100 + iota - //UpdateAccount repesents update account. + // UpdateAccount repesents update account. UpdateAccount // DeleteAccount repesents the delete account action. DeleteAccount - //UpdateAccountAuthor represents the update account author. + // UpdateAccountAuthor represents the update account author. UpdateAccountAuthor ) @@ -59,14 +58,13 @@ const ( IncreaseAsset ActionType = 0x200 + iota // IssueAsset repesents Issue asset action. IssueAsset - //DestroyAsset destroy asset + // DestroyAsset destroy asset DestroyAsset // SetAssetOwner repesents set asset new owner action. SetAssetOwner - //SetAssetFounder set asset founder - //SetAssetFounder + // UpdateAsset update asset UpdateAsset - //Transfer repesents transfer asset action. + // Transfer repesents transfer asset action. Transfer ) @@ -120,9 +118,9 @@ type actionData struct { type Action struct { data actionData // cache - hash atomic.Value - sender atomic.Value - author atomic.Value + hash atomic.Value + senderPubkeys atomic.Value + author atomic.Value } // NewAction initialize transaction's action. @@ -156,8 +154,8 @@ func (a *Action) GetSign() []*SignData { return a.data.Sign } -//CheckValid Check the validity of all fields -func (a *Action) CheckValid(conf *params.ChainConfig) error { +// Check the validity of all fields +func (a *Action) Check(conf *params.ChainConfig) error { //check To switch a.Type() { case CreateContract: diff --git a/types/action_test.go b/types/action_test.go index 55bb6fe0..87f26b59 100644 --- a/types/action_test.go +++ b/types/action_test.go @@ -103,7 +103,7 @@ func TestActionEncodeAndDecode(t *testing.T) { assert.Equal(t, testAction, actAction) } -func TestAction_CheckValid(t *testing.T) { +func TestAction_Check(t *testing.T) { actionBytes, err := rlp.EncodeToBytes(testAction) if err != nil { t.Fatal(err) @@ -114,7 +114,7 @@ func TestAction_CheckValid(t *testing.T) { t.Fatal(err) } - if err := actAction.CheckValid(params.DefaultChainconfig); err != nil { + if err := actAction.Check(params.DefaultChainconfig); err != nil { t.Errorf("TestAction_CheckValue err, wantErr %v", true) } @@ -129,7 +129,7 @@ func TestAction_CheckValid(t *testing.T) { t.Fatal(err) } - if err := actAction2.CheckValid(params.DefaultChainconfig); err == nil { + if err := actAction2.Check(params.DefaultChainconfig); err == nil { t.Errorf("TestAction2_CheckValue err, wantErr %v", false) } @@ -144,7 +144,7 @@ func TestAction_CheckValid(t *testing.T) { t.Fatal(err) } - if err := actAction3.CheckValid(params.DefaultChainconfig); err != nil { + if err := actAction3.Check(params.DefaultChainconfig); err != nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } @@ -159,7 +159,7 @@ func TestAction_CheckValid(t *testing.T) { t.Fatal(err) } - if err := actAction3.CheckValid(params.DefaultChainconfig); err != nil { + if err := actAction3.Check(params.DefaultChainconfig); err != nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } @@ -174,7 +174,7 @@ func TestAction_CheckValid(t *testing.T) { t.Fatal(err) } - if err := actAction3.CheckValid(params.DefaultChainconfig); err != nil { + if err := actAction3.Check(params.DefaultChainconfig); err != nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } } @@ -240,7 +240,7 @@ var ( ) ) -func TestAction_CheckValid2(t *testing.T) { +func TestAction_Check2(t *testing.T) { // actionBytes10, err := rlp.EncodeToBytes(testAction10) // if err != nil { @@ -252,23 +252,23 @@ func TestAction_CheckValid2(t *testing.T) { // t.Fatal(err) // } - if err := testAction10.CheckValid(params.DefaultChainconfig); err != nil { + if err := testAction10.Check(params.DefaultChainconfig); err != nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } - if err := testAction11.CheckValid(params.DefaultChainconfig); err != nil { + if err := testAction11.Check(params.DefaultChainconfig); err != nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } - if err := testAction12.CheckValid(params.DefaultChainconfig); err != nil { + if err := testAction12.Check(params.DefaultChainconfig); err != nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } - if err := testAction13.CheckValid(params.DefaultChainconfig); err != nil { + if err := testAction13.Check(params.DefaultChainconfig); err != nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } - if err := testAction14.CheckValid(params.DefaultChainconfig); err != nil { + if err := testAction14.Check(params.DefaultChainconfig); err != nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } @@ -335,7 +335,7 @@ var ( ) ) -func TestAction_CheckValid3(t *testing.T) { +func TestAction_Check3(t *testing.T) { // actionBytes10, err := rlp.EncodeToBytes(testAction10) // if err != nil { @@ -347,23 +347,23 @@ func TestAction_CheckValid3(t *testing.T) { // t.Fatal(err) // } - if err := testAction20.CheckValid(params.DefaultChainconfig); err == nil { + if err := testAction20.Check(params.DefaultChainconfig); err == nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } - if err := testAction21.CheckValid(params.DefaultChainconfig); err == nil { + if err := testAction21.Check(params.DefaultChainconfig); err == nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } - if err := testAction22.CheckValid(params.DefaultChainconfig); err == nil { + if err := testAction22.Check(params.DefaultChainconfig); err == nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } - if err := testAction23.CheckValid(params.DefaultChainconfig); err == nil { + if err := testAction23.Check(params.DefaultChainconfig); err == nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } - if err := testAction24.CheckValid(params.DefaultChainconfig); err == nil { + if err := testAction24.Check(params.DefaultChainconfig); err == nil { t.Errorf("TestAction3_CheckValue err, wantErr %v", false) } diff --git a/types/block.go b/types/block.go index f832cf80..8f0f3a99 100644 --- a/types/block.go +++ b/types/block.go @@ -32,26 +32,26 @@ import ( // ForkID represents a blockchain fork type ForkID struct { - Cur uint64 - Next uint64 + Cur uint64 `json:"cur"` + Next uint64 `json:"next"` } // Header represents a block header in the blockchain. type Header struct { - ParentHash common.Hash `json:"parentHash"` - Coinbase common.Name `json:"miner"` - ProposedIrreversible uint64 `json:"proposedIrreversible"` - Root common.Hash `json:"stateRoot"` - TxsRoot common.Hash `json:"transactionsRoot"` - ReceiptsRoot common.Hash `json:"receiptsRoot"` - Bloom Bloom `json:"logsBloom"` - Difficulty *big.Int `json:"difficulty"` - Number *big.Int `json:"number"` - GasLimit uint64 `json:"gasLimit"` - GasUsed uint64 `json:"gasUsed"` - Time *big.Int `json:"timestamp"` - Extra []byte `json:"extraData"` - ForkID ForkID `json:"forkID"` + ParentHash common.Hash + Coinbase common.Name + ProposedIrreversible uint64 + Root common.Hash + TxsRoot common.Hash + ReceiptsRoot common.Hash + Bloom Bloom + Difficulty *big.Int + Number *big.Int + GasLimit uint64 + GasUsed uint64 + Time *big.Int + Extra []byte + ForkID ForkID } // Hash returns the block hash of the header, which is simply the keccak256 hash of its @@ -227,6 +227,16 @@ func (b *Block) WithBody(transactions []*Transaction) *Block { return block } +// Check the validity of all fields +func (b *Block) Check() error { + for _, tx := range b.Txs { + if len(tx.actions) == 0 { + return ErrEmptyActions + } + } + return nil +} + // CopyHeader creates a deep copy of a block header to prevent side effects from // modifying a header variable. func CopyHeader(h *Header) *Header { diff --git a/types/log.go b/types/log.go index c5a18570..9a0b62f5 100644 --- a/types/log.go +++ b/types/log.go @@ -24,7 +24,7 @@ import ( // Log represents a contract log event. These events are generated by the LOG opcode and // stored/indexed by the node. type Log struct { - Name common.Name // address of the contract that generated the event + Name common.Name // name of the contract that generated the event Topics []common.Hash // list of topics provided by the contract. Data []byte // supplied by the contract, usually ABI-encoded BlockNumber uint64 // block in which the transaction was included diff --git a/types/signer.go b/types/signer.go index 97745a6b..483fd4ef 100644 --- a/types/signer.go +++ b/types/signer.go @@ -72,7 +72,7 @@ func SignActionWithMultiKey(a *Action, tx *Transaction, s Signer, keys []*KeyPai } func RecoverMultiKey(signer Signer, a *Action, tx *Transaction) ([]common.PubKey, error) { - if sc := a.sender.Load(); sc != nil { + if sc := a.senderPubkeys.Load(); sc != nil { sigCache := sc.(sigCache) if sigCache.signer.Equal(signer) { return sigCache.pubKeys, nil @@ -83,7 +83,7 @@ func RecoverMultiKey(signer Signer, a *Action, tx *Transaction) ([]common.PubKey if err != nil { return []common.PubKey{}, err } - a.sender.Store(sigCache{signer: signer, pubKeys: pubKeys}) + a.senderPubkeys.Store(sigCache{signer: signer, pubKeys: pubKeys}) return pubKeys, nil } @@ -136,7 +136,7 @@ func (s Signer) PubKeys(a *Action, tx *Transaction) ([]common.PubKey, error) { for _, sign := range a.data.Sign { V := new(big.Int).Sub(sign.V, s.chainIDMul) V.Sub(V, big8) - data, err := recoverPlain(s.Hash(tx), sign.R, sign.S, V, true) + data, err := recoverPlain(s.Hash(tx), sign.R, sign.S, V) if err != nil { return nil, err } @@ -187,12 +187,12 @@ func (s Signer) Hash(tx *Transaction) common.Hash { }) } -func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) ([]byte, error) { +func recoverPlain(sighash common.Hash, R, S, Vb *big.Int) ([]byte, error) { if Vb.BitLen() > 8 { return nil, ErrInvalidSig } V := byte(Vb.Uint64() - 27) - if !crypto.ValidateSignatureValues(V, R, S, homestead) { + if !crypto.ValidateSignatureValues(V, R, S) { return nil, ErrInvalidSig } // encode the snature in uncompressed format diff --git a/types/signer_test.go b/types/signer_test.go index 53c83425..4db62246 100644 --- a/types/signer_test.go +++ b/types/signer_test.go @@ -24,24 +24,6 @@ import ( "github.com/fractalplatform/fractal/crypto" ) -// func TestSigning(t *testing.T) { -// key, _ := crypto.GenerateKey() -// exp := crypto.FromECDSAPub(&key.PublicKey) -// signer := NewSigner(big.NewInt(18)) -// if err := SignAction(testTx.GetActions()[0], testTx, signer, key); err != nil { -// t.Fatal(err) -// } - -// pubkey, err := Recover(signer, testTx.GetActions()[0], testTx) -// if err != nil { -// t.Fatal(err) -// } - -// if bytes.Compare(pubkey.Bytes(), exp) != 0 { -// t.Errorf("exected from and address to be equal. Got %x want %x", pubkey, exp) -// } -// } - func TestSigningMultiKey(t *testing.T) { keys := make([]*KeyPair, 0) pubs := make([]common.PubKey, 0) @@ -63,7 +45,7 @@ func TestSigningMultiKey(t *testing.T) { for i, pubkey := range pubkeys { if pubkey.Compare(pubs[i]) != 0 { - t.Errorf("exected from and address to be equal. Got %x want %x", pubkey, pubs[i]) + t.Errorf("exected from and pubkey to be equal. Got %x want %x", pubkey, pubs[i]) } } @@ -75,7 +57,7 @@ func TestSigningMultiKey(t *testing.T) { for i, pubkey := range pubkeys { if pubkey.Compare(pubs[i]) != 0 { - t.Errorf("exected from and address to be equal. Got %x want %x", pubkey, pubs[i]) + t.Errorf("exected from and pubkey to be equal. Got %x want %x", pubkey, pubs[i]) } } } diff --git a/types/transaction.go b/types/transaction.go index 14f4f423..3788eb61 100644 --- a/types/transaction.go +++ b/types/transaction.go @@ -18,14 +18,27 @@ package types import ( "container/heap" + "errors" + "fmt" "io" "math/big" "sync/atomic" "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/params" "github.com/fractalplatform/fractal/utils/rlp" ) +var ( + // ErrOversizedData is returned if the input data of a transaction is greater + // than some meaningful limit a user might use. This is not a consensus error + // making the transaction invalid, rather a DOS protection. + ErrOversizedData = errors.New("oversized data") + + // ErrEmptyActions transaction no actions + ErrEmptyActions = errors.New("transaction no actions") +) + // Transaction represents an entire transaction in the block. type Transaction struct { actions []*Action @@ -114,6 +127,29 @@ func (tx *Transaction) Size() common.StorageSize { return common.StorageSize(c) } +// Check the validity of all fields +func (tx *Transaction) Check(conf *params.ChainConfig) error { + if len(tx.actions) == 0 { + return ErrEmptyActions + } + + // Heuristic limit, reject transactions over 32KB to prfeed DOS attacks + if tx.Size() > common.StorageSize(params.MaxTxSize) { + return ErrOversizedData + } + + if conf.SysTokenID != tx.gasAssetID { + return fmt.Errorf("only support system asset %d as tx fee", conf.SysTokenID) + } + + for _, action := range tx.actions { + if err := action.Check(conf); err != nil { + return err + } + } + return nil +} + // RPCTransaction that will serialize to the RPC representation of a transaction. type RPCTransaction struct { BlockHash common.Hash `json:"blockHash"` @@ -214,7 +250,7 @@ func NewTransactionsByPriceAndNonce(txs map[common.Name][]*Transaction) *Transac heads := make(TxByPrice, 0, len(txs)) for from, accTxs := range txs { heads = append(heads, accTxs[0]) - // Ensure the sender address is from the signer + // Ensure the sender name is from the signer action := accTxs[0].actions[0] acc := action.Sender() txs[acc] = accTxs[1:]