OSDN Git Service

add_federation (#1936)
authorPoseidon <shenao.78@163.com>
Mon, 24 May 2021 07:58:07 +0000 (15:58 +0800)
committerGitHub <noreply@github.com>
Mon, 24 May 2021 07:58:07 +0000 (15:58 +0800)
cmd/bytomd/commands/init.go
config/config.go
config/federation.go [new file with mode: 0644]
consensus/general.go
database/store.go
node/node.go
protocol/auth_verification.go
protocol/bc/types/sup_link.go
protocol/bc/types/sup_link_test.go
protocol/state/checkpoint.go

index 410c65b..85f4866 100644 (file)
@@ -39,6 +39,14 @@ func initFiles(cmd *cobra.Command, args []string) {
                cfg.EnsureRoot(config.RootDir, "solonet")
        }
 
+       //generate the federation config file
+       fedFilePath := config.FederationFile()
+       if _, err := os.Stat(fedFilePath); os.IsNotExist(err) {
+               if err := cfg.ExportFederationFile(fedFilePath, config); err != nil {
+                       log.WithFields(log.Fields{"module": logModule, "config": fedFilePath, "error": err}).Fatal("fail on export federation file")
+               }
+       }
+
        //generate the node private key
        keyFilePath := path.Join(config.RootDir, config.PrivateKeyFile)
        if _, err := os.Stat(keyFilePath); os.IsNotExist(err) {
index ebc59f0..45ab418 100644 (file)
@@ -22,12 +22,13 @@ type Config struct {
        // Top level options use an anonymous struct
        BaseConfig `mapstructure:",squash"`
        // Options for services
-       P2P       *P2PConfig       `mapstructure:"p2p"`
-       Wallet    *WalletConfig    `mapstructure:"wallet"`
-       Auth      *RPCAuthConfig   `mapstructure:"auth"`
-       Web       *WebConfig       `mapstructure:"web"`
-       Simd      *SimdConfig      `mapstructure:"simd"`
-       Websocket *WebsocketConfig `mapstructure:"ws"`
+       P2P        *P2PConfig        `mapstructure:"p2p"`
+       Wallet     *WalletConfig     `mapstructure:"wallet"`
+       Auth       *RPCAuthConfig    `mapstructure:"auth"`
+       Web        *WebConfig        `mapstructure:"web"`
+       Simd       *SimdConfig       `mapstructure:"simd"`
+       Websocket  *WebsocketConfig  `mapstructure:"ws"`
+       Federation *FederationConfig `mapstructure:"federation"`
 }
 
 // Default configurable parameters.
@@ -40,6 +41,7 @@ func DefaultConfig() *Config {
                Web:        DefaultWebConfig(),
                Simd:       DefaultSimdConfig(),
                Websocket:  DefaultWebsocketConfig(),
+               Federation: DefaultFederationConfig(),
        }
 }
 
@@ -80,20 +82,20 @@ func (cfg *Config) PrivateKey() *chainkd.XPrv {
        return cfg.XPrv
 }
 
-//-----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
 // BaseConfig
 type BaseConfig struct {
        // The root directory for all data.
        // This should be set in viper so it can unmarshal into this struct
        RootDir string `mapstructure:"home"`
 
-       //The alias of the node
+       // The alias of the node
        NodeAlias string `mapstructure:"node_alias"`
 
-       //The ID of the network to json
+       // The ID of the network to json
        ChainID string `mapstructure:"chain_id"`
 
-       //log level to set
+       // log level to set
        LogLevel string `mapstructure:"log_level"`
 
        // A custom human readable name for this node
@@ -123,20 +125,23 @@ type BaseConfig struct {
        PrivateKeyFile string `mapstructure:"private_key_file"`
        XPrv           *chainkd.XPrv
        XPub           *chainkd.XPub
+
+       FederationFileName string `mapstructure:"federation_file"`
 }
 
 // Default configurable base parameters.
 func DefaultBaseConfig() BaseConfig {
        return BaseConfig{
-               Moniker:           "anonymous",
-               ProfListenAddress: "",
-               Mining:            false,
-               DBBackend:         "leveldb",
-               DBPath:            "data",
-               KeysPath:          "keystore",
-               NodeAlias:         "",
-               LogFile:           "log",
-               PrivateKeyFile:    "node_key.txt",
+               Moniker:            "anonymous",
+               ProfListenAddress:  "",
+               Mining:             false,
+               DBBackend:          "leveldb",
+               DBPath:             "data",
+               KeysPath:           "keystore",
+               NodeAlias:          "",
+               LogFile:            "log",
+               PrivateKeyFile:     "node_key.txt",
+               FederationFileName: "federation.json",
        }
 }
 
@@ -152,6 +157,10 @@ func (b BaseConfig) KeysDir() string {
        return rootify(b.KeysPath, b.RootDir)
 }
 
+func (b BaseConfig) FederationFile() string {
+       return rootify(b.FederationFileName, b.RootDir)
+}
+
 // P2PConfig
 type P2PConfig struct {
        ListenAddress    string `mapstructure:"laddr"`
@@ -182,7 +191,7 @@ func DefaultP2PConfig() *P2PConfig {
        }
 }
 
-//-----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
 type WalletConfig struct {
        Disable  bool   `mapstructure:"disable"`
        Rescan   bool   `mapstructure:"rescan"`
@@ -207,6 +216,11 @@ type WebsocketConfig struct {
        MaxNumConcurrentReqs int `mapstructure:"max_num_concurrent_reqs"`
 }
 
+type FederationConfig struct {
+       Xpubs  []chainkd.XPub `json:"xpubs"`
+       Quorum int            `json:"quorum"`
+}
+
 // Default configurable rpc's auth parameters.
 func DefaultRPCAuthConfig() *RPCAuthConfig {
        return &RPCAuthConfig{
@@ -245,7 +259,27 @@ func DefaultWebsocketConfig() *WebsocketConfig {
        }
 }
 
-//-----------------------------------------------------------------------------
+// Default configurable federation parameters.
+func DefaultFederationConfig() *FederationConfig {
+       return &FederationConfig{
+               Xpubs: []chainkd.XPub{
+                       xpub("580daf48fa8962100047cb1391da890bb7f2c849fdbc9b368cb4394a4c7cbb0977e2e7ebbf055dc0ef90af6a0d2af01ce7ec56b735d016aab597815ec48552e5"),
+                       xpub("f3f6bcf61b65fa9d1566455a5688ca8b395efdc22e654963134b5e5cb0a45d8be522d21abc384a73177a7b9d64eba915fcfe2862d86a508a3c46dc410bdd72ad"),
+                       xpub("53559612f2b7bcada18948b7de39d63947a0e2bd7336d07db1350c54ba5743996b84bf9d18ff7a2457e1a5c70ce5013e4a3b62666ddb03294c53051d5f5c70c0"),
+                       xpub("7c88cc58adfc71818b08308d43c29de22460b0ea6895449cbec6e458d7dc09e0aea243fa5075ee6621da0d805bd047f6bb207329c5bd2ca3253b172fb323b512"),
+               },
+               Quorum: 2,
+       }
+}
+
+func xpub(str string) (xpub chainkd.XPub) {
+       if err := xpub.UnmarshalText([]byte(str)); err != nil {
+               log.Panicf("Fail converts a string to xpub")
+       }
+       return xpub
+}
+
+// -----------------------------------------------------------------------------
 // Utils
 
 // helper function to make config creation independent of root dir
diff --git a/config/federation.go b/config/federation.go
new file mode 100644 (file)
index 0000000..5d5f913
--- /dev/null
@@ -0,0 +1,30 @@
+package config
+
+import (
+       "bytes"
+       "encoding/json"
+       "io/ioutil"
+       "os"
+)
+
+func ExportFederationFile(fedFile string, config *Config) error {
+       buf := new(bytes.Buffer)
+
+       encoder := json.NewEncoder(buf)
+       encoder.SetIndent("", "  ")
+       if err := encoder.Encode(config.Federation); err != nil {
+               return err
+       }
+
+       return ioutil.WriteFile(fedFile, buf.Bytes(), 0644)
+}
+
+func LoadFederationFile(fedFile string, config *Config) error {
+       file, err := os.Open(fedFile)
+       if err != nil {
+               return err
+       }
+       defer file.Close()
+
+       return json.NewDecoder(file).Decode(config.Federation)
+}
index a7be813..d1868f5 100644 (file)
@@ -10,12 +10,12 @@ import (
 // consensus variables
 const (
        // Max gas that one block contains
-       MaxBlockGas      = uint64(10000000)
-       VMGasRate        = int64(200)
-       StorageGasRate   = int64(1)
-       MaxGasAmount     = int64(200000)
-       DefaultGasCredit = int64(30000)
-       NumOfValidators  = int(10)
+       MaxBlockGas        = uint64(10000000)
+       VMGasRate          = int64(200)
+       StorageGasRate     = int64(1)
+       MaxGasAmount       = int64(200000)
+       DefaultGasCredit   = int64(30000)
+       MaxNumOfValidators = int(10)
 
        // config parameter for coinbase reward
        CoinbasePendingBlockNumber = uint64(100)
index b840b4a..f0d2d7f 100644 (file)
@@ -354,7 +354,7 @@ func (s *Store) SaveCheckpoints(checkpoints ...*state.Checkpoint) error {
 
 func setSupLinkToCheckpoint(c *state.Checkpoint, supLinks types.SupLinks) {
        for _, supLink := range supLinks {
-               var signatures [consensus.NumOfValidators]string
+               var signatures [consensus.MaxNumOfValidators]string
                for i, signature := range supLink.Signatures {
                        signatures[i] = hex.EncodeToString(signature)
                }
index 771b4ec..92652f2 100644 (file)
@@ -11,6 +11,7 @@ import (
        log "github.com/sirupsen/logrus"
        cmn "github.com/tendermint/tmlibs/common"
        browser "github.com/toqueteos/webbrowser"
+
        "github.com/bytom/bytom/proposal/blockproposer"
        "github.com/prometheus/prometheus/util/flock"
 
@@ -59,18 +60,10 @@ type Node struct {
 
 // NewNode create bytom node
 func NewNode(config *cfg.Config) *Node {
-       ctx := context.Background()
-       if err := lockDataDirectory(config); err != nil {
-               cmn.Exit("Error: " + err.Error())
-       }
-
-       if err := bytomLog.InitLogFile(config); err != nil {
-               log.WithField("err", err).Fatalln("InitLogFile failed")
+       if err := initNodeConfig(config); err != nil {
+               cmn.Exit(cmn.Fmt("Failed to init config: %v", err))
        }
 
-       initActiveNetParams(config)
-       initCommonConfig(config)
-
        // Get store
        if config.DBBackend != "memdb" && config.DBBackend != "leveldb" {
                cmn.Exit(cmn.Fmt("Param db_backend [%v] is invalid, use leveldb or memdb", config.DBBackend))
@@ -97,7 +90,7 @@ func NewNode(config *cfg.Config) *Node {
        txFeedDB := dbm.NewDB("txfeeds", config.DBBackend, config.DBDir())
        txFeed = txfeed.NewTracker(txFeedDB, chain)
 
-       if err = txFeed.Prepare(ctx); err != nil {
+       if err = txFeed.Prepare(context.Background()); err != nil {
                log.WithFields(log.Fields{"module": logModule, "error": err}).Error("start txfeed")
                return nil
        }
@@ -161,6 +154,25 @@ func NewNode(config *cfg.Config) *Node {
        return node
 }
 
+func initNodeConfig(config *cfg.Config) error {
+       if err := lockDataDirectory(config); err != nil {
+               cmn.Exit("Error: " + err.Error())
+       }
+
+       if err := bytomLog.InitLogFile(config); err != nil {
+               log.WithField("err", err).Fatalln("InitLogFile failed")
+       }
+
+       if err := cfg.LoadFederationFile(config.FederationFile(), config); err != nil {
+               log.WithField("err", err).Info("Failed to load federated information")
+               return err
+       }
+
+       initActiveNetParams(config)
+       initCommonConfig(config)
+       return nil
+}
+
 // Lock data directory after daemonization
 func lockDataDirectory(config *cfg.Config) error {
        _, _, err := flock.New(filepath.Join(config.RootDir, "LOCK"))
index 4682d93..c43e891 100644 (file)
@@ -74,7 +74,7 @@ func (c *Casper) addVerificationToCheckpoint(target *state.Checkpoint, validator
                }
 
                supLink := target.AddVerification(v.SourceHash, v.SourceHeight, validators[v.PubKey].Order, v.Signature)
-               if target.Status != state.Unjustified || !supLink.IsMajority() || source.Status == state.Finalized {
+               if target.Status != state.Unjustified || !supLink.IsMajority(len(validators)) || source.Status == state.Finalized {
                        continue
                }
 
index 1ece0b6..be69564 100644 (file)
@@ -62,7 +62,7 @@ func (s SupLinks) writeTo(w io.Writer) error {
 type SupLink struct {
        SourceHeight uint64
        SourceHash   bc.Hash
-       Signatures   [consensus.NumOfValidators][]byte
+       Signatures   [consensus.MaxNumOfValidators][]byte
 }
 
 func (s *SupLink) readFrom(r *blockchain.Reader) (err error) {
@@ -74,7 +74,7 @@ func (s *SupLink) readFrom(r *blockchain.Reader) (err error) {
                return err
        }
 
-       for i := 0; i < consensus.NumOfValidators; i++ {
+       for i := 0; i < consensus.MaxNumOfValidators; i++ {
                if s.Signatures[i], err = blockchain.ReadVarstr31(r); err != nil {
                        return err
                }
index f56c842..4998ad8 100644 (file)
@@ -23,7 +23,7 @@ func TestReadWriteSupLink(t *testing.T) {
                                {
                                        SourceHeight: 100,
                                        SourceHash:   testutil.MustDecodeHash("0a3cd1175e295a35c2b63054969c3fe54eeaa3eb68258227b28d8daa6cf4c50c"),
-                                       Signatures: [consensus.NumOfValidators][]byte{
+                                       Signatures: [consensus.MaxNumOfValidators][]byte{
                                                testutil.MustDecodeHexString("750318156e8c913c378a8d31294fca1084df3be3967035425f470f81e00cd824d1f12bf8e1c3b308f4b1a916438b9ce630722bc8d92ef0feebbbaf987dd7a60e"),
                                                testutil.MustDecodeHexString("be7c7e0ba54109c8c457cdbba4691d7aaae32eb4b8ac63755f2494be406027ce66c7b4730bfd2506fa2caaba12a7bbbea2faca5f07bb64fe06a568b6415e7506"),
                                        },
@@ -31,7 +31,7 @@ func TestReadWriteSupLink(t *testing.T) {
                                {
                                        SourceHeight: 105,
                                        SourceHash:   testutil.MustDecodeHash("546c91cefc6a06f9b7a0aaa4d69db9a7f229af27928304a44ecd48e33ba2ba91"),
-                                       Signatures: [consensus.NumOfValidators][]byte{
+                                       Signatures: [consensus.MaxNumOfValidators][]byte{
                                                testutil.MustDecodeHexString("38c9a6a48eeea993b2d4137e73b17e4743ce3935636fcce957ae2291c691491525f39509a1c21fec3c7f78403ae88e375b796fa9dcc4cac0af8a987994f62c07"),
                                                testutil.MustDecodeHexString("4fe5646b2b669aaef0dd74a090e150de676218d0a6e693bb2d1cc791282517669d7903c60a909a5d9c5a996e5797ea9dded20b52dc4b8ec272e86e5fc4e8a008"),
                                                nil,
@@ -86,7 +86,7 @@ func TestReadWriteSupLink(t *testing.T) {
                                {
                                        SourceHeight: 200,
                                        SourceHash:   testutil.MustDecodeHash("0a3cd1175e295a35c2b63054969c3fe54eeaa3eb68258227b28d8daa6cf4c50c"),
-                                       Signatures: [consensus.NumOfValidators][]byte{
+                                       Signatures: [consensus.MaxNumOfValidators][]byte{
                                                testutil.MustDecodeHexString("750318156e8c913c378a8d31294fca1084df3be3967035425f470f81e00cd824d1f12bf8e1c3b308f4b1a916438b9ce630722bc8d92ef0feebbbaf987dd7a60e"),
                                                testutil.MustDecodeHexString("be7c7e0ba54109c8c457cdbba4691d7aaae32eb4b8ac63755f2494be406027ce66c7b4730bfd2506fa2caaba12a7bbbea2faca5f07bb64fe06a568b6415e7506"),
                                                testutil.MustDecodeHexString("9938ea16d6caae68b7e9318f1aed387ef9767dc0d80db807e0d0a77065229ceffef7a8b6407882f5d6e29b2edf1c6373bb1c47188138068e2baa4851c04c6f0e"),
index c12edb1..6986c7a 100644 (file)
@@ -3,6 +3,7 @@ package state
 import (
        "sort"
 
+       "github.com/bytom/bytom/config"
        "github.com/bytom/bytom/consensus"
        "github.com/bytom/bytom/protocol/bc"
 )
@@ -35,18 +36,18 @@ const (
 type SupLink struct {
        SourceHeight uint64
        SourceHash   bc.Hash
-       Signatures   [consensus.NumOfValidators]string
+       Signatures   [consensus.MaxNumOfValidators]string
 }
 
 // IsMajority if at least 2/3 of validators have published votes with sup link
-func (s *SupLink) IsMajority() bool {
+func (s *SupLink) IsMajority(numOfValidators int) bool {
        numOfSignatures := 0
        for _, signature := range s.Signatures {
                if signature != "" {
                        numOfSignatures++
                }
        }
-       return numOfSignatures > consensus.NumOfValidators*2/3
+       return numOfSignatures > numOfValidators*2/3
 }
 
 // Checkpoint represent the block/hash under consideration for finality for a given epoch.
@@ -120,16 +121,27 @@ func (c *Checkpoint) Validators() map[string]*Validator {
                }
        }
 
+       if len(validators) == 0 {
+               return federationValidators()
+       }
+
        sort.Slice(validators, func(i, j int) bool {
                return validators[i].Guaranty+validators[i].Vote > validators[j].Guaranty+validators[j].Vote
        })
 
        result := make(map[string]*Validator)
-       for i := 0; i < len(validators) && i < consensus.NumOfValidators; i++ {
+       for i := 0; i < len(validators) && i < consensus.MaxNumOfValidators; i++ {
                validator := validators[i]
                validator.Order = i
                result[validator.PubKey] = validator
        }
-
        return result
 }
+
+func federationValidators() map[string]*Validator {
+       validators := map[string]*Validator{}
+       for i, xPub := range config.CommonConfig.Federation.Xpubs {
+               validators[xPub.String()] = &Validator{PubKey: xPub.String(), Order: i}
+       }
+       return validators
+}