diff --git a/.gitignore b/.gitignore index 07f03b8..23c9bda 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,31 @@ -deaIDE +# See http://help.github.com/ignore-files/ for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile ~/.gitignore_global + +*# +*~ +.project +.settings + +# used by the Makefile +/build/_workspace/ +/build/bin/ +*.zip +/gemmill/modules/go-log/testdir + +# travis +profile.tmp +profile.cov + +# IdeaIDE .idea +# VS Code +.vscode + +*.log build -.DS_Store +.DS_Store + diff --git a/.travis.yml b/.travis.yml old mode 100644 new mode 100755 index f6f625f..1985b8a --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -nguage: go +language: go go_import_path: https://github.com/dappledger/AnnChain sudo: false jobs: @@ -17,3 +17,6 @@ jobs: - make script: - make test + - go test ./gemmill/... -coverprofile=coverage.txt -covermode=atomic + after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index f3a273e..9c4a896 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,77 @@ cd AnnChain make ``` +## Supported Consensus + +AnnChain supports bft and raft consensus as valid consensus options,and bft is the default.if you want to use raft, you can operate like this: + +##### First, set consensus to raft in config.toml file: + +``` shell +consensus = "raft" +``` + +##### Then, add raft peers config file raft-cluster.json into the runtime dir(take four nodes for example): + +``` shell +{ + "advertise": "ann7939-validator8fc99df2-2.default.svc.cluster.local:23000", + "local": { + "bind": "0.0.0.0:23000", + "pub_key": [ + 1, + "35EC28D113DB8D057140F903BAB049770CABAD4C2838509602552511C3F2D2E3" + ], + "rpc": "ann7939-validator8fc99df2-2.default.svc.cluster.local:47000" + }, + "peers": [ + { + "bind": "ann7939-validator480649ca-0.default.svc.cluster.local:23000", + "pub_key": [ + 1, + "7B788FD0A5A1504C438B2D6B5602717C07F5E82D25175B4065B75C46017B770D" + ], + "rpc": "ann7939-validator480649ca-0.default.svc.cluster.local:47000" + }, + { + "bind": "ann7939-validatorb14a47dc-1.default.svc.cluster.local:23000", + "pub_key": [ + 1, + "1FE0A5560BB9376348CB8F218BDA2011280606571DB20B841FA9F7560143796D" + ], + "rpc": "ann7939-validatorb14a47dc-1.default.svc.cluster.local:47000" + }, + { + "bind": "ann7939-validator8fc99df2-2.default.svc.cluster.local:23000", + "pub_key": [ + 1, + "35EC28D113DB8D057140F903BAB049770CABAD4C2838509602552511C3F2D2E3" + ], + "rpc": "ann7939-validator8fc99df2-2.default.svc.cluster.local:47000" + }, + { + "bind": "ann7939-validatore78bd527-3.default.svc.cluster.local:23000", + "pub_key": [ + 1, + "3C521E9D3D942654FA1E6C52E7B3A4EDE059E047FB4DF4F00F04C092149002EA" + ], + "rpc": "10.103.237.176:47000" + } + ] +} +``` + +* advertise: advertise address is used for others peers to connect. + +* local.bind: local bind address for raft protocl. + +* local.pub_key: node's pubkey, same as pbft pubkey. + +* local.rpc: node's rpc bind address. + +* peers: others node's bind address and pub_key info, including it selft. + + ## Quick Start #### Single node diff --git a/build.sh b/build.sh index 4ad30f2..a9b5229 100644 --- a/build.sh +++ b/build.sh @@ -30,15 +30,11 @@ run() genesis ) build $prev"/cmd/genesis" "genesis" ;; - api ) - build $prev"/cmd/api" "genesis-api" - ;; gtool ) build $prev"/cmd/client" "gtool" ;; * ) build $prev"/cmd/genesis" "genesis" - build $prev"/cmd/api" "genesis-api" build $prev"/cmd/client" "gtool" ;; esac diff --git a/chain/core/node.go b/chain/core/node.go index 3efd01a..20e5248 100644 --- a/chain/core/node.go +++ b/chain/core/node.go @@ -208,7 +208,14 @@ func (n *Node) StartRPC() ([]net.Listener, error) { mux := http.NewServeMux() // wm := rpcserver.NewWebsocketManager(rpcRoutes, n.evsw) // mux.HandleFunc("/websocket", wm.WebsocketHandler) - rpcserver.RegisterRPCFuncs(mux, n.rpcRoutes()) + routes := n.rpcRoutes() + for _, v := range n.Angine.APIs() { + for n, h := range v { + routes[n] = h + } + } + rpcserver.RegisterRPCFuncs(mux, routes) + listener, err := rpcserver.StartHTTPServer(listenAddr, mux) if err != nil { return nil, err diff --git a/cmd/client/commands/version.go b/cmd/client/commands/version.go index 91f8b15..e2b84a2 100644 --- a/cmd/client/commands/version.go +++ b/cmd/client/commands/version.go @@ -17,10 +17,11 @@ import ( "fmt" "gopkg.in/urfave/cli.v1" + + "github.com/dappledger/AnnChain/chain/types" ) var ( - VERSION string VersionCommands = cli.Command{ Name: "version", Action: ShowVersion, @@ -36,5 +37,5 @@ var ( ) func ShowVersion(ctx *cli.Context) { - fmt.Println("version:", VERSION) + fmt.Println("version:", types.GetVersion()) } diff --git a/cmd/client/main.go b/cmd/client/main.go index f2262a6..66308d6 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -18,6 +18,7 @@ import ( "gopkg.in/urfave/cli.v1" + "github.com/dappledger/AnnChain/chain/types" "github.com/dappledger/AnnChain/cmd/client/commands" "github.com/dappledger/AnnChain/cmd/client/commons" ) @@ -27,7 +28,7 @@ func main() { app := cli.NewApp() app.Name = "anntool" - app.Version = "0.6" + app.Version = types.GetVersion() app.Commands = []cli.Command{ commands.SignCommand, diff --git a/eth/common/hexutil/json_test.go b/eth/common/hexutil/json_test.go index 290bf9c..055e002 100644 --- a/eth/common/hexutil/json_test.go +++ b/eth/common/hexutil/json_test.go @@ -19,6 +19,8 @@ package hexutil import ( "bytes" "encoding/hex" + "encoding/json" + "errors" "math/big" "testing" ) @@ -55,15 +57,17 @@ func referenceBytes(s string) []byte { return b } +var errJSONEOF = errors.New("unexpected end of JSON input") + var unmarshalBytesTests = []unmarshalTest{ // invalid encoding - {input: "", wantErr: errNonString}, - {input: "null", wantErr: errNonString}, - {input: "10", wantErr: errNonString}, - {input: `"0"`, wantErr: ErrMissingPrefix}, - {input: `"0x0"`, wantErr: ErrOddLength}, - {input: `"0xxx"`, wantErr: ErrSyntax}, - {input: `"0x01zz01"`, wantErr: ErrSyntax}, + {input: "", wantErr: errJSONEOF}, + {input: "null", wantErr: errNonString(bytesT)}, + {input: "10", wantErr: errNonString(bytesT)}, + {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bytesT)}, + {input: `"0x0"`, wantErr: wrapTypeError(ErrOddLength, bytesT)}, + {input: `"0xxx"`, wantErr: wrapTypeError(ErrSyntax, bytesT)}, + {input: `"0x01zz01"`, wantErr: wrapTypeError(ErrSyntax, bytesT)}, // valid encoding {input: `""`, want: referenceBytes("")}, @@ -80,7 +84,7 @@ var unmarshalBytesTests = []unmarshalTest{ func TestUnmarshalBytes(t *testing.T) { for _, test := range unmarshalBytesTests { var v Bytes - err := v.UnmarshalJSON([]byte(test.input)) + err := json.Unmarshal([]byte(test.input), &v) if !checkError(t, test.input, err, test.wantErr) { continue } @@ -104,7 +108,7 @@ func BenchmarkUnmarshalBytes(b *testing.B) { func TestMarshalBytes(t *testing.T) { for _, test := range encodeBytesTests { in := test.input.([]byte) - out, err := Bytes(in).MarshalJSON() + out, err := json.Marshal(Bytes(in)) if err != nil { t.Errorf("%x: %v", in, err) continue @@ -122,14 +126,18 @@ func TestMarshalBytes(t *testing.T) { var unmarshalBigTests = []unmarshalTest{ // invalid encoding - {input: "", wantErr: errNonString}, - {input: "null", wantErr: errNonString}, - {input: "10", wantErr: errNonString}, - {input: `"0"`, wantErr: ErrMissingPrefix}, - {input: `"0x"`, wantErr: ErrEmptyNumber}, - {input: `"0x01"`, wantErr: ErrLeadingZero}, - {input: `"0xx"`, wantErr: ErrSyntax}, - {input: `"0x1zz01"`, wantErr: ErrSyntax}, + {input: "", wantErr: errJSONEOF}, + {input: "null", wantErr: errNonString(bigT)}, + {input: "10", wantErr: errNonString(bigT)}, + {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bigT)}, + {input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, bigT)}, + {input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, bigT)}, + {input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, bigT)}, + {input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, bigT)}, + { + input: `"0x10000000000000000000000000000000000000000000000000000000000000000"`, + wantErr: wrapTypeError(ErrBig256Range, bigT), + }, // valid encoding {input: `""`, want: big.NewInt(0)}, @@ -153,7 +161,7 @@ var unmarshalBigTests = []unmarshalTest{ func TestUnmarshalBig(t *testing.T) { for _, test := range unmarshalBigTests { var v Big - err := v.UnmarshalJSON([]byte(test.input)) + err := json.Unmarshal([]byte(test.input), &v) if !checkError(t, test.input, err, test.wantErr) { continue } @@ -177,7 +185,7 @@ func BenchmarkUnmarshalBig(b *testing.B) { func TestMarshalBig(t *testing.T) { for _, test := range encodeBigTests { in := test.input.(*big.Int) - out, err := (*Big)(in).MarshalJSON() + out, err := json.Marshal((*Big)(in)) if err != nil { t.Errorf("%d: %v", in, err) continue @@ -195,15 +203,15 @@ func TestMarshalBig(t *testing.T) { var unmarshalUint64Tests = []unmarshalTest{ // invalid encoding - {input: "", wantErr: errNonString}, - {input: "null", wantErr: errNonString}, - {input: "10", wantErr: errNonString}, - {input: `"0"`, wantErr: ErrMissingPrefix}, - {input: `"0x"`, wantErr: ErrEmptyNumber}, - {input: `"0x01"`, wantErr: ErrLeadingZero}, - {input: `"0xfffffffffffffffff"`, wantErr: ErrUint64Range}, - {input: `"0xx"`, wantErr: ErrSyntax}, - {input: `"0x1zz01"`, wantErr: ErrSyntax}, + {input: "", wantErr: errJSONEOF}, + {input: "null", wantErr: errNonString(uint64T)}, + {input: "10", wantErr: errNonString(uint64T)}, + {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, uint64T)}, + {input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, uint64T)}, + {input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, uint64T)}, + {input: `"0xfffffffffffffffff"`, wantErr: wrapTypeError(ErrUint64Range, uint64T)}, + {input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, uint64T)}, + {input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, uint64T)}, // valid encoding {input: `""`, want: uint64(0)}, @@ -219,7 +227,7 @@ var unmarshalUint64Tests = []unmarshalTest{ func TestUnmarshalUint64(t *testing.T) { for _, test := range unmarshalUint64Tests { var v Uint64 - err := v.UnmarshalJSON([]byte(test.input)) + err := json.Unmarshal([]byte(test.input), &v) if !checkError(t, test.input, err, test.wantErr) { continue } @@ -241,7 +249,7 @@ func BenchmarkUnmarshalUint64(b *testing.B) { func TestMarshalUint64(t *testing.T) { for _, test := range encodeUint64Tests { in := test.input.(uint64) - out, err := Uint64(in).MarshalJSON() + out, err := json.Marshal(Uint64(in)) if err != nil { t.Errorf("%d: %v", in, err) continue diff --git a/eth/core/vm/interpreter.go b/eth/core/vm/interpreter.go index 669b9b5..2d6d990 100644 --- a/eth/core/vm/interpreter.go +++ b/eth/core/vm/interpreter.go @@ -114,7 +114,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { } // Edit by zhongan - cfg.JumpTable = byzantiumInstructionSet + cfg.JumpTable = constantinopleInstructionSet } return &EVMInterpreter{ diff --git a/eth/core/vm/runtime/runtime_example_test.go b/eth/core/vm/runtime/runtime_example_test.go index 514d85d..d8f2c1e 100644 --- a/eth/core/vm/runtime/runtime_example_test.go +++ b/eth/core/vm/runtime/runtime_example_test.go @@ -20,11 +20,12 @@ import ( "fmt" "github.com/dappledger/AnnChain/eth/common" + "github.com/dappledger/AnnChain/eth/core/vm" "github.com/dappledger/AnnChain/eth/core/vm/runtime" ) func ExampleExecute() { - ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil) + ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, &runtime.Config{EVMConfig: vm.Config{EVMGasLimit: 100000000}}) if err != nil { fmt.Println(err) } diff --git a/eth/core/vm/runtime/runtime_test.go b/eth/core/vm/runtime/runtime_test.go index d418cd3..fac2a68 100644 --- a/eth/core/vm/runtime/runtime_test.go +++ b/eth/core/vm/runtime/runtime_test.go @@ -83,7 +83,7 @@ func TestExecute(t *testing.T) { byte(vm.PUSH1), 32, byte(vm.PUSH1), 0, byte(vm.RETURN), - }, nil, nil) + }, nil, &Config{EVMConfig: vm.Config{EVMGasLimit: 100000000}}) if err != nil { t.Fatal("didn't expect error", err) } @@ -106,7 +106,7 @@ func TestCall(t *testing.T) { byte(vm.RETURN), }) - ret, _, err := Call(address, nil, &Config{State: state}) + ret, _, err := Call(address, nil, &Config{State: state, EVMConfig: vm.Config{EVMGasLimit: 100000000}}) if err != nil { t.Fatal("didn't expect error", err) } diff --git a/gemmill/angine.go b/gemmill/angine.go index 9331ca3..074114e 100644 --- a/gemmill/angine.go +++ b/gemmill/angine.go @@ -27,6 +27,11 @@ import ( "sync" "time" + rpcserver "github.com/dappledger/AnnChain/gemmill/rpc/server" + + "github.com/dappledger/AnnChain/gemmill/consensus/pbft" + "github.com/dappledger/AnnChain/gemmill/consensus/raft" + "go.uber.org/zap" "github.com/pkg/errors" @@ -71,7 +76,7 @@ type Angine struct { dataArchive *archive.Archive conf *viper.Viper txPool types.TxPool - consensus *consensus.ConsensusState + consensus consensus.Engine traceRouter *trace.Router stateMachine *state.State p2pSwitch *p2p.Switch @@ -86,6 +91,8 @@ type Angine struct { getAdminVote func([]byte, *types.Validator) ([]byte, error) queryPayLoadTxParser func([]byte) ([]byte, error) + + apis []map[string]*rpcserver.RPCFunc } type Tunes struct { @@ -192,6 +199,10 @@ func NewAngine(app types.Application, tune *Tunes) (angine *Angine, err error) { return } +func (a *Angine) APIs() []map[string]*rpcserver.RPCFunc { + return a.apis +} + func (a *Angine) SetQueryPayLoadTxParser(fn func([]byte) ([]byte, error)) { a.queryPayLoadTxParser = fn } @@ -260,10 +271,6 @@ func closeDBs(a *Angine) { } } -// func (e *Angine) SetAdminVoteRPC(f func([]byte, *types.Validator) ([]byte, error)) { -// e.getAdminVote = f -// } - func (ang *Angine) assembleStateMachine(stateM *state.State) { conf := ang.tune.Conf conf.Set("chain_id", stateM.ChainID) @@ -283,14 +290,37 @@ func (ang *Angine) assembleStateMachine(stateM *state.State) { } memReactor := mempool.NewTxReactor(conf, txPool) - consensusState := consensus.NewConsensusState(conf, stateM, blockStore, txPool) - consensusState.SetPrivValidator(ang.privValidator) - consensusReactor := consensus.NewConsensusReactor(consensusState, fastSync) - consensusState.BindReactor(consensusReactor) + var consensusEngine consensus.Engine + + if conf.Get("consensus") == "raft" { + + consensusState, err := raft.NewConsensusState(conf, *ang.eventSwitch, blockStore, stateM, txPool, ang.privValidator) + if err != nil { + log.Fatal("assembleStateMachine with raft err", zap.Error(err)) + } + consensusEngine = consensusState + + bcReactor.SetBlockVerifier(func(bID types.BlockID, h int64, lc *types.Commit) error { return nil }) + + ang.apis = append(ang.apis, consensusState.NewPublicAPI().API()) + + } else { + + consensusState := pbft.NewConsensusState(conf, stateM, blockStore, txPool) + consensusState.SetPrivValidator(ang.privValidator) + + consensusReactor := pbft.NewConsensusReactor(consensusState, fastSync) + consensusState.BindReactor(consensusReactor) + ang.p2pSwitch.AddReactor("CONSENSUS", consensusReactor) + setEventSwitch(*ang.eventSwitch, bcReactor, memReactor, consensusReactor) + + consensusEngine = consensusState + + bcReactor.SetBlockVerifier(func(bID types.BlockID, h int64, lc *types.Commit) error { + return stateM.Validators.VerifyCommit(stateM.ChainID, bID, h, lc) + }) + } - bcReactor.SetBlockVerifier(func(bID types.BlockID, h int64, lc *types.Commit) error { - return stateM.Validators.VerifyCommit(stateM.ChainID, bID, h, lc) - }) bcReactor.SetBlockExecuter(func(blk *types.Block, pst *types.PartSet, c *types.Commit) error { blockStore.SaveBlock(blk, pst, c) if err := stateM.ApplyBlock(*ang.eventSwitch, blk, pst.Header(), txPool, -1); err != nil { @@ -304,7 +334,6 @@ func (ang *Angine) assembleStateMachine(stateM *state.State) { ang.p2pSwitch.AddReactor("MEMPOOL", memReactor) ang.p2pSwitch.AddReactor("BLOCKCHAIN", bcReactor) - ang.p2pSwitch.AddReactor("CONSENSUS", consensusReactor) var addrBook *p2p.AddrBook if conf.GetBool("pex_reactor") { @@ -317,16 +346,17 @@ func (ang *Angine) assembleStateMachine(stateM *state.State) { ang.p2pSwitch.SetAuthByCA(authByCA(conf, &stateM.Validators)) } - setEventSwitch(*ang.eventSwitch, bcReactor, memReactor, consensusReactor) + setEventSwitch(*ang.eventSwitch, bcReactor, memReactor, consensusEngine) ang.blockstore = blockStore - ang.consensus = consensusState + ang.consensus = consensusEngine ang.txPool = txPool ang.stateMachine = stateM ang.genesis = stateM.GenesisDoc ang.addrBook = addrBook ang.stateMachine.SetBlockExecutable(ang) + ang.stateMachine.SetBlockVerifier(consensusEngine) ang.InitPlugins() for _, p := range ang.plugins { @@ -633,10 +663,14 @@ func (e *Angine) GetNumPeers() int { } func (e *Angine) GetConsensusStateInfo() (string, []string) { - roundState := e.consensus.GetRoundState() + c, ok := e.consensus.(*pbft.ConsensusState) + if !ok { + return "", nil + } + roundState := c.GetRoundState() peerRoundStates := make([]string, 0, e.p2pSwitch.Peers().Size()) for _, p := range e.p2pSwitch.Peers().List() { - peerState := p.Data.Get(types.PeerStateKey).(*consensus.PeerState) + peerState := p.Data.Get(types.PeerStateKey).(*pbft.PeerState) peerRoundState := peerState.GetRoundState() peerRoundStateStr := p.Key + ":" + string(wire.JSONBytes(peerRoundState)) peerRoundStates = append(peerRoundStates, peerRoundStateStr) @@ -1145,7 +1179,13 @@ const ( func (ag *Angine) HealthStatus() int { cur := time.Now().Unix() - lcommitTime := ag.consensus.CommitTime.Unix() + c, ok := ag.consensus.(*pbft.ConsensusState) + if !ok { + return http.StatusNoContent + } + + lcommitTime := c.CommitTime.Unix() + if cur > lcommitTime+TIME_OUT_HEALTH { return int(http.StatusInternalServerError) } diff --git a/gemmill/consensus/engine.go b/gemmill/consensus/engine.go new file mode 100644 index 0000000..0c1a859 --- /dev/null +++ b/gemmill/consensus/engine.go @@ -0,0 +1,9 @@ +package consensus + +import "github.com/dappledger/AnnChain/gemmill/types" + +type Engine interface { + GetValidators() (int64, []*types.Validator) + SetEventSwitch(types.EventSwitch) + ValidateBlock(*types.Block) error +} diff --git a/gemmill/consensus/README.md b/gemmill/consensus/pbft/README.md similarity index 100% rename from gemmill/consensus/README.md rename to gemmill/consensus/pbft/README.md diff --git a/gemmill/consensus/common.go b/gemmill/consensus/pbft/common.go similarity index 98% rename from gemmill/consensus/common.go rename to gemmill/consensus/pbft/common.go index 282ae68..80b8b3e 100644 --- a/gemmill/consensus/common.go +++ b/gemmill/consensus/pbft/common.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package consensus +package pbft import ( "github.com/dappledger/AnnChain/gemmill/types" diff --git a/gemmill/consensus/height_vote_set.go b/gemmill/consensus/pbft/height_vote_set.go similarity index 99% rename from gemmill/consensus/height_vote_set.go rename to gemmill/consensus/pbft/height_vote_set.go index fa65f02..f94ff88 100644 --- a/gemmill/consensus/height_vote_set.go +++ b/gemmill/consensus/pbft/height_vote_set.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package consensus +package pbft import ( "strings" diff --git a/gemmill/consensus/reactor.go b/gemmill/consensus/pbft/reactor.go similarity index 99% rename from gemmill/consensus/reactor.go rename to gemmill/consensus/pbft/reactor.go index b4ad1cc..c75deaf 100644 --- a/gemmill/consensus/reactor.go +++ b/gemmill/consensus/pbft/reactor.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package consensus +package pbft import ( "bytes" @@ -390,9 +390,9 @@ func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) { func makeRoundStepMessages(rs *RoundState) (nrsMsg *NewRoundStepMessage, csMsg *CommitStepMessage) { nrsMsg = &NewRoundStepMessage{ - Height: rs.Height, - Round: rs.Round, - Step: rs.Step, + Height: rs.Height, + Round: rs.Round, + Step: rs.Step, SecondsSinceStartTime: int(time.Now().Sub(rs.StartTime).Seconds()), LastCommitRound: rs.LastCommit.Round(), } diff --git a/gemmill/consensus/replay.go b/gemmill/consensus/pbft/replay.go similarity index 99% rename from gemmill/consensus/replay.go rename to gemmill/consensus/pbft/replay.go index 975a121..61f6e03 100644 --- a/gemmill/consensus/replay.go +++ b/gemmill/consensus/pbft/replay.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package consensus +package pbft import ( "bufio" diff --git a/gemmill/consensus/state.go b/gemmill/consensus/pbft/state.go similarity index 98% rename from gemmill/consensus/state.go rename to gemmill/consensus/pbft/state.go index bbc300f..b445bf2 100644 --- a/gemmill/consensus/state.go +++ b/gemmill/consensus/pbft/state.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package consensus +package pbft import ( "bytes" @@ -1595,3 +1595,39 @@ func CompareHRS(h1, r1 int64, s1 RoundStepType, h2, r2 int64, s2 RoundStepType) } return 0 } + +func (cs *ConsensusState) ValidateBlock(block *types.Block) error { + + // Basic block validation. + s := cs.state + err := block.ValidateBasic(s.ChainID, s.LastBlockHeight, s.LastBlockID, s.LastBlockTime, s.AppHash, s.ReceiptsHash) + if err != nil { + return err + } + + if err := block.ValidateCommit(); err != nil { + return err + } + + if !s.Validators.HasAddress(block.ProposerAddress) { + return fmt.Errorf("Block.Header.ProposerAddress, %X, is not a validator", block.ProposerAddress) + } + + // Validate block LastCommit. + if block.Height == 1 { + if len(block.LastCommit.Precommits) != 0 { + return errors.New("Block at height 1 (first block) should have no LastCommit precommits") + } + } else { + if len(block.LastCommit.Precommits) != s.LastValidators.Size() { + return errors.New(gcmn.Fmt("Invalid block commit size. Expected %v, got %v", + s.LastValidators.Size(), len(block.LastCommit.Precommits))) + } + err := s.LastValidators.VerifyCommit( + s.ChainID, s.LastBlockID, block.Height-1, block.LastCommit) + if err != nil { + return err + } + } + return nil +} diff --git a/gemmill/consensus/test_data/README.md b/gemmill/consensus/pbft/test_data/README.md similarity index 100% rename from gemmill/consensus/test_data/README.md rename to gemmill/consensus/pbft/test_data/README.md diff --git a/gemmill/consensus/test_data/build.sh b/gemmill/consensus/pbft/test_data/build.sh similarity index 100% rename from gemmill/consensus/test_data/build.sh rename to gemmill/consensus/pbft/test_data/build.sh diff --git a/gemmill/consensus/test_data/empty_block.cswal b/gemmill/consensus/pbft/test_data/empty_block.cswal similarity index 100% rename from gemmill/consensus/test_data/empty_block.cswal rename to gemmill/consensus/pbft/test_data/empty_block.cswal diff --git a/gemmill/consensus/test_data/small_block1.cswal b/gemmill/consensus/pbft/test_data/small_block1.cswal similarity index 100% rename from gemmill/consensus/test_data/small_block1.cswal rename to gemmill/consensus/pbft/test_data/small_block1.cswal diff --git a/gemmill/consensus/test_data/small_block2.cswal b/gemmill/consensus/pbft/test_data/small_block2.cswal similarity index 100% rename from gemmill/consensus/test_data/small_block2.cswal rename to gemmill/consensus/pbft/test_data/small_block2.cswal diff --git a/gemmill/consensus/ticker.go b/gemmill/consensus/pbft/ticker.go similarity index 99% rename from gemmill/consensus/ticker.go rename to gemmill/consensus/pbft/ticker.go index 6815568..d6cd94f 100644 --- a/gemmill/consensus/ticker.go +++ b/gemmill/consensus/pbft/ticker.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package consensus +package pbft import ( "time" diff --git a/gemmill/consensus/version.go b/gemmill/consensus/pbft/version.go similarity index 98% rename from gemmill/consensus/version.go rename to gemmill/consensus/pbft/version.go index 9fe8c62..2d87195 100644 --- a/gemmill/consensus/version.go +++ b/gemmill/consensus/pbft/version.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package consensus +package pbft import ( gcmn "github.com/dappledger/AnnChain/gemmill/modules/go-common" diff --git a/gemmill/consensus/wal.go b/gemmill/consensus/pbft/wal.go similarity index 99% rename from gemmill/consensus/wal.go rename to gemmill/consensus/pbft/wal.go index 9de7da0..c1d19f6 100644 --- a/gemmill/consensus/wal.go +++ b/gemmill/consensus/pbft/wal.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package consensus +package pbft import ( "time" diff --git a/gemmill/consensus/raft/api.go b/gemmill/consensus/raft/api.go new file mode 100755 index 0000000..632b819 --- /dev/null +++ b/gemmill/consensus/raft/api.go @@ -0,0 +1,163 @@ +package raft + +import ( + "encoding/hex" + "encoding/json" + "fmt" + + "github.com/dappledger/AnnChain/gemmill/go-crypto" + "github.com/dappledger/AnnChain/gemmill/modules/go-log" + rpcclient "github.com/dappledger/AnnChain/gemmill/rpc/client" + rpcserver "github.com/dappledger/AnnChain/gemmill/rpc/server" + "github.com/dappledger/AnnChain/gemmill/types" + "github.com/hashicorp/raft" + "github.com/pkg/errors" + "go.uber.org/zap" +) + +type PublicAPI struct { + *ConsensusState +} + +func (p *PublicAPI) API() map[string]*rpcserver.RPCFunc { + + return map[string]*rpcserver.RPCFunc{ + "raft/role": rpcserver.NewRPCFunc(p.Role, ""), + "raft/leader": rpcserver.NewRPCFunc(p.Leader, ""), + "raft/add_peer": rpcserver.NewRPCFunc(p.AddPeer, "addr,rpc,pubKey"), + "raft/remove_peer": rpcserver.NewRPCFunc(p.RemovePeer, "pubKey"), + "raft/stats": rpcserver.NewRPCFunc(p.Stats, ""), + } +} + +func (p *PublicAPI) Role() (string, error) { + + return p.rawRaft.State().String(), nil +} + +type LeaderResult struct { + ID string + Address string + RPC string +} + +func (p *PublicAPI) Leader() (*LeaderResult, error) { + + if p.rawRaft.Leader() == "" { + return nil, errors.New("not found leader") + + } + + peer := p.conf.clusterConfig.FindByBindAddress(string(p.rawRaft.Leader())) + if peer == nil { + return nil, errors.New("not found leader") + } + + rpc, err := tryResolveTCPAddr(peer.RPC) + if err != nil { + return nil, err + } + bind, err := tryResolveTCPAddr(peer.Bind) + if err != nil { + return nil, err + } + return &LeaderResult{ID: fmt.Sprintf("%x", peer.PubKey.Bytes()), Address: string(bind), RPC: rpc}, nil +} + +type AddPeerResult struct{} + +func (p *PublicAPI) AddPeer(addr, rpc string, pubKey string) (*AddPeerResult, error) { + + pubKeyBytes, err := hex.DecodeString(pubKey) + if err != nil { + return nil, fmt.Errorf("invalid pubKey, must be hex encode") + } + + if p.rawRaft.State() == raft.Leader { + + for _, peer := range p.conf.clusterConfig.Peers { + + if p.conf.clusterConfig.Local.PubKey.Equals(peer.PubKey) { + continue + } + + cli := rpcclient.NewClientJSONRPC(peer.RPC) + r := AddPeerResult{} + if _, err := cli.Call("raft/add_peer", []interface{}{addr, rpc, pubKey}, &r); err != nil { + log.Error("call raft add peer", zap.String("rpc", peer.RPC), zap.Error(err)) + } + } + i := p.rawRaft.AddVoter(raft.ServerID(pubKey), raft.ServerAddress(addr), 0, 0) + if err := i.Error(); err != nil { + return nil, err + } + } + + publicKey, err := crypto.PubKeyFromBytes(pubKeyBytes) + if err != nil { + return nil, err + } + + if p.conf.clusterConfig.AddPeer(Peer{PubKey: publicKey, RPC: rpc, Bind: addr}) { + if err := p.conf.clusterConfig.Save(); err != nil { + return nil, err + } + } + + p.ConsensusState.fsm.state.Validators.Add(&types.Validator{ + Address: publicKey.Address(), + PubKey: publicKey, + }) + return &AddPeerResult{}, nil +} + +func (p *PublicAPI) Stats() (string, error) { + bs, err := json.MarshalIndent(p.rawRaft.Stats(), "", "\t") + return string(bs), err +} + +type RemovePeerResult struct{} + +func (p *PublicAPI) RemovePeer(pubKey string) (*RemovePeerResult, error) { + + pubKeyBytes, err := hex.DecodeString(pubKey) + if err != nil { + return nil, fmt.Errorf("invalid pubKey, must be hex encode") + } + + if p.rawRaft.State() == raft.Leader { + + for _, peer := range p.conf.clusterConfig.Peers { + + if p.conf.clusterConfig.Local.PubKey.Equals(peer.PubKey) { + continue + } + + cli := rpcclient.NewClientJSONRPC(peer.RPC) + r := RemovePeerResult{} + if _, err := cli.Call("raft/remove_peer", []interface{}{pubKey}, &r); err != nil { + log.Error("call peers remove peer", zap.String("rpc", peer.RPC), zap.Error(err)) + } + } + + i := p.rawRaft.RemoveServer(raft.ServerID(pubKey), 0, 0) + if err := i.Error(); err != nil { + return nil, err + } + } + + publicKey, err := crypto.PubKeyFromBytes(pubKeyBytes) + if err != nil { + return nil, err + } + + if p.conf.clusterConfig.Remove(publicKey) { + if err := p.conf.clusterConfig.Save(); err != nil { + return nil, err + } + } + + // We can not remove it from validators, otherwise when a new node replay txs, it will be got errors + // p.ConsensusState.fsm.state.Validators.Remove(publicKey.Address()) + return &RemovePeerResult{}, nil +} diff --git a/gemmill/consensus/raft/fsm.go b/gemmill/consensus/raft/fsm.go new file mode 100755 index 0000000..9f2fe91 --- /dev/null +++ b/gemmill/consensus/raft/fsm.go @@ -0,0 +1,123 @@ +package raft + +import ( + "bytes" + "io" + "io/ioutil" + "sync" + "time" + + "go.uber.org/zap" + + "github.com/hashicorp/raft" + + "github.com/dappledger/AnnChain/gemmill/blockchain" + "github.com/dappledger/AnnChain/gemmill/go-wire" + "github.com/dappledger/AnnChain/gemmill/state" + "github.com/dappledger/AnnChain/gemmill/types" + + "github.com/dappledger/AnnChain/gemmill/modules/go-log" +) + +type BlockChainFSM struct { + conf *config + evsw types.EventSwitch + blockExecutable state.IBlockExecutable + mempool types.TxPool + state *state.State + appliedCh chan *types.Block + blockStore *blockchain.BlockStore + mut sync.Mutex +} + +func newBlockChainFSM(conf *config, mempool types.TxPool, blockStore *blockchain.BlockStore, state *state.State) *BlockChainFSM { + + return &BlockChainFSM{ + conf: conf, + mempool: mempool, + state: state, + appliedCh: make(chan *types.Block), + blockStore: blockStore, + } +} + +func (b *BlockChainFSM) SetEventSwitch(evsw types.EventSwitch) { + b.evsw = evsw +} + +func (b *BlockChainFSM) Apply(l *raft.Log) interface{} { + + start := time.Now() + r := bytes.NewBuffer(l.Data) + var err error + n := 0 + block := wire.ReadBinary(&types.Block{}, r, types.MaxBlockSize, &n, &err).(*types.Block) + partSet := types.NewPartSetFromData(l.Data, b.conf.blockPartSize) + + log.Debug("FSM.Apply start apply", zap.Int64("height", block.Height)) + if b.blockStore.Height()+1 != block.Height { + log.Warn("FSM.Apply, found dup block", zap.Int64("height", block.Height)) + return nil + } + b.blockStore.SaveBlock(block, partSet, &types.Commit{}) + + stateCopy := b.state.Copy() + if err := stateCopy.ApplyBlock(b.evsw, block, partSet.Header(), b.mempool, 0); err != nil { + log.Error("FSM.Apply state ApplyBlock", zap.Int64("height", block.Height), zap.Error(err)) + return err + } + stateCopy.Save() + + b.state = stateCopy + end := time.Now() + log.Info("BlockChainFSM Applied Block", zap.Int64("height", block.Height), zap.Int("txs num", len(block.Data.Txs)), zap.Duration("taken", end.Sub(start))) + + b.appliedCh <- block + return nil +} + +func (b *BlockChainFSM) createProposalBlock(proposerAddr []byte) *types.Block { + + txs := b.mempool.Reap(b.conf.blockSize) + + if len(txs) < b.conf.blockSize { + t1 := time.NewTimer(b.conf.emptyBlockInterval) + L1: + for { + select { + case <-t1.C: + break L1 + case <-time.After(time.Millisecond * 100): + txs = b.mempool.Reap(b.conf.blockSize) + if len(txs) > 0 { + t1.Stop() + break L1 + } + } + } + } + + h := b.state.LastBlockHeight + 1 + + block, _ := types.MakeBlock(h, b.state.ChainID, txs, nil, &types.Commit{}, proposerAddr, + b.state.LastBlockID, b.state.Validators.Hash(), b.state.AppHash, b.state.ReceiptsHash, b.conf.blockPartSize) + return block +} + +func (b *BlockChainFSM) Snapshot() (raft.FSMSnapshot, error) { + + return &BlockChainSnapshot{ + Height: b.state.LastBlockHeight, + Hash: b.state.LastBlockID.Hash, + }, nil +} + +func (b *BlockChainFSM) Restore(r io.ReadCloser) error { + + io.Copy(ioutil.Discard, r) + return nil +} + +func (b *BlockChainFSM) AppliedCh() <-chan *types.Block { + return b.appliedCh +} diff --git a/gemmill/consensus/raft/peer.go b/gemmill/consensus/raft/peer.go new file mode 100755 index 0000000..b2184c4 --- /dev/null +++ b/gemmill/consensus/raft/peer.go @@ -0,0 +1,150 @@ +package raft + +import ( + "fmt" + "io/ioutil" + "net" + "os" + "time" + + "github.com/dappledger/AnnChain/gemmill/go-crypto" + "github.com/dappledger/AnnChain/gemmill/go-wire" + "github.com/hashicorp/raft" +) + +type Peer struct { + PubKey crypto.PubKey `json:"pub_key"` + RPC string `json:"rpc"` + Bind string `json:"bind"` +} + +type ClusterConfig struct { + filename string + + Local Peer `json:"local"` + Advertise string `json:"advertise"` + Peers []Peer `json:"peers"` +} + +func NewClusterConfig(filename string) (*ClusterConfig, error) { + + data, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + c := ClusterConfig{} + if err := wire.ReadJSONBytes(data, &c); err != nil { + return nil, err + } + c.filename = filename + return &c, nil +} + +func (c *ClusterConfig) String() string { + data := wire.JSONBytesPretty(c) + return string(data) +} + +func (c *ClusterConfig) Save() error { + + data := wire.JSONBytesPretty(c) + + tmp := c.filename + ".tmp" + if err := ioutil.WriteFile(tmp, data, 0644); err != nil { + return nil + } + + if err := os.Rename(tmp, c.filename); err != nil { + return err + } + return nil +} + +func (c *ClusterConfig) AddPeer(peer Peer) bool { + + for _, p := range c.Peers { + if p.Bind == peer.Bind { + return false + } + if p.PubKey.Equals(peer.PubKey) { + return false + } + } + c.Peers = append(c.Peers, peer) + return true +} + +func (c *ClusterConfig) FindByBindAddress(bind string) *Peer { + + for i, p := range c.Peers { + if p.Bind == bind { + return &c.Peers[i] + } + + n, err := tryResolveTCPAddr(p.Bind) + if err != nil { + continue + } + + if bind == n { + return &c.Peers[i] + } + } + return nil +} + +func (c *ClusterConfig) Remove(pubKey crypto.PubKey) bool { + + for i, p := range c.Peers { + if p.PubKey.Equals(pubKey) { + var peers []Peer + if i > 0 { + peers = c.Peers[:i] + } + if i != len(c.Peers)-1 { + peers = append(peers, c.Peers[i+1:]...) + } + c.Peers = peers + return true + } + } + return false +} + +func (c *ClusterConfig) LocalServer() raft.Server { + + return raft.Server{ + ID: raft.ServerID(fmt.Sprintf("%x", c.Local.PubKey.Bytes())), + Address: raft.ServerAddress(c.Local.Bind), + } +} + +func tryResolveTCPAddr(bind string) (string, error) { + + for i := 0; i < 10; i++ { + n, err := net.ResolveTCPAddr("tcp", bind) + if err != nil { + time.Sleep(time.Second) + continue + } + return n.String(), nil + } + return "", fmt.Errorf("resolveTCPAddr %v err", bind) +} + +func (c *ClusterConfig) Server() ([]raft.Server, error) { + + servers := make([]raft.Server, 0, len(c.Peers)) + for _, c := range c.Peers { + + n, err := tryResolveTCPAddr(c.Bind) + if err != nil { + return nil, fmt.Errorf("invalid address, err %v", err) + } + servers = append(servers, raft.Server{ + ID: raft.ServerID(fmt.Sprintf("%x", c.PubKey.Bytes())), + Address: raft.ServerAddress(n), + }) + } + return servers, nil +} diff --git a/gemmill/consensus/raft/secret_tcp_transport.go b/gemmill/consensus/raft/secret_tcp_transport.go new file mode 100755 index 0000000..5d734f0 --- /dev/null +++ b/gemmill/consensus/raft/secret_tcp_transport.go @@ -0,0 +1,91 @@ +package raft + +import ( + "errors" + "io" + "net" + "time" + + "github.com/dappledger/AnnChain/gemmill/go-crypto" + "github.com/dappledger/AnnChain/gemmill/modules/go-log" + "github.com/dappledger/AnnChain/gemmill/p2p" + "github.com/hashicorp/raft" + "go.uber.org/zap" +) + +type SecretTCPStreamLayer struct { + privKey crypto.PrivKey + advertise net.Addr + listener *net.TCPListener +} + +func (t *SecretTCPStreamLayer) Accept() (net.Conn, error) { + conn, err := t.listener.Accept() + if err != nil { + return nil, err + } + return p2p.MakeSecretConnection(conn, t.privKey) +} + +func (t *SecretTCPStreamLayer) Close() error { + return t.listener.Close() +} + +func (t *SecretTCPStreamLayer) Addr() net.Addr { + + if t.advertise != nil { + return t.advertise + } + return t.listener.Addr() +} + +func (t *SecretTCPStreamLayer) Dial(address raft.ServerAddress, timeout time.Duration) (conn net.Conn, err error) { + + for i := 0; i < 3; i++ { + + conn, err = net.DialTimeout("tcp", string(address), timeout) + if err != nil { + log.Error("SecretTCPStreamLayer.Dial", zap.String("address", string(address)), zap.Error(err), zap.Int("try", i)) + time.Sleep(time.Second * 2) + continue + } + return p2p.MakeSecretConnection(conn, t.privKey) + } + return +} + +// NewTCPTransport returns a NetworkTransport that is built on top of +// a Secret TCP streaming transport layer. +func NewSecretTCPTransport( + bindAddr string, + advertise net.Addr, + maxPool int, + timeout time.Duration, + logOutput io.Writer, + privKey crypto.PrivKey, +) (*raft.NetworkTransport, error) { + + list, err := net.Listen("tcp", bindAddr) + if err != nil { + return nil, err + } + + // Create stream + stream := &SecretTCPStreamLayer{ + privKey: privKey, + advertise: advertise, + listener: list.(*net.TCPListener), + } + + // Verify that we have a usable advertise address + addr, ok := stream.Addr().(*net.TCPAddr) + if !ok { + list.Close() + return nil, errors.New("not tcp") + } + if addr.IP.IsUnspecified() { + list.Close() + return nil, errors.New("errNotAdvertisable") + } + return raft.NewNetworkTransport(stream, maxPool, timeout, logOutput), nil +} diff --git a/gemmill/consensus/raft/snapshot.go b/gemmill/consensus/raft/snapshot.go new file mode 100755 index 0000000..e7cfa04 --- /dev/null +++ b/gemmill/consensus/raft/snapshot.go @@ -0,0 +1,20 @@ +package raft + +import ( + "fmt" + "github.com/hashicorp/raft" +) + +type BlockChainSnapshot struct { + Height int64 + Hash []byte +} + +func (s *BlockChainSnapshot) Persist(sink raft.SnapshotSink) error { + _, err := sink.Write([]byte(fmt.Sprintf("%d-%x", s.Height, s.Hash))) + return err +} + +func (s *BlockChainSnapshot) Release() { + // nothing to do +} diff --git a/gemmill/consensus/raft/state.go b/gemmill/consensus/raft/state.go new file mode 100755 index 0000000..6d848c1 --- /dev/null +++ b/gemmill/consensus/raft/state.go @@ -0,0 +1,301 @@ +package raft + +import ( + "errors" + "fmt" + "io" + "net" + "os" + "path" + "sync" + "sync/atomic" + "time" + + "go.uber.org/zap" + + "github.com/dappledger/AnnChain/gemmill/blockchain" + "github.com/dappledger/AnnChain/gemmill/go-crypto" + "github.com/dappledger/AnnChain/gemmill/go-wire" + "github.com/dappledger/AnnChain/gemmill/modules/go-common" + "github.com/dappledger/AnnChain/gemmill/modules/go-log" + "github.com/dappledger/AnnChain/gemmill/state" + "github.com/dappledger/AnnChain/gemmill/types" + "github.com/hashicorp/raft" + "github.com/hashicorp/raft-boltdb" + "github.com/spf13/viper" +) + +type config struct { + raftDir string // store logs, snapshot, peers + peersDir string + snapshotDir string + snapshotLog io.WriteCloser + transLog io.WriteCloser + raftLog io.WriteCloser + blockSize int + blockPartSize int + emptyBlockInterval time.Duration + clusterConfig *ClusterConfig + logStore raft.LogStore + stableStore raft.StableStore +} + +func initConfig(conf *viper.Viper) (*config, error) { + + runtime := conf.GetString("runtime") + raftDir := path.Join(runtime, "raft") + c := config{ + raftDir: raftDir, + peersDir: runtime, // peers stored in $runtime/peers.json + snapshotDir: path.Join(raftDir, "snapshot"), + } + + raftLogDir := path.Join(raftDir, "logs") + fs, err := os.Stat(raftLogDir) + if err != nil { + if os.IsNotExist(err) { + if err := os.MkdirAll(raftLogDir, os.ModeDir|0777); err != nil { + return nil, err + } + } else { + return nil, err + } + } else if !fs.IsDir() { + return nil, errors.New("raftDir is not empty and not dir") + } + + { + fname := path.Join(runtime, "raft", "logs", "snapshot.log") + file, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + return nil, err + } + c.snapshotLog = file + + logStore, err := raftboltdb.NewBoltStore(path.Join(runtime, "raft", "raft-log.bolt")) + if err != nil { + return nil, err + } + c.logStore = logStore + + stableStore, err := raftboltdb.NewBoltStore(path.Join(runtime, "raft", "raft-stable.bolt")) + if err != nil { + return nil, err + } + c.stableStore = stableStore + } + + { + fname := path.Join(runtime, "raft", "logs", "trans.log") + file, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + return nil, err + } + c.transLog = file + } + + { + fname := path.Join(raftLogDir, "raft.log") + fp, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + return nil, err + } + c.raftLog = fp + } + + clusterConfig, err := NewClusterConfig(path.Join(runtime, "raft-cluster.json")) + if err != nil { + return nil, err + } + c.clusterConfig = clusterConfig + c.blockSize = conf.GetInt("block_size") + c.blockPartSize = conf.GetInt("block_part_size") + + c.emptyBlockInterval = conf.GetDuration("raft.empty_block_interval") + if c.emptyBlockInterval == 0 { + c.emptyBlockInterval = time.Second * 3 + } + + return &c, nil +} + +type ConsensusState struct { + *common.BaseService + rawRaft *raft.Raft + mempool types.TxPool + proposalCh chan *types.Block + evsw types.EventSwitch + privValidator *types.PrivValidator // for signing votes + conf *config + stop chan struct{} + fsm *BlockChainFSM + mtx sync.Mutex + isRunning uint32 +} + +func (cs *ConsensusState) OnStart() error { + cs.BaseService.OnStart() + + return nil +} + +func (cs *ConsensusState) OnStop() { + cs.BaseService.OnStop() + defer cs.conf.snapshotLog.Close() + defer cs.conf.transLog.Close() + cs.stop <- struct{}{} +} + +func (cs *ConsensusState) GetValidators() (int64, []*types.Validator) { + cs.mtx.Lock() + defer cs.mtx.Unlock() + + return cs.fsm.state.LastBlockHeight, cs.fsm.state.Validators.Copy().Validators +} + +func (cs *ConsensusState) SetEventSwitch(evsw types.EventSwitch) { + cs.mtx.Lock() + defer cs.mtx.Unlock() + cs.fsm.SetEventSwitch(evsw) +} + +func NewConsensusState(vconf *viper.Viper, evsw types.EventSwitch, blockStore *blockchain.BlockStore, state *state.State, mempool types.TxPool, privValidator *types.PrivValidator) (*ConsensusState, error) { + + raftConf := raft.DefaultConfig() + + config, err := initConfig(vconf) + if err != nil { + return nil, err + } + raftConf.LogOutput = config.raftLog + + fsm := newBlockChainFSM(config, mempool, blockStore, state) + + raftConf.LocalID = config.clusterConfig.LocalServer().ID + + var a net.Addr + if config.clusterConfig.Advertise != "" { + a, err = net.ResolveTCPAddr("tcp", config.clusterConfig.Advertise) + if err != nil { + return nil, err + } + } + + trans, err := NewSecretTCPTransport(config.clusterConfig.Local.Bind, a, 10, time.Second*10, config.transLog, privValidator.PrivKey) + if err != nil { + return nil, err + } + + fileSnap, err := raft.NewFileSnapshotStore(config.snapshotDir, 10, config.snapshotLog) + if err != nil { + return nil, err + } + + // memStore is good enough, because Raft Log is same with the block, and block stored in LevelDB. + // Besides, raft snapshot is useless, BlockChain sync system did it for snapshot. + //store := raft.NewInmemStore() + + rawRaft, err := raft.NewRaft(raftConf, fsm, config.logStore, config.stableStore, fileSnap, trans) + if err != nil { + return nil, err + } + server, err := config.clusterConfig.Server() + if err != nil { + return nil, err + } + configuration := raft.Configuration{Servers: server} + fmt.Println("server:", server) + + rawRaft.BootstrapCluster(configuration) + + cs := &ConsensusState{ + rawRaft: rawRaft, + mempool: mempool, + proposalCh: make(chan *types.Block), + evsw: evsw, + conf: config, + stop: make(chan struct{}), + fsm: fsm, + privValidator: privValidator, + } + cs.BaseService = common.NewBaseService("RaftConsensusState", cs) + + types.AddListenerForEvent(cs.evsw, "conS", types.EventStringSwitchToConsensus(), func(data types.TMEventData) { + go cs.run() + }) + + return cs, err +} + +func (cs *ConsensusState) getSignature(b *types.Block) (crypto.Signature, error) { + + return crypto.SignatureFromBytes(b.Extra) +} + +func (cs *ConsensusState) sign(b *types.Block) { + + b.Header.Extra = cs.privValidator.Sign(b.Hash()).Bytes() +} + +func (cs *ConsensusState) ValidateBlock(b *types.Block) error { + + s := cs.fsm.state + + if err := b.ValidateBasic(s.ChainID, s.LastBlockHeight, s.LastBlockID, s.LastBlockTime, s.AppHash, s.ReceiptsHash); err != nil { + return err + } + + sig, err := cs.getSignature(b) + if err != nil { + return err + } + + _, v := cs.fsm.state.Validators.GetByAddress(b.ProposerAddress) + if !v.PubKey.VerifyBytes(b.Hash(), sig) { + return errors.New(common.Fmt("Wrong Block.Signature. proposerAddress %x, hash %x, signature %v", b.ProposerAddress, b.Hash(), sig.String())) + } + + return nil +} + +func (cs *ConsensusState) run() { + + if !atomic.CompareAndSwapUint32(&cs.isRunning, 0, 1) { + return + } + +L1: + for { + select { + case _ = <-cs.stop: + break L1 + default: + switch cs.rawRaft.State() { + case raft.Follower: + select { + case _ = <-cs.fsm.AppliedCh(): + case _ = <-time.After(time.Second * 1): + } + case raft.Leader: + + start := time.Now() + b := cs.fsm.createProposalBlock(cs.privValidator.GetAddress()) + end := time.Now() + log.Debug("createProposalBlock", zap.Int64("height", b.Height), zap.Duration("taken", end.Sub(start))) + cs.sign(b) + + data := wire.BinaryBytes(b) + cs.rawRaft.Apply(data, time.Second*3) + + _ = <-cs.fsm.AppliedCh() + default: + time.Sleep(time.Second) + } + } + } + atomic.StoreUint32(&cs.isRunning, 0) +} + +func (cs *ConsensusState) NewPublicAPI() *PublicAPI { + return &PublicAPI{cs} +} diff --git a/gemmill/go-wire/cmd/wire/wire.go b/gemmill/go-wire/cmd/wire/wire.go index dc0d4e4..5593f74 100644 --- a/gemmill/go-wire/cmd/wire/wire.go +++ b/gemmill/go-wire/cmd/wire/wire.go @@ -19,8 +19,8 @@ import ( "os" "strings" - gcmn "github.com/dappledger/AnnChain/gemmill/modules/go-common" "github.com/dappledger/AnnChain/gemmill/go-wire/expr" + gcmn "github.com/dappledger/AnnChain/gemmill/modules/go-common" ) func main() { @@ -37,11 +37,11 @@ func main() { // fmt.Println(input) got, err := expr.ParseReader(input, strings.NewReader(input)) if err != nil { - Exit("Error parsing input: " + err.Error()) + gcmn.Exit("Error parsing input: " + err.Error()) } gotBytes, err := got.(expr.Byteful).Bytes() if err != nil { - Exit("Error serializing parsed input: " + err.Error()) + gcmn.Exit("Error serializing parsed input: " + err.Error()) } fmt.Println(gcmn.Fmt("%X", gotBytes)) diff --git a/gemmill/plugin/admin_op.go b/gemmill/plugin/admin_op.go index 77921f0..12710d6 100644 --- a/gemmill/plugin/admin_op.go +++ b/gemmill/plugin/admin_op.go @@ -98,7 +98,7 @@ func (s *AdminOp) DeliverTx(tx []byte, i int) (bool, error) { return false, s.ProcessAdminOP(cmd, nil) } -func (s *AdminOp) ExecTX(app AdminApp, tx []byte) (error) { +func (s *AdminOp) ExecTX(app AdminApp, tx []byte) error { data := agtypes.UnwrapTx(tx) cmd := &agtypes.AdminOPCmd{} if err := json.Unmarshal(data, cmd); err != nil { @@ -116,7 +116,7 @@ func (s *AdminOp) ExecBlock(p *ExecBlockParams) (*ExecBlockReturns, error) { // Run ExTxs of block for i, tx := range p.Block.Data.ExTxs { if _, err := s.DeliverTx(tx, i); err != nil { - return nil, fmt.Errorf("[Plugin AdminOp ExecBlock]:%s", zap.Error(err)) + return nil, fmt.Errorf("[Plugin AdminOp ExecBlock]:%s", err.Error()) } } diff --git a/gemmill/state/execution.go b/gemmill/state/execution.go index 270d0af..f7833f5 100644 --- a/gemmill/state/execution.go +++ b/gemmill/state/execution.go @@ -145,34 +145,7 @@ func (s *State) ValidateBlock(block *types.Block) error { } func (s *State) validateBlock(block *types.Block) error { - // Basic block validation. - err := block.ValidateBasic(s.ChainID, s.LastBlockHeight, s.LastBlockID, s.LastBlockTime, s.AppHash, s.ReceiptsHash) - if err != nil { - return err - } - - if !s.Validators.HasAddress(block.ProposerAddress) { - return fmt.Errorf("Block.Header.ProposerAddress, %X, is not a validator", block.ProposerAddress) - } - - // Validate block LastCommit. - if block.Height == 1 { - if len(block.LastCommit.Precommits) != 0 { - return errors.New("Block at height 1 (first block) should have no LastCommit precommits") - } - } else { - if len(block.LastCommit.Precommits) != s.LastValidators.Size() { - return errors.New(gcmn.Fmt("Invalid block commit size. Expected %v, got %v", - s.LastValidators.Size(), len(block.LastCommit.Precommits))) - } - err := s.LastValidators.VerifyCommit( - s.ChainID, s.LastBlockID, block.Height-1, block.LastCommit) - if err != nil { - return err - } - } - - return nil + return s.blockVerifier.ValidateBlock(block) } // ApplyBlock executes the block, then commits and updates the mempool atomically diff --git a/gemmill/state/state.go b/gemmill/state/state.go index 8708b12..125379d 100644 --- a/gemmill/state/state.go +++ b/gemmill/state/state.go @@ -42,6 +42,10 @@ type IBlockExecutable interface { EndBlock(*types.Block, events.Fireable, *types.PartSetHeader, []*types.ValidatorAttr, *types.ValidatorSet) error } +type BlockVerifier interface { + ValidateBlock(*types.Block) error +} + // NOTE: not goroutine-safe. type State struct { started bool @@ -50,6 +54,7 @@ type State struct { mtx sync.Mutex db dbm.DB blockExecutable IBlockExecutable + blockVerifier BlockVerifier // should not change GenesisDoc *types.GenesisDoc @@ -106,6 +111,7 @@ func (s *State) Copy() *State { return &State{ db: s.db, blockExecutable: s.blockExecutable, + blockVerifier: s.blockVerifier, GenesisDoc: s.GenesisDoc, ChainID: s.ChainID, @@ -191,6 +197,10 @@ func (s *State) Bytes() []byte { return buf.Bytes() } +func (s *State) SetBlockVerifier(verifier BlockVerifier) { + s.blockVerifier = verifier +} + // Mutate state variables to match block and validators // after running EndBlock func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader types.PartSetHeader, prevValSet, nextValSet *types.ValidatorSet) { diff --git a/gemmill/types/block.go b/gemmill/types/block.go index 0920273..a42f666 100644 --- a/gemmill/types/block.go +++ b/gemmill/types/block.go @@ -85,14 +85,6 @@ func (b *Block) ValidateBasic(chainID string, lastBlockHeight int64, lastBlockID if !b.LastBlockID.Equals(lastBlockID) { return errors.New(gcmn.Fmt("Wrong Block.Header.LastBlockID. Expected %v, got %v", lastBlockID, b.LastBlockID)) } - if !bytes.Equal(b.LastCommitHash, b.LastCommit.Hash()) { - return errors.New(gcmn.Fmt("Wrong Block.Header.LastCommitHash. Expected %X, got %X", b.LastCommitHash, b.LastCommit.Hash())) - } - if b.Header.Height != 1 { - if err := b.LastCommit.ValidateBasic(); err != nil { - return err - } - } if !bytes.Equal(b.DataHash, b.Data.Hash()) { return errors.New(gcmn.Fmt("Wrong Block.Header.DataHash. Expected %X, got %X", b.DataHash, b.Data.Hash())) } @@ -106,6 +98,20 @@ func (b *Block) ValidateBasic(chainID string, lastBlockHeight int64, lastBlockID return nil } +func (b *Block) ValidateCommit() error { + + if !bytes.Equal(b.LastCommitHash, b.LastCommit.Hash()) { + return errors.New(gcmn.Fmt("Wrong Block.Header.LastCommitHash. Expected %X, got %X", b.LastCommitHash, b.LastCommit.Hash())) + } + + if b.Header.Height != 1 { + if err := b.LastCommit.ValidateBasic(); err != nil { + return err + } + } + return nil +} + func (b *Block) FillHeader() { if b.LastCommitHash == nil { b.LastCommitHash = b.LastCommit.Hash() @@ -183,6 +189,7 @@ type Header struct { AppHash []byte `json:"app_hash"` // state after txs from the previous block ReceiptsHash []byte `json:"recepits_hash"` // recepits_hash from previous block ProposerAddress []byte `json:"proposer_address"` + Extra []byte `json:"extra"` // doesn't included in header hash } // NOTE: hash is nil if required fields are missing. diff --git a/gemmill/utils/ethtools/common.go b/gemmill/utils/ethtools/common.go deleted file mode 100644 index 350cda7..0000000 --- a/gemmill/utils/ethtools/common.go +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2017 ZhongAn Information Technology Services Co.,Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ethtools - -import ( - "crypto/ecdsa" - "fmt" - "strconv" - "strings" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" -) - -var ethSigner = ethtypes.HomesteadSigner{} - -func ParseData(methodName string, abiDef abi.ABI, params []interface{}) (string, error) { - args, err := ParseArgs(methodName, &abiDef, params) - if err != nil { - return "", err - } - data, err := abiDef.Pack(methodName, args...) - if err != nil { - return "", err - } - - var hexData string - for _, b := range data { - hexDataP := strconv.FormatInt(int64(b), 16) - if len(hexDataP) == 1 { - hexDataP = "0" + hexDataP - } - hexData += hexDataP - } - return hexData, nil -} - -func ParseArgs(methodName string, abiDef *abi.ABI, params []interface{}) ([]interface{}, error) { - var method abi.Method - if methodName == "" { - method = abiDef.Constructor - } else { - var ok bool - method, ok = abiDef.Methods[methodName] - if !ok { - return nil, fmt.Errorf("no such method") - } - } - - if params == nil { - params = []interface{}{} - } - if len(params) != len(method.Inputs) { - return nil, fmt.Errorf("unmatched params") - } - args := []interface{}{} - - for i := range params { - a, err := ParseArg(method.Inputs[i], params[i]) - if err != nil { - fmt.Println(fmt.Sprintf("fail to parse args %v into %s: %v ", params[i], method.Inputs[i].Name, err)) - return nil, err - } - args = append(args, a) - } - return args, nil -} - -func ParseArg(input abi.Argument, value interface{}) (interface{}, error) { - typeName := input.Type.String() - switch { - case strings.Index(typeName, "bool") == 0: - if typeName == "bool" { - return ParseBool(value) - } - return ParseBoolSlice(value, input.Type.SliceSize) - case strings.Index(typeName, "address") == 0: - if typeName == "address" { - return ParseAddress(value) - } - return ParseAddressSlice(value, input.Type.SliceSize) - case strings.Index(typeName, "uint8") == 0: - if typeName == "uint8" { - return ParseUint8(value) - } - return ParseUint8Slice(value, input.Type.SliceSize) - case strings.Index(typeName, "uint16") == 0: - if typeName == "uint16" { - return ParseUint16(value) - } - return ParseUint16Slice(value, input.Type.SliceSize) - case strings.Index(typeName, "uint32") == 0: - if typeName == "uint32" { - return ParseUint32(value) - } - return ParseUint32Slice(value, input.Type.SliceSize) - case strings.Index(typeName, "uint64") == 0: - if typeName == "uint64" { - return ParseUint64(value) - } - return ParseUint64Slice(value, input.Type.SliceSize) - case strings.Index(typeName, "int8") == 0: - if typeName == "int8" { - return ParseInt8(value) - } - return ParseInt8Slice(value, input.Type.SliceSize) - case strings.Index(typeName, "int16") == 0: - if typeName == "int16" { - return ParseInt16(value) - } - return ParseInt16Slice(value, input.Type.SliceSize) - case strings.Index(typeName, "int32") == 0: - if typeName == "int32" { - return ParseInt32(value) - } - return ParseInt32Slice(value, input.Type.SliceSize) - case strings.Index(typeName, "int64") == 0: - if typeName == "int64" { - return ParseInt64(value) - } - return ParseInt64Slice(value, input.Type.SliceSize) - case strings.Index(typeName, "uint256") == 0 || - strings.Index(typeName, "uint128") == 0 || - strings.Index(typeName, "int256") == 0 || - strings.Index(typeName, "int128") == 0: - if typeName == "uint256" || typeName == "uint128" || - typeName == "int256" || typeName == "int128" { - return ParseBigInt(value) - } - return ParseBigIntSlice(value, input.Type.SliceSize) - case strings.Index(typeName, "bytes8") == 0: - if typeName == "bytes8" { - return ParseBytesM(value, 8) - } - return ParseBytesMSlice(value, 8, input.Type.SliceSize) - case strings.Index(typeName, "bytes16") == 0: - if typeName == "bytes16" { - return ParseBytesM(value, 16) - } - return ParseBytesMSlice(value, 16, input.Type.SliceSize) - case strings.Index(typeName, "bytes32") == 0: - if typeName == "bytes32" { - return ParseBytesM(value, 32) - } - return ParseBytesMSlice(value, 32, input.Type.SliceSize) - case strings.Index(typeName, "bytes64") == 0: - if typeName == "bytes64" { - return ParseBytesM(value, 64) - } - return ParseBytesMSlice(value, 64, input.Type.SliceSize) - case strings.Index(typeName, "bytes") == 0: - if typeName == "bytes" { - return ParseBytes(value) - } - case typeName == "string": - return ParseString(value) - } - return nil, fmt.Errorf("type %v of %v is unsupported", typeName, input.Name) -} - -func unpackResult(method string, abiDef abi.ABI, output string) (interface{}, error) { - m, ok := abiDef.Methods[method] - if !ok { - return nil, fmt.Errorf("No such method") - } - if len(m.Outputs) == 1 { - var result interface{} - parsedData := common.ParseData(output) - if err := abiDef.Unpack(&result, method, parsedData); err != nil { - fmt.Println("error:", err) - return nil, err - } - outtype := m.Outputs[0].Type.String() - if strings.Index(outtype, "bytes") == 0 { - return parseByteNSlc(outtype, result), nil - } - return result, nil - } - - d := common.ParseData(output) - var result []interface{} - if err := abiDef.Unpack(&result, method, d); err != nil { - fmt.Println("fail to unpack outpus:", err) - return nil, err - } - - retVal := map[string]interface{}{} - for i, output := range m.Outputs { - if output.Name == "" { - return result, nil - } - outtype := output.Type.String() - if strings.Index(outtype, "bytes") == 0 { - retVal[output.Name] = parseByteNSlc(outtype, result[i]) - } else { - retVal[output.Name] = result[i] - } - } - return retVal, nil -} - -func parseByteNSlc(outtype string, result interface{}) string { - if outtype != "bytes[]" && outtype != "bytes" { - // TODO just 1D-array now - b := result.([][]byte) - idx := 0 - for i := 0; i < len(b); i++ { - for j := 0; j < len(b[i]); j++ { - if b[i][j] == 0 { - idx = j - } else { - break - } - } - b[i] = b[i][idx+1:] - } - return fmt.Sprintf("%s", b) - } else { - b := result.([]byte) - idx := 0 - for i := 0; i < len(b); i++ { - if b[i] == 0 { - idx = i - } else { - break - } - } - b = b[idx+1:] - return fmt.Sprintf("%s", b) - } - -} - -func SignTx(tx *ethtypes.Transaction, privkey *ecdsa.PrivateKey) (*ethtypes.Transaction, error) { - sig, err := crypto.Sign(tx.SigHash(ethSigner).Bytes(), privkey) - if err != nil { - return nil, err - } - sigTx, err := tx.WithSignature(ethSigner, sig) - if err != nil { - return nil, err - } - return sigTx, nil -} - -// TxSignToBytes returns txBytes,txHashBytes,error -func TxSignToBytes(tx *ethtypes.Transaction, privkey *ecdsa.PrivateKey) ([]byte, common.Hash, error) { - signedTx, err := SignTx(tx, privkey) - if err != nil { - return nil, common.Hash{}, err - } - txBytes, err := rlp.EncodeToBytes(signedTx) - txHash := signedTx.Hash() - return txBytes, txHash, err -} diff --git a/gemmill/utils/ethtools/value_parser.go b/gemmill/utils/ethtools/value_parser.go deleted file mode 100644 index 4cc63c8..0000000 --- a/gemmill/utils/ethtools/value_parser.go +++ /dev/null @@ -1,626 +0,0 @@ -// Copyright 2017 ZhongAn Information Technology Services Co.,Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ethtools - -import ( - "bytes" - "encoding/binary" - "encoding/json" - "fmt" - "math/big" - "strconv" - - simplejson "github.com/bitly/go-simplejson" - "github.com/ethereum/go-ethereum/common" - - "errors" - "strings" -) - -func ParseUint8(value interface{}) (uint8, error) { - if value == nil { - return 0, fmt.Errorf("value cannot be nil") - } - v, err := strconv.ParseUint(fmt.Sprintf("%v", value), 10, 8) - if err != nil { - return 0, err - } - return uint8(v), nil -} - -func ParseUint16(value interface{}) (uint16, error) { - if value == nil { - return 0, fmt.Errorf("value cannot be nil") - } - v, err := strconv.ParseUint(fmt.Sprintf("%v", value), 10, 16) - if err != nil { - return 0, err - } - return uint16(v), nil -} - -func ParseUint32(value interface{}) (uint32, error) { - if value == nil { - return 0, fmt.Errorf("value cannot be nil") - } - v, err := strconv.ParseUint(fmt.Sprintf("%v", value), 10, 32) - if err != nil { - return 0, err - } - return uint32(v), nil -} - -func ParseUint64(value interface{}) (uint64, error) { - if value == nil { - return 0, fmt.Errorf("value cannot be nil") - } - v, err := strconv.ParseUint(fmt.Sprintf("%v", value), 10, 64) - if err != nil { - return 0, err - } - return v, nil -} - -func ParseInt8(value interface{}) (int8, error) { - if value == nil { - return 0, fmt.Errorf("value cannot be nil") - } - v, err := strconv.ParseInt(fmt.Sprintf("%v", value), 10, 8) - if err != nil { - return 0, err - } - return int8(v), nil -} - -func ParseInt16(value interface{}) (int16, error) { - if value == nil { - return 0, fmt.Errorf("value cannot be nil") - } - v, err := strconv.ParseInt(fmt.Sprintf("%v", value), 10, 16) - if err != nil { - return 0, err - } - return int16(v), nil -} - -func ParseInt32(value interface{}) (int32, error) { - if value == nil { - return 0, fmt.Errorf("value cannot be nil") - } - v, err := strconv.ParseInt(fmt.Sprintf("%v", value), 10, 32) - if err != nil { - return 0, err - } - return int32(v), nil -} - -func ParseInt64(value interface{}) (int64, error) { - if value == nil { - return 0, fmt.Errorf("value cannot be nil") - } - v, err := strconv.ParseInt(fmt.Sprintf("%v", value), 10, 64) - if err != nil { - return 0, err - } - return v, nil -} - -func ParseBigInt(value interface{}) (*big.Int, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - strVal := fmt.Sprintf("%v", value) - - if strings.Index(strVal, "e") != -1 { - s, err := parseScientificNotation(strVal) - if err != nil { - fmt.Println(fmt.Sprintf("fail to parse scientific notation %s: %v", strVal, err)) - return nil, err - } - bi, ok := new(big.Int).SetString(s, 10) - if !ok { - return nil, fmt.Errorf("Fail to parse %v to big.Int", value) - } - return bi, nil - } - v, ok := new(big.Int).SetString(strVal, 10) - if !ok { - return nil, fmt.Errorf("Fail to convert %v to big.Int", value) - } - return v, nil -} - -func parseScientificNotation(str string) (string, error) { - eIndex := strings.Index(str, "e+") - if eIndex == -1 || eIndex == 0 { - return "", errors.New("invalid scientific notation number") - } - times, err := strconv.ParseInt(str[eIndex+2:], 10, 64) - if err != nil { - return "", err - } - if times == 0 { - return str[:eIndex], nil - } - intTimes := int(times) - pointIndex := strings.Index(str, ".") - if pointIndex+1 == eIndex { - return "", errors.New("invalid scientific notation number") - } - var withoutPoint string - if pointIndex != -1 { - withoutPoint = str[:pointIndex] + str[pointIndex+1:eIndex] - } else { - withoutPoint = str[:eIndex] - } - - l := len(withoutPoint) - if pointIndex == -1 { - for i := 0; i < intTimes; i++ { - withoutPoint += "0" - } - return deletePrefixZero(withoutPoint), nil - } else if intTimes >= l-pointIndex { - for i := 0; i < intTimes-(l-pointIndex); i++ { - withoutPoint += "0" - } - return deletePrefixZero(withoutPoint), nil - } else { - withoutPoint = withoutPoint[:pointIndex+intTimes] + "." + withoutPoint[pointIndex+intTimes:] - return deletePrefixZero(withoutPoint), nil - } -} - -func deletePrefixZero(s string) string { - for strings.Index(s, "0") == 0 { - s = s[1:] - } - return s -} - -func ParseUint8Slice(value interface{}, size int) ([]uint8, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - retVals := []uint8{} - for _, v := range values { - retVal, err := ParseUint8(v) - if err != nil { - return nil, err - } - retVals = append(retVals, retVal) - } - return retVals, nil -} - -func ParseUint16Slice(value interface{}, size int) ([]uint16, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - retVals := []uint16{} - for _, v := range values { - retVal, err := ParseUint16(v) - if err != nil { - return nil, err - } - retVals = append(retVals, retVal) - } - return retVals, nil -} - -func ParseUint32Slice(value interface{}, size int) ([]uint32, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - retVals := []uint32{} - for _, v := range values { - retVal, err := ParseUint32(v) - if err != nil { - return nil, err - } - retVals = append(retVals, retVal) - } - return retVals, nil -} - -func ParseUint64Slice(value interface{}, size int) ([]uint64, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - retVals := []uint64{} - for _, v := range values { - retVal, err := ParseUint64(v) - if err != nil { - return nil, err - } - retVals = append(retVals, retVal) - } - return retVals, nil -} - -func ParseInt8Slice(value interface{}, size int) ([]int8, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - retVals := []int8{} - for _, v := range values { - retVal, err := ParseInt8(v) - if err != nil { - return nil, err - } - retVals = append(retVals, retVal) - } - return retVals, nil -} - -func ParseInt16Slice(value interface{}, size int) ([]int16, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - retVals := []int16{} - for _, v := range values { - retVal, err := ParseInt16(v) - if err != nil { - return nil, err - } - retVals = append(retVals, retVal) - } - return retVals, nil -} - -func ParseInt32Slice(value interface{}, size int) ([]int32, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - retVals := []int32{} - for _, v := range values { - retVal, err := ParseInt32(v) - if err != nil { - return nil, err - } - retVals = append(retVals, retVal) - } - return retVals, nil -} - -func ParseInt64Slice(value interface{}, size int) ([]int64, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - retVals := []int64{} - for _, v := range values { - retVal, err := ParseInt64(v) - if err != nil { - return nil, err - } - retVals = append(retVals, retVal) - } - return retVals, nil -} - -func ParseBigIntSlice(value interface{}, size int) ([]*big.Int, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - biValues := []*big.Int{} - for _, v := range values { - biValue, err := ParseBigInt(v) - if err != nil { - return nil, err - } - biValues = append(biValues, biValue) - } - return biValues, nil -} - -func ParseAddress(value interface{}) (common.Address, error) { - if value == nil { - return common.Address{}, fmt.Errorf("value cannot be nil") - } - addrStr := fmt.Sprintf("%v", value) - if IsAddressType(addrStr) { - return common.HexToAddress(addrStr), nil - } else { - return common.Address{}, fmt.Errorf("connot convert %v to address", value) - } -} - -func ParseAddressSlice(value interface{}, size int) ([]common.Address, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - addrValues := []common.Address{} - for _, v := range values { - addrValue, err := ParseAddress(v) - if err != nil { - return nil, err - } - addrValues = append(addrValues, addrValue) - } - return addrValues, nil -} - -func ParseBytesM(value interface{}, m int) ([]byte, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - b := []byte(fmt.Sprintf("%v", value)) - //if len(b) > 1 && b[0] == '"' && b[len(b)-1] == '"' { - // b = b[1 : len(b)-1] - //} - if len(b) > m { - return nil, fmt.Errorf("%v is out of range: [%d]byte", value, m) - } - return common.RightPadBytes(b, m), nil -} - -func ParseBytes(value interface{}) ([]byte, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - return []byte(fmt.Sprintf("%v", value)), nil -} - -func ParseBytesMSlice(value interface{}, m, size int) ([][]byte, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - retVals := [][]byte{} - for _, v := range values { - retVal, err := ParseBytesM(v, m) - if err != nil { - return nil, err - } - retVals = append(retVals, retVal) - } - return retVals, nil -} - -func ParseBool(value interface{}) (bool, error) { - if value == nil { - return false, fmt.Errorf("value cannot be nil") - } - b, err := strconv.ParseBool(fmt.Sprintf("%v", value)) - if err != nil { - return false, err - } - return b, nil -} - -func ParseBoolSlice(value interface{}, size int) ([]bool, error) { - if value == nil { - return nil, fmt.Errorf("value cannot be nil") - } - values, ok := value.([]interface{}) - if !ok { - return nil, fmt.Errorf("cannot convert %v to []interface{}", value) - } - if size != -1 && size != len(values) { - return nil, fmt.Errorf("size of %v must be %d", value, size) - } - retVals := []bool{} - for _, v := range values { - retVal, err := ParseBool(v) - if err != nil { - return nil, err - } - retVals = append(retVals, retVal) - } - return retVals, nil -} - -func ParseString(value interface{}) (string, error) { - b := []byte(fmt.Sprintf("%v", value)) - // if len(b) > 1 && b[0] == '"' && b[len(b)-1] == '"' { - // b = b[1 : len(b)-1] - // } - return string(b), nil -} - -func IsAddressType(str string) bool { - return strings.HasPrefix(str, "0x") || strings.HasPrefix(str, "0X") -} - -const TRIM_SET = " \n\t" - -func JsonParamsToSlc(jsonParam json.RawMessage) ([]interface{}, error) { - j, err := simplejson.NewJson(jsonParam) - if err != nil { - return nil, err - } - return j.MustArray(), nil -} - -// @Deprecated, has quotation trouble -func JsonParamsToSlcV1(jsonParam json.RawMessage) ([]interface{}, error) { - - //strJson := strings.TrimRight(strings.TrimLeft(string(jsonParam), "["), "]") - strJson := strings.Trim(string(jsonParam), TRIM_SET) - if len(strJson) == 0 || strJson == "[]" || strJson == "null" { - return nil, nil - } - switch strJson[0] { - case '{': - // type object - strJson = strJson[1 : len(strJson)-1] - strSlc := strings.Split(strJson, ":") - params := make([]interface{}, 0, len(strSlc)) - //str := "" - return params, fmt.Errorf("invalid param type") - case '[': - // type set - params, _, err := StringToSlc(strJson) - return params, err - default: - return nil, fmt.Errorf("parse fail") - } -} - -// continue...TODO -func StringToSlc(jsonParam string) ([]interface{}, int, error) { - strJson := strings.Trim(string(jsonParam), TRIM_SET) - if len(strJson) < 2 { - return nil, 0, nil - } - baseidx := strings.Index(jsonParam, strJson) - var count int - if strJson[0] == '[' { - strJson = strJson[1:] - count = 1 - } - params := make([]interface{}, 0) - byteStr := []byte(strJson) - var begin int = -1 - for i := 0; i < len(byteStr); i++ { - switch byteStr[i] { - case '[': - subParams, length, err := StringToSlc(string(strJson[i:])) - if err != nil { - return nil, 0, err - } - params = append(params, interface{}(subParams)) - i += length + 1 - begin = -1 - case ']': - count-- - if count < 0 { - return nil, 0, fmt.Errorf("\"[\" \"]\" mismatch") - } - if p := _checkAndTransferToParam(byteStr, begin, i); p != nil { - params = append(params, p) - } - return params, baseidx + i + 1, nil - - case ',': - if p := _checkAndTransferToParam(byteStr, begin, i); p != nil { - params = append(params, p) - } - begin = -1 - default: - if i == len(byteStr)-1 { - params = append(params, _checkAndTransferToParam(byteStr, begin, i)) - } - if begin < 0 { - begin = i - } - } - - } - return params, len(jsonParam), nil -} - -func _checkAndTransferToParam(bts []byte, begin, end int) interface{} { - if begin < 0 { - return nil - } - // trust params begin & end - ret := strings.Trim(string(bts[begin:end]), TRIM_SET) - if strings.HasPrefix(ret, "\"0x") { - // address drop '"'s - return ret[1 : len(ret)-1] - } - return ret -} - -func IntTo4Bytes(num int) []byte { - var buf bytes.Buffer - BinWrite(&buf, uint32(num)) - return buf.Bytes() -} - -func BinRead(buf *bytes.Buffer, data interface{}) { - binary.Read(buf, binary.LittleEndian, data) -} - -func BinWrite(buf *bytes.Buffer, data interface{}) { - binary.Write(buf, binary.LittleEndian, data) -} diff --git a/go.mod b/go.mod index 1a7b77b..28df55a 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,8 @@ require ( github.com/gorilla/websocket v0.0.0-20170718202341-a69d9f6de432 github.com/hashicorp/golang-lru v0.5.0 github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/raft v1.1.1 + github.com/hashicorp/raft-boltdb v0.0.0-20190605210249-ef2e128ed477 github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmhodges/levigo v1.0.0 github.com/kr/pretty v0.1.0 // indirect @@ -43,7 +45,7 @@ require ( go.uber.org/zap v0.0.0-20170802171341-e68420e36ce8 golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09 // indirect - golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be + golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db // indirect golang.org/x/tools v0.0.0-20190517183331-d88f79806bbd gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 diff --git a/go.sum b/go.sum index 37d4580..a62cbaf 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,22 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/allegro/bigcache v1.2.0 h1:qDaE0QoF29wKBb3+pXFrJFy1ihe5OT9OiXhg1t85SxM= github.com/allegro/bigcache v1.2.0/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/aristanetworks/goarista v0.0.0-20180424004133-70dca2f27708 h1:QHczF0ONAhgjtlNxlRedLZ0Hszmjs6Cmqw/oTJ4+K3s= github.com/aristanetworks/goarista v0.0.0-20180424004133-70dca2f27708/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= github.com/astaxie/beego v0.0.0-20171218111859-f16688817aa4 h1:S+9NpWgwkBlu/nZi3F4bkj5gcf5luYb4h5hc58f+tXo= github.com/astaxie/beego v0.0.0-20171218111859-f16688817aa4/go.mod h1:0R4++1tUqERR0WYFWdfkcrsyoVBCG4DgpDGokT3yb+U= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bitly/go-simplejson v0.0.0-20170206154632-da1a8928f709 h1:/cPOaJttpOzfKrpqdnyl/F7jnjGr3hMszNT+5v0YRWY= github.com/bitly/go-simplejson v0.0.0-20170206154632-da1a8928f709/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/btcsuite/btcd v0.0.0-20190427004231-96897255fd17 h1:m0N5Vg5nP3zEz8TREZpwX3gt4Biw3/8fbIf4A3hO96g= github.com/btcsuite/btcd v0.0.0-20190427004231-96897255fd17/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= @@ -20,6 +26,8 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -37,10 +45,26 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pO github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gorilla/websocket v0.0.0-20170718202341-a69d9f6de432 h1:vtP1PA6IcEyr6fLKRODvfrmEakMZD0o4GTMdb30MoYQ= github.com/gorilla/websocket v0.0.0-20170718202341-a69d9f6de432/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.1 h1:9PZfAcVEvez4yhLH2TBU64/h/z4xlFI80cWXRrxuKuM= +github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/raft v1.1.0/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= +github.com/hashicorp/raft v1.1.1 h1:HJr7UE1x/JrJSc9Oy6aDBHtNHUUBHjcQjTgvUVihoZs= +github.com/hashicorp/raft v1.1.1/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= +github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= +github.com/hashicorp/raft-boltdb v0.0.0-20190605210249-ef2e128ed477 h1:bLsrEmB2NUwkHH18FOJBIa04wOV2RQalJrcafTYu6Lg= +github.com/hashicorp/raft-boltdb v0.0.0-20190605210249-ef2e128ed477/go.mod h1:aUF6HQr8+t3FC/ZHAC+pZreUBhTaxumuu3L+d37uRxk= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -59,6 +83,7 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-sqlite3 v0.0.0-20180218131552-696e2e43cb00 h1:EBBUnaoxrlTrnmwlqJSrPQ2bhkQKPs4vm9nSuaEiqak= github.com/mattn/go-sqlite3 v0.0.0-20180218131552-696e2e43cb00/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747 h1:eQox4Rh4ewJF+mqYPxCkmBAirRnPaHEB26UkNuPyjlk= github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= @@ -70,6 +95,8 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= @@ -78,6 +105,10 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= @@ -96,6 +127,7 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/syndtr/goleveldb v0.0.0-20170725064836-b89cc31ef797 h1:eDCldGfZxcrXnTnynVsYUmABi9pCd/fhhh6SpKAa2dA= github.com/syndtr/goleveldb v0.0.0-20170725064836-b89cc31ef797/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= go.uber.org/atomic v0.0.0-20170719224650-70bd1261d36b h1:8oSyNGzqkEt+JvEEv4c7S8hBRe73J96WmS6hpcdT4mk= go.uber.org/atomic v0.0.0-20170719224650-70bd1261d36b/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= @@ -107,17 +139,20 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09 h1:KaQtG+aDELoNmXYas3TVkGNYRuq8JQ1aa7LJt8EXVyo= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be h1:mI+jhqkn68ybP0ORJqunXn+fq+Eeb4hHKqLQcFICjAc= -golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed h1:uPxWBzB3+mlnjy9W58qY1j/cjyFjutgw/Vhan2zLy/A= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=