diff --git a/cmd/certgen/main.go b/cmd/certgen/main.go deleted file mode 100644 index deb7da83bc1e121c05d7a5de8129863d691299b9..0000000000000000000000000000000000000000 --- a/cmd/certgen/main.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Generate a self-signed X.509 certificate for a TLS server. Outputs to -// 'cert.pem' and 'key.pem' and will overwrite existing files. - -package main - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "flag" - "fmt" - "log" - "math/big" - "net" - "os" - "strings" - "time" -) - -var ( - host = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for") - validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011") - validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for") - isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority") - rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set") - ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521") -) - -func publicKey(priv interface{}) interface{} { - switch k := priv.(type) { - case *rsa.PrivateKey: - return &k.PublicKey - case *ecdsa.PrivateKey: - return &k.PublicKey - default: - return nil - } -} - -func pemBlockForKey(priv interface{}) *pem.Block { - switch k := priv.(type) { - case *rsa.PrivateKey: - return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} - case *ecdsa.PrivateKey: - b, err := x509.MarshalECPrivateKey(k) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err) - os.Exit(2) - } - return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} - default: - return nil - } -} - -func main() { - flag.Parse() - - if len(*host) == 0 { - log.Fatalf("Missing required --host parameter") - } - - var priv interface{} - var err error - switch *ecdsaCurve { - case "": - priv, err = rsa.GenerateKey(rand.Reader, *rsaBits) - case "P224": - priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) - case "P256": - priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - case "P384": - priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - case "P521": - priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - default: - fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve) - os.Exit(1) - } - if err != nil { - log.Fatalf("failed to generate private key: %s", err) - } - - var notBefore time.Time - if len(*validFrom) == 0 { - notBefore = time.Now() - } else { - notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err) - os.Exit(1) - } - } - - notAfter := notBefore.Add(*validFor) - - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - log.Fatalf("failed to generate serial number: %s", err) - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{"Acme Co"}, - }, - NotBefore: notBefore, - NotAfter: notAfter, - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - hosts := strings.Split(*host, ",") - for _, h := range hosts { - if ip := net.ParseIP(h); ip != nil { - template.IPAddresses = append(template.IPAddresses, ip) - } else { - template.DNSNames = append(template.DNSNames, h) - } - } - - if *isCA { - template.IsCA = true - template.KeyUsage |= x509.KeyUsageCertSign - } - - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) - if err != nil { - log.Fatalf("Failed to create certificate: %s", err) - } - - certOut, err := os.Create("cert.pem") - if err != nil { - log.Fatalf("failed to open cert.pem for writing: %s", err) - } - pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - certOut.Close() - log.Print("written cert.pem\n") - - keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - log.Print("failed to open key.pem for writing:", err) - return - } - pem.Encode(keyOut, pemBlockForKey(priv)) - keyOut.Close() - log.Print("written key.pem\n") -} diff --git a/cmd/erisdbss/main.go b/cmd/erisdbss/main.go deleted file mode 100644 index 69e5542e17a0bb17b733b16a336dec9573aa81fa..0000000000000000000000000000000000000000 --- a/cmd/erisdbss/main.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "github.com/gin-gonic/gin" - ess "github.com/eris-ltd/eris-db/erisdb/erisdbss" - "github.com/eris-ltd/eris-db/server" - "os" - "path" -) - -func main() { - gin.SetMode(gin.ReleaseMode) - - baseDir := path.Join(os.TempDir(), "/.edbservers") - ss := ess.NewServerServer(baseDir) - proc := server.NewServeProcess(nil, ss) - err := proc.Start() - if err != nil { - panic(err.Error()) - } - <-proc.StopEventChannel() - os.RemoveAll(baseDir) -} diff --git a/core/core.go b/core/core.go index 8ecde83da7fd8a2989de6906562a1a04356e3965..cb4d29c9dcbb002f52d6901046332d5db1294721 100644 --- a/core/core.go +++ b/core/core.go @@ -94,3 +94,34 @@ func (core *Core) NewGateway(config *server.ServerConfig) (*server.ServeProcess, return proc, nil } + + +// func StartRPC(config cfg.Config, n *node.Node, edbApp *edbapp.ErisDBApp) ([]net.Listener, error) { +// rpccore.SetConfig(config) +// +// rpccore.SetErisDBApp(edbApp) +// rpccore.SetBlockStore(n.BlockStore()) +// rpccore.SetConsensusState(n.ConsensusState()) +// rpccore.SetConsensusReactor(n.ConsensusReactor()) +// rpccore.SetMempoolReactor(n.MempoolReactor()) +// rpccore.SetSwitch(n.Switch()) +// rpccore.SetPrivValidator(n.PrivValidator()) +// rpccore.SetGenDoc(LoadGenDoc(config.GetString("genesis_file"))) +// +// listenAddrs := strings.Split(config.GetString("rpc_laddr"), ",") +// +// // we may expose the rpc over both a unix and tcp socket +// listeners := make([]net.Listener, len(listenAddrs)) +// for i, listenAddr := range listenAddrs { +// mux := http.NewServeMux() +// wm := rpcserver.NewWebsocketManager(rpccore.Routes, n.EventSwitch()) +// mux.HandleFunc("/websocket", wm.WebsocketHandler) +// rpcserver.RegisterRPCFuncs(mux, rpccore.Routes) +// listener, err := rpcserver.StartHTTPServer(listenAddr, mux) +// if err != nil { +// return nil, err +// } +// listeners[i] = listener +// } +// return listeners, nil +// } diff --git a/erisdb/codec.go b/erisdb/codec.go deleted file mode 100644 index 7e6b10cf3e04d1184d5fedf8d42c1d09f474d474..0000000000000000000000000000000000000000 --- a/erisdb/codec.go +++ /dev/null @@ -1,50 +0,0 @@ -package erisdb - -import ( - "io" - "io/ioutil" - - wire "github.com/tendermint/go-wire" - - rpc "github.com/eris-ltd/eris-db/rpc" -) - -// Codec that uses tendermints 'binary' package for JSON. -type TCodec struct { -} - -// Get a new codec. -func NewTCodec() rpc.Codec { - return &TCodec{} -} - -// Encode to an io.Writer. -func (this *TCodec) Encode(v interface{}, w io.Writer) error { - var err error - var n int - wire.WriteJSON(v, w, &n, &err) - return err -} - -// Encode to a byte array. -func (this *TCodec) EncodeBytes(v interface{}) ([]byte, error) { - return wire.JSONBytes(v), nil -} - -// Decode from an io.Reader. -func (this *TCodec) Decode(v interface{}, r io.Reader) error { - bts, errR := ioutil.ReadAll(r) - if errR != nil { - return errR - } - var err error - wire.ReadJSON(v, bts, &err) - return err -} - -// Decode from a byte array. -func (this *TCodec) DecodeBytes(v interface{}, bts []byte) error { - var err error - wire.ReadJSON(v, bts, &err) - return err -} diff --git a/erisdb/erisdbss/http.go b/erisdb/erisdbss/http.go deleted file mode 100644 index f738e9b5fa84bef63ccf200bc42ced4532c55ddb..0000000000000000000000000000000000000000 --- a/erisdb/erisdbss/http.go +++ /dev/null @@ -1,118 +0,0 @@ -package erisdbss - -import ( - "bytes" - "encoding/json" - "net/http" - "os" - - "github.com/eris-ltd/eris-db/config" - stypes "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" - "github.com/gin-gonic/gin" - . "github.com/tendermint/go-common" - "github.com/tendermint/go-wire" - tmtypes "github.com/tendermint/tendermint/types" -) - -const TendermintConfigDefault = `# This is a TOML config file. -# For more information, see https:/github.com/toml-lang/toml - -moniker = "__MONIKER__" -seeds = "" -fast_sync = false -db_backend = "leveldb" -log_level = "debug" -node_laddr = "" -rpc_laddr = "" -` - -// User data accepts a private validator and genesis json object. -// * PrivValidator is the private validator json data. -// * Genesis is the genesis json data. -// * MaxDuration is the maximum duration of the process (in seconds). -// If this is 0, it will be set to REAPER_THRESHOLD -// TODO more stuff, like tendermint and server config files. Will probably -// wait with this until the eris/EPM integration. -type RequestData struct { - PrivValidator *tmtypes.PrivValidator `json:"priv_validator"` - Genesis *stypes.GenesisDoc `json:"genesis"` - MaxDuration uint `json:"max_duration"` -} - -// The response is the port of the newly generated server. The assumption -// here is that the host name or ip is the same, and the default server -// settings apply. -// TODO return some "live" data after starting the node, so that -// the requester can validate that everything is fine. Maybe -// some data directly from the state manager. Genesis hash? -type ResponseData struct { - Port string `json:"port"` -} - -// Serves requests to fire up erisdb executables. POSTing to the server -// endpoint (/server by default) with RequestData in the body will create -// a fresh working directory with files based on that indata, fire up a -// new 'erisdb' executable and point it to that dir. The purpose is mostly -// to make testing easier, since setting up a node is as easy as making a -// http request. -// TODO link up with eris/EPM instead, to spawn new nodes in containers. -type ServerServer struct { - running bool - serverManager *ServerManager -} - -// Create a new ServerServer with the given base directory. -func NewServerServer(baseDir string) *ServerServer { - os.RemoveAll(baseDir) - EnsureDir(baseDir, 0777) - return &ServerServer{serverManager: NewServerManager(100, baseDir)} -} - -// Start the server. -func (this *ServerServer) Start(config *config.ServerConfig, router *gin.Engine) { - router.POST("/server", this.handleFunc) - this.running = true -} - -// Is the server currently running. -func (this *ServerServer) Running() bool { - return this.running -} - -// Shut the server down. Will close all websocket sessions. -func (this *ServerServer) ShutDown() { - this.running = false - this.serverManager.killAll() -} - -// Handle incoming requests. -func (this *ServerServer) handleFunc(c *gin.Context) { - log.Debug("Incoming message") - r := c.Request - var buf bytes.Buffer - n, errR := buf.ReadFrom(r.Body) - if errR != nil || n == 0 { - http.Error(c.Writer, "Bad request.", 400) - return - } - bts := buf.Bytes() - var errDC error - reqData := &RequestData{} - wire.ReadJSON(reqData, bts, &errDC) - if errDC != nil { - http.Error(c.Writer, "Failed to decode json.", 400) - return - } - log.Debug("Starting to add.") - resp, errA := this.serverManager.add(reqData) - if errA != nil { - http.Error(c.Writer, "Internal error: "+errA.Error(), 500) - return - } - log.Debug("Work done.", "PORT", resp.Port) - w := c.Writer - enc := json.NewEncoder(w) - enc.Encode(resp) - w.WriteHeader(200) - -} diff --git a/erisdb/erisdbss/log.go b/erisdb/erisdbss/log.go deleted file mode 100644 index 719216427a0cbd239e0ae187dcfac8bf14604a72..0000000000000000000000000000000000000000 --- a/erisdb/erisdbss/log.go +++ /dev/null @@ -1,7 +0,0 @@ -package erisdbss - -import ( - "github.com/tendermint/log15" -) - -var log = log15.New("module", "eris/serverserver") diff --git a/erisdb/erisdbss/server_manager.go b/erisdb/erisdbss/server_manager.go deleted file mode 100644 index 34faeb71905b7ab8cc734803e5035e1e0d0153d5..0000000000000000000000000000000000000000 --- a/erisdb/erisdbss/server_manager.go +++ /dev/null @@ -1,311 +0,0 @@ -package erisdbss - -import ( - "bufio" - "fmt" - "os" - "os/exec" - "path" - "strings" - "sync" - "time" - - "github.com/eris-ltd/eris-db/config" - "github.com/eris-ltd/eris-db/files" - "github.com/eris-ltd/eris-db/server" - . "github.com/tendermint/go-common" - "github.com/tendermint/go-wire" -) - -const ( - REAPER_TIMEOUT = 5 * time.Second - REAPER_THRESHOLD = 10 * time.Second - // Ports to new server processes are PORT_BASE + i, where i is an integer from the pool. - PORT_BASE = 29000 - // How long are we willing to wait for a process to become ready. - PROC_START_TIMEOUT = 3 * time.Second - // Name of the process executable. - EXECUTABLE_NAME = "erisdb" -) - -// Executable processes. These are used to wrap processes that are . -type ExecProcess interface { - Start(chan<- error) - Kill() error -} - -// Wrapper for exec.Cmd. Will wait for a token from stdout using line scanning. -type CmdProcess struct { - cmd *exec.Cmd - token string -} - -func newCmdProcess(cmd *exec.Cmd, token string) *CmdProcess { - return &CmdProcess{cmd, token} -} - -func (this *CmdProcess) Start(doneChan chan<- error) { - log.Debug("Starting erisdb process") - reader, errSP := this.cmd.StdoutPipe() - - if errSP != nil { - doneChan <- errSP - } - - scanner := bufio.NewScanner(reader) - scanner.Split(bufio.ScanLines) - - if errStart := this.cmd.Start(); errStart != nil { - doneChan <- errStart - return - } - fmt.Println("process started, waiting for token") - for scanner.Scan() { - text := scanner.Text() - log.Debug(text) - if strings.Index(text, this.token) != -1 { - log.Debug("Token found", "token", this.token) - go func() { - for scanner.Scan() { - text := scanner.Text() - log.Debug(text) - } - }() - break - } - } - - if err := scanner.Err(); err != nil { - doneChan <- fmt.Errorf("Error reading from process stdout:", err) - return - } - log.Debug("ErisDB server ready.") - doneChan <- nil -} - -func (this *CmdProcess) Kill() error { - err := this.cmd.Process.Kill() - if err != nil { - return err - } - _, err2 := this.cmd.Process.Wait() - return err2 -} - -// A serve task. This wraps a running process. It was designed to run 'erisdb' processes. -type ServeTask struct { - sp ExecProcess - workDir string - start time.Time - maxDuration time.Duration - port uint16 -} - -// Create a new serve task. -func newServeTask(port uint16, workDir string, maxDuration time.Duration, process ExecProcess) *ServeTask { - return &ServeTask{ - process, - workDir, - time.Now(), - maxDuration, - port, - } -} - -// Catches events that callers subscribe to and adds them to an array ready to be polled. -type ServerManager struct { - mtx *sync.Mutex - idPool *server.IdPool - maxProcs uint - running []*ServeTask - reap bool - baseDir string -} - -// -func NewServerManager(maxProcs uint, baseDir string) *ServerManager { - sm := &ServerManager{ - mtx: &sync.Mutex{}, - idPool: server.NewIdPool(maxProcs), - maxProcs: maxProcs, - running: make([]*ServeTask, 0), - reap: true, - baseDir: baseDir, - } - go reap(sm) - return sm -} - -func reap(sm *ServerManager) { - if !sm.reap { - return - } - time.Sleep(REAPER_TIMEOUT) - sm.mtx.Lock() - defer sm.mtx.Unlock() - // The processes are added in order so just read from bottom of array until - // a time is below reaper threshold, then break. - for len(sm.running) > 0 { - task := sm.running[0] - if task.maxDuration != 0 && time.Since(task.start) > task.maxDuration { - log.Debug("[SERVER REAPER] Closing down server.", "port", task.port) - task.sp.Kill() - sm.running = sm.running[1:] - sm.idPool.ReleaseId(uint(task.port - PORT_BASE)) - } else { - break - } - } - go reap(sm) -} - -// Add a new erisdb process to the list. -func (this *ServerManager) add(data *RequestData) (*ResponseData, error) { - this.mtx.Lock() - defer this.mtx.Unlock() - cfg := config.DefaultServerConfig() - // Port is PORT_BASE + a value between 1 and the max number of servers. - id, errId := this.idPool.GetId() - if errId != nil { - return nil, errId - } - port := uint16(PORT_BASE + id) - cfg.Bind.Port = port - - folderName := fmt.Sprintf("testnode%d", port) - workDir, errCWD := this.createWorkDir(data, &cfg, folderName) - if errCWD != nil { - return nil, errCWD - } - - // TODO ... - - // Create a new erisdb process. - cmd := exec.Command(EXECUTABLE_NAME, workDir) - proc := &CmdProcess{cmd, "DONTMINDME55891"} - - errSt := waitForProcStarted(proc) - - if errSt != nil { - return nil, errSt - } - - maxDur := time.Duration(data.MaxDuration) * time.Second - if maxDur == 0 { - maxDur = REAPER_THRESHOLD - } - - st := newServeTask(port, workDir, maxDur, proc) - this.running = append(this.running, st) - - // TODO add validation data. The node should ideally return some post-deploy state data - // and send it back with the server URL, so that the validity of the chain can be - // established client-side before starting the tests. - return &ResponseData{fmt.Sprintf("%d", port)}, nil -} - -// Add a new erisdb process to the list. -func (this *ServerManager) killAll() { - this.mtx.Lock() - defer this.mtx.Unlock() - for len(this.running) > 0 { - task := this.running[0] - log.Debug("Closing down server.", "port", task.port) - task.sp.Kill() - this.running = this.running[1:] - this.idPool.ReleaseId(uint(task.port - PORT_BASE)) - } -} - -// Creates a temp folder for the tendermint/erisdb node to run in. -// Folder name is port based, so port=1337 meens folder="testnode1337" -// Old folders are cleared out. before creating them, and the server will -// clean out all of this servers workdir (defaults to ~/.edbservers) when -// starting and when stopping. -func (this *ServerManager) createWorkDir(data *RequestData, cfg *config.ServerConfig, folderName string) (string, error) { - - workDir := path.Join(this.baseDir, folderName) - os.RemoveAll(workDir) - errED := EnsureDir(workDir, 0777) - if errED != nil { - return "", errED - } - - cfgName := path.Join(workDir, "config.toml") - scName := path.Join(workDir, "server_conf.toml") - pvName := path.Join(workDir, "priv_validator.json") - genesisName := path.Join(workDir, "genesis.json") - - // Write config. - errCFG := files.WriteFileRW(cfgName, []byte(TendermintConfigDefault)) - if errCFG != nil { - return "", errCFG - } - log.Info("File written.", "name", cfgName) - - // Write validator. - errPV := writeJSON(pvName, data.PrivValidator) - if errPV != nil { - return "", errPV - } - log.Info("File written.", "name", pvName) - - // Write genesis - errG := writeJSON(genesisName, data.Genesis) - if errG != nil { - return "", errG - } - log.Info("File written.", "name", genesisName) - - // Write server config. - errWC := config.WriteServerConfig(scName, cfg) - if errWC != nil { - return "", errWC - } - log.Info("File written.", "name", scName) - return workDir, nil -} - -// Used to write json files using tendermints binary package. -func writeJSON(file string, v interface{}) error { - var n int - var errW error - fo, errC := os.Create(file) - if errC != nil { - return errC - } - wire.WriteJSON(v, fo, &n, &errW) - if errW != nil { - return errW - } - errL := fo.Close() - if errL != nil { - return errL - } - return nil -} - -func waitForProcStarted(proc ExecProcess) error { - timeoutChan := make(chan struct{}) - doneChan := make(chan error) - done := new(bool) - go func(b *bool) { - time.Sleep(PROC_START_TIMEOUT) - if !*b { - timeoutChan <- struct{}{} - } - }(done) - go proc.Start(doneChan) - var errSt error - select { - case errD := <-doneChan: - errSt = errD - *done = true - break - case <-timeoutChan: - _ = proc.Kill() - errSt = fmt.Errorf("Process start timed out") - break - } - return errSt -} diff --git a/erisdb/event_cache.go b/erisdb/event_cache.go deleted file mode 100644 index a28752d0546fb42f0f79271f2fa43302d8e7f652..0000000000000000000000000000000000000000 --- a/erisdb/event_cache.go +++ /dev/null @@ -1,126 +0,0 @@ -package erisdb - -import ( - "fmt" - ep "github.com/eris-ltd/eris-db/erisdb/pipe" - "sync" - "time" - - "github.com/tendermint/go-events" -) - -var ( - reaperTimeout = 5 * time.Second - reaperThreshold = 10 * time.Second -) - -type EventCache struct { - mtx *sync.Mutex - events []interface{} - ts time.Time - subId string -} - -func newEventCache() *EventCache { - return &EventCache{ - &sync.Mutex{}, - make([]interface{}, 0), - time.Now(), - "", - } -} - -func (this *EventCache) poll() []interface{} { - this.mtx.Lock() - defer this.mtx.Unlock() - var evts []interface{} - if len(this.events) > 0 { - evts = this.events - this.events = []interface{}{} - } else { - evts = []interface{}{} - } - this.ts = time.Now() - return evts -} - -// Catches events that callers subscribe to and adds them to an array ready to be polled. -type EventSubscriptions struct { - mtx *sync.Mutex - eventEmitter ep.EventEmitter - subs map[string]*EventCache - reap bool -} - -func NewEventSubscriptions(eventEmitter ep.EventEmitter) *EventSubscriptions { - es := &EventSubscriptions{ - mtx: &sync.Mutex{}, - eventEmitter: eventEmitter, - subs: make(map[string]*EventCache), - reap: true, - } - go reap(es) - return es -} - -func reap(es *EventSubscriptions) { - if !es.reap { - return - } - time.Sleep(reaperTimeout) - es.mtx.Lock() - defer es.mtx.Unlock() - for id, sub := range es.subs { - if time.Since(sub.ts) > reaperThreshold { - // Seems like Go is ok with this.. - delete(es.subs, id) - es.eventEmitter.Unsubscribe(id) - } - } - go reap(es) -} - -// Add a subscription and return the generated id. Note event dispatcher -// has to call func which involves aquiring a mutex lock, so might be -// a delay - though a conflict is practically impossible, and if it does -// happen it's for an insignificant amount of time (the time it takes to -// carry out EventCache.poll() ). -func (this *EventSubscriptions) add(eventId string) (string, error) { - subId, errSID := generateSubId() - if errSID != nil { - return "", errSID - } - cache := newEventCache() - _, errC := this.eventEmitter.Subscribe(subId, eventId, - func(evt events.EventData) { - cache.mtx.Lock() - defer cache.mtx.Unlock() - cache.events = append(cache.events, evt) - }) - cache.subId = subId - this.subs[subId] = cache - if errC != nil { - return "", errC - } - return subId, nil -} - -func (this *EventSubscriptions) poll(subId string) ([]interface{}, error) { - sub, ok := this.subs[subId] - if !ok { - return nil, fmt.Errorf("Subscription not active. ID: " + subId) - } - return sub.poll(), nil -} - -func (this *EventSubscriptions) remove(subId string) error { - this.mtx.Lock() - defer this.mtx.Unlock() - // TODO Check this. - _, ok := this.subs[subId] - if !ok { - return fmt.Errorf("Subscription not active. ID: " + subId) - } - delete(this.subs, subId) - return nil -} diff --git a/erisdb/event_cache_test.go b/erisdb/event_cache_test.go deleted file mode 100644 index d0f0ac253b44d7ee150818eb200ff5ffa2ce0ecc..0000000000000000000000000000000000000000 --- a/erisdb/event_cache_test.go +++ /dev/null @@ -1,211 +0,0 @@ -package erisdb - -import ( - "encoding/hex" - "fmt" - "runtime" - "testing" - "time" - - "github.com/eris-ltd/eris-db/txs" - "github.com/stretchr/testify/assert" - "github.com/tendermint/go-events" -) - -var mockInterval = 10 * time.Millisecond - -type mockSub struct { - subId string - eventId string - f func(events.EventData) - shutdown bool - sdChan chan struct{} -} - -// A mock event -func newMockSub(subId, eventId string, f func(events.EventData)) mockSub { - return mockSub{subId, eventId, f, false, make(chan struct{})} -} - -type mockEventEmitter struct { - subs map[string]mockSub -} - -func newMockEventEmitter() *mockEventEmitter { - return &mockEventEmitter{make(map[string]mockSub)} -} - -func (this *mockEventEmitter) Subscribe(subId, eventId string, callback func(events.EventData)) (bool, error) { - if _, ok := this.subs[subId]; ok { - return false, nil - } - me := newMockSub(subId, eventId, callback) - - go func() { - <-me.sdChan - me.shutdown = true - }() - go func() { - for { - if !me.shutdown { - me.f(txs.EventDataNewBlock{}) - } else { - return - } - time.Sleep(mockInterval) - } - }() - return true, nil -} - -func (this *mockEventEmitter) Unsubscribe(subId string) (bool, error) { - sub, ok := this.subs[subId] - if !ok { - return false, nil - } - sub.shutdown = true - delete(this.subs, subId) - return true, nil -} - -// Test that event subscriptions can be added manually and then automatically reaped. -func TestSubReaping(t *testing.T) { - runtime.GOMAXPROCS(runtime.NumCPU()) - NUM_SUBS := 100 - reaperThreshold = 200 * time.Millisecond - reaperTimeout = 100 * time.Millisecond - - mee := newMockEventEmitter() - eSubs := NewEventSubscriptions(mee) - doneChan := make(chan error) - go func() { - for i := 0; i < NUM_SUBS; i++ { - time.Sleep(2 * time.Millisecond) - go func() { - id, err := eSubs.add("WeirdEvent") - if err != nil { - doneChan <- err - return - } - if len(id) != 64 { - doneChan <- fmt.Errorf("Id not of length 64") - return - } - _, err2 := hex.DecodeString(id) - if err2 != nil { - doneChan <- err2 - } - - doneChan <- nil - }() - } - }() - k := 0 - for k < NUM_SUBS { - err := <-doneChan - assert.NoError(t, err) - k++ - } - time.Sleep(1100 * time.Millisecond) - - assert.Len(t, mee.subs, 0) - assert.Len(t, eSubs.subs, 0) - t.Logf("Added %d subs that were all automatically reaped.", NUM_SUBS) -} - -// Test that event subscriptions can be added and removed manually. -func TestSubManualClose(t *testing.T) { - NUM_SUBS := 100 - // Keep the reaper out of this. - reaperThreshold = 10000 * time.Millisecond - reaperTimeout = 10000 * time.Millisecond - - mee := newMockEventEmitter() - eSubs := NewEventSubscriptions(mee) - doneChan := make(chan error) - go func() { - for i := 0; i < NUM_SUBS; i++ { - time.Sleep(2 * time.Millisecond) - go func() { - id, err := eSubs.add("WeirdEvent") - if err != nil { - doneChan <- err - return - } - if len(id) != 64 { - doneChan <- fmt.Errorf("Id not of length 64") - return - } - _, err2 := hex.DecodeString(id) - if err2 != nil { - doneChan <- err2 - } - time.Sleep(100 * time.Millisecond) - err3 := eSubs.remove(id) - if err3 != nil { - doneChan <- err3 - } - doneChan <- nil - }() - } - }() - k := 0 - for k < NUM_SUBS { - err := <-doneChan - assert.NoError(t, err) - k++ - } - - assert.Len(t, mee.subs, 0) - assert.Len(t, eSubs.subs, 0) - t.Logf("Added %d subs that were all closed down by unsubscribing.", NUM_SUBS) -} - -// Test that the system doesn't fail under high pressure. -func TestSubFlooding(t *testing.T) { - NUM_SUBS := 100 - // Keep the reaper out of this. - reaperThreshold = 10000 * time.Millisecond - reaperTimeout = 10000 * time.Millisecond - // Crank it up. Now pressure is 10 times higher on each sub. - mockInterval = 1 * time.Millisecond - mee := newMockEventEmitter() - eSubs := NewEventSubscriptions(mee) - doneChan := make(chan error) - go func() { - for i := 0; i < NUM_SUBS; i++ { - time.Sleep(1 * time.Millisecond) - go func() { - id, err := eSubs.add("WeirdEvent") - if err != nil { - doneChan <- err - return - } - if len(id) != 64 { - doneChan <- fmt.Errorf("Id not of length 64") - return - } - _, err2 := hex.DecodeString(id) - if err2 != nil { - doneChan <- err2 - } - time.Sleep(1000 * time.Millisecond) - err3 := eSubs.remove(id) - if err3 != nil { - doneChan <- err3 - } - doneChan <- nil - }() - } - }() - k := 0 - for k < NUM_SUBS { - err := <-doneChan - assert.NoError(t, err) - k++ - } - - assert.Len(t, mee.subs, 0) - assert.Len(t, eSubs.subs, 0) - t.Logf("Added %d subs that all received 1000 events each. They were all closed down by unsubscribing.", NUM_SUBS) -} diff --git a/erisdb/event_filters.go b/erisdb/event_filters.go deleted file mode 100644 index 498fab486c75cf542fe7d8ecd4c9780d20984599..0000000000000000000000000000000000000000 --- a/erisdb/event_filters.go +++ /dev/null @@ -1,50 +0,0 @@ -package erisdb - -import ( - "bytes" - "encoding/hex" - "fmt" - - ep "github.com/eris-ltd/eris-db/erisdb/pipe" - "github.com/eris-ltd/eris-db/txs" -) - -// Filter for account code. -// Ops: == or != -// Could be used to match against nil, to see if an account is a contract account. -type AccountCallTxHashFilter struct { - op string - value []byte - match func([]byte, []byte) bool -} - -func (this *AccountCallTxHashFilter) Configure(fd *ep.FilterData) error { - op := fd.Op - val, err := hex.DecodeString(fd.Value) - - if err != nil { - return fmt.Errorf("Wrong value type.") - } - if op == "==" { - this.match = func(a, b []byte) bool { - return bytes.Equal(a, b) - } - } else if op == "!=" { - this.match = func(a, b []byte) bool { - return !bytes.Equal(a, b) - } - } else { - return fmt.Errorf("Op: " + this.op + " is not supported for 'code' filtering") - } - this.op = op - this.value = val - return nil -} - -func (this *AccountCallTxHashFilter) Match(v interface{}) bool { - emct, ok := v.(*txs.EventDataCall) - if !ok { - return false - } - return this.match(emct.TxID, this.value) -} diff --git a/erisdb/json_service.go b/erisdb/json_service.go deleted file mode 100644 index fec59e473a18060132d05b87259837d1593066d5..0000000000000000000000000000000000000000 --- a/erisdb/json_service.go +++ /dev/null @@ -1,180 +0,0 @@ -package erisdb - -import ( - "encoding/json" - "net/http" - - cfg "github.com/eris-ltd/eris-db/config" - ep "github.com/eris-ltd/eris-db/erisdb/pipe" - rpc "github.com/eris-ltd/eris-db/rpc" - "github.com/eris-ltd/eris-db/server" - - "github.com/gin-gonic/gin" -) - -// Server used to handle JSON-RPC 2.0 requests. Implements server.Server -type JsonRpcServer struct { - service server.HttpService - running bool -} - -// Create a new JsonRpcServer -func NewJsonRpcServer(service server.HttpService) *JsonRpcServer { - return &JsonRpcServer{service: service} -} - -// Start adds the rpc path to the router. -func (this *JsonRpcServer) Start(config *cfg.ServerConfig, router *gin.Engine) { - router.POST(config.HTTP.JsonRpcEndpoint, this.handleFunc) - this.running = true -} - -// Is the server currently running? -func (this *JsonRpcServer) Running() bool { - return this.running -} - -// Shut the server down. Does nothing. -func (this *JsonRpcServer) ShutDown() { - this.running = false -} - -// Handler passes message on directly to the service which expects -// a normal http request and response writer. -func (this *JsonRpcServer) handleFunc(c *gin.Context) { - r := c.Request - w := c.Writer - - this.service.Process(r, w) -} - -// Used for ErisDb. Implements server.HttpService -type ErisDbJsonService struct { - codec rpc.Codec - pipe ep.Pipe - eventSubs *EventSubscriptions - defaultHandlers map[string]RequestHandlerFunc -} - -// Create a new JSON-RPC 2.0 service for erisdb (tendermint). -func NewErisDbJsonService(codec rpc.Codec, pipe ep.Pipe, eventSubs *EventSubscriptions) server.HttpService { - - tmhttps := &ErisDbJsonService{codec: codec, pipe: pipe, eventSubs: eventSubs} - mtds := &ErisDbMethods{codec, pipe} - - dhMap := mtds.getMethods() - // Events - dhMap[EVENT_SUBSCRIBE] = tmhttps.EventSubscribe - dhMap[EVENT_UNSUBSCRIBE] = tmhttps.EventUnsubscribe - dhMap[EVENT_POLL] = tmhttps.EventPoll - tmhttps.defaultHandlers = dhMap - return tmhttps -} - -// Process a request. -func (this *ErisDbJsonService) Process(r *http.Request, w http.ResponseWriter) { - - // Create new request object and unmarshal. - req := &rpc.RPCRequest{} - decoder := json.NewDecoder(r.Body) - errU := decoder.Decode(req) - - // Error when decoding. - if errU != nil { - this.writeError("Failed to parse request: "+errU.Error(), "", rpc.PARSE_ERROR, w) - return - } - - // Wrong protocol version. - if req.JSONRPC != "2.0" { - this.writeError("Wrong protocol version: "+req.JSONRPC, req.Id, rpc.INVALID_REQUEST, w) - return - } - - mName := req.Method - - if handler, ok := this.defaultHandlers[mName]; ok { - resp, errCode, err := handler(req, w) - if err != nil { - this.writeError(err.Error(), req.Id, errCode, w) - } else { - this.writeResponse(req.Id, resp, w) - } - } else { - this.writeError("Method not found: "+mName, req.Id, rpc.METHOD_NOT_FOUND, w) - } -} - -// Helper for writing error responses. -func (this *ErisDbJsonService) writeError(msg, id string, code int, w http.ResponseWriter) { - response := rpc.NewRPCErrorResponse(id, code, msg) - err := this.codec.Encode(response, w) - // If there's an error here all bets are off. - if err != nil { - http.Error(w, "Failed to marshal standard error response: "+err.Error(), 500) - return - } - w.WriteHeader(200) -} - -// Helper for writing responses. -func (this *ErisDbJsonService) writeResponse(id string, result interface{}, w http.ResponseWriter) { - log.Debug("Result: %v\n", result) - response := rpc.NewRPCResponse(id, result) - err := this.codec.Encode(response, w) - log.Debug("Response: %v\n", response) - if err != nil { - this.writeError("Internal error: "+err.Error(), id, rpc.INTERNAL_ERROR, w) - return - } - w.WriteHeader(200) -} - -// *************************************** Events ************************************ - -// Subscribe to an event. -func (this *ErisDbJsonService) EventSubscribe(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &EventIdParam{} - err := json.Unmarshal(request.Params, param) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - eventId := param.EventId - subId, errC := this.eventSubs.add(eventId) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.EventSub{subId}, 0, nil -} - -// Un-subscribe from an event. -func (this *ErisDbJsonService) EventUnsubscribe(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &SubIdParam{} - err := json.Unmarshal(request.Params, param) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - subId := param.SubId - - result, errC := this.pipe.Events().Unsubscribe(subId) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.EventUnsub{result}, 0, nil -} - -// Check subscription event cache for new data. -func (this *ErisDbJsonService) EventPoll(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &SubIdParam{} - err := json.Unmarshal(request.Params, param) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - subId := param.SubId - - result, errC := this.eventSubs.poll(subId) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.PollResponse{result}, 0, nil -} diff --git a/erisdb/methods.go b/erisdb/methods.go deleted file mode 100644 index df26e002bf1b22fca0d4d04b8b0b2687eaec4898..0000000000000000000000000000000000000000 --- a/erisdb/methods.go +++ /dev/null @@ -1,497 +0,0 @@ -package erisdb - -import ( - "crypto/rand" - "encoding/hex" - "strings" - - ep "github.com/eris-ltd/eris-db/erisdb/pipe" - rpc "github.com/eris-ltd/eris-db/rpc" - - "github.com/eris-ltd/eris-db/txs" -) - -// TODO use the method name definition file. -const ( - SERVICE_NAME = "erisdb" - - GET_ACCOUNTS = SERVICE_NAME + ".getAccounts" // Accounts - GET_ACCOUNT = SERVICE_NAME + ".getAccount" - GET_STORAGE = SERVICE_NAME + ".getStorage" - GET_STORAGE_AT = SERVICE_NAME + ".getStorageAt" - GEN_PRIV_ACCOUNT = SERVICE_NAME + ".genPrivAccount" - GEN_PRIV_ACCOUNT_FROM_KEY = SERVICE_NAME + ".genPrivAccountFromKey" - GET_BLOCKCHAIN_INFO = SERVICE_NAME + ".getBlockchainInfo" // Blockchain - GET_GENESIS_HASH = SERVICE_NAME + ".getGenesisHash" - GET_LATEST_BLOCK_HEIGHT = SERVICE_NAME + ".getLatestBlockHeight" - GET_LATEST_BLOCK = SERVICE_NAME + ".getLatestBlock" - GET_BLOCKS = SERVICE_NAME + ".getBlocks" - GET_BLOCK = SERVICE_NAME + ".getBlock" - GET_CONSENSUS_STATE = SERVICE_NAME + ".getConsensusState" // Consensus - GET_VALIDATORS = SERVICE_NAME + ".getValidators" - GET_NETWORK_INFO = SERVICE_NAME + ".getNetworkInfo" // Net - GET_CLIENT_VERSION = SERVICE_NAME + ".getClientVersion" - GET_MONIKER = SERVICE_NAME + ".getMoniker" - GET_CHAIN_ID = SERVICE_NAME + ".getChainId" - IS_LISTENING = SERVICE_NAME + ".isListening" - GET_LISTENERS = SERVICE_NAME + ".getListeners" - GET_PEERS = SERVICE_NAME + ".getPeers" - GET_PEER = SERVICE_NAME + ".getPeer" - CALL = SERVICE_NAME + ".call" // Tx - CALL_CODE = SERVICE_NAME + ".callCode" - BROADCAST_TX = SERVICE_NAME + ".broadcastTx" - GET_UNCONFIRMED_TXS = SERVICE_NAME + ".getUnconfirmedTxs" - SIGN_TX = SERVICE_NAME + ".signTx" - TRANSACT = SERVICE_NAME + ".transact" - TRANSACT_AND_HOLD = SERVICE_NAME + ".transactAndHold" - TRANSACT_NAMEREG = SERVICE_NAME + ".transactNameReg" - EVENT_SUBSCRIBE = SERVICE_NAME + ".eventSubscribe" // Events - EVENT_UNSUBSCRIBE = SERVICE_NAME + ".eventUnsubscribe" - EVENT_POLL = SERVICE_NAME + ".eventPoll" - GET_NAMEREG_ENTRY = SERVICE_NAME + ".getNameRegEntry" // Namereg - GET_NAMEREG_ENTRIES = SERVICE_NAME + ".getNameRegEntries" -) - -// The rpc method handlers. -type ErisDbMethods struct { - codec rpc.Codec - pipe ep.Pipe -} - -// Used to handle requests. interface{} param is a wildcard used for example with socket events. -type RequestHandlerFunc func(*rpc.RPCRequest, interface{}) (interface{}, int, error) - -// Private. Create a method name -> method handler map. -func (this *ErisDbMethods) getMethods() map[string]RequestHandlerFunc { - dhMap := make(map[string]RequestHandlerFunc) - // Accounts - dhMap[GET_ACCOUNTS] = this.Accounts - dhMap[GET_ACCOUNT] = this.Account - dhMap[GET_STORAGE] = this.AccountStorage - dhMap[GET_STORAGE_AT] = this.AccountStorageAt - dhMap[GEN_PRIV_ACCOUNT] = this.GenPrivAccount - dhMap[GEN_PRIV_ACCOUNT_FROM_KEY] = this.GenPrivAccountFromKey - // Blockchain - dhMap[GET_BLOCKCHAIN_INFO] = this.BlockchainInfo - dhMap[GET_GENESIS_HASH] = this.GenesisHash - dhMap[GET_LATEST_BLOCK_HEIGHT] = this.LatestBlockHeight - dhMap[GET_LATEST_BLOCK] = this.LatestBlock - dhMap[GET_BLOCKS] = this.Blocks - dhMap[GET_BLOCK] = this.Block - // Consensus - dhMap[GET_CONSENSUS_STATE] = this.ConsensusState - dhMap[GET_VALIDATORS] = this.Validators - // Network - dhMap[GET_NETWORK_INFO] = this.NetworkInfo - dhMap[GET_CLIENT_VERSION] = this.ClientVersion - dhMap[GET_MONIKER] = this.Moniker - dhMap[GET_CHAIN_ID] = this.ChainId - dhMap[IS_LISTENING] = this.Listening - dhMap[GET_LISTENERS] = this.Listeners - dhMap[GET_PEERS] = this.Peers - dhMap[GET_PEER] = this.Peer - // Txs - dhMap[CALL] = this.Call - dhMap[CALL_CODE] = this.CallCode - dhMap[BROADCAST_TX] = this.BroadcastTx - dhMap[GET_UNCONFIRMED_TXS] = this.UnconfirmedTxs - dhMap[SIGN_TX] = this.SignTx - dhMap[TRANSACT] = this.Transact - dhMap[TRANSACT_AND_HOLD] = this.TransactAndHold - dhMap[TRANSACT_NAMEREG] = this.TransactNameReg - // Namereg - dhMap[GET_NAMEREG_ENTRY] = this.NameRegEntry - dhMap[GET_NAMEREG_ENTRIES] = this.NameRegEntries - - return dhMap -} - -// TODO add some sanity checks on address params and such. -// Look into the reflection code in core, see what can be used. - -// *************************************** Accounts *************************************** - -func (this *ErisDbMethods) GenPrivAccount(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - pac, errC := this.pipe.Accounts().GenPrivAccount() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return pac, 0, nil -} - -func (this *ErisDbMethods) GenPrivAccountFromKey(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - - param := &PrivKeyParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - - privKey := param.PrivKey - pac, errC := this.pipe.Accounts().GenPrivAccountFromKey(privKey) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return pac, 0, nil -} - -func (this *ErisDbMethods) Account(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &AddressParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - address := param.Address - // TODO is address check? - account, errC := this.pipe.Accounts().Account(address) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return account, 0, nil -} - -func (this *ErisDbMethods) Accounts(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &AccountsParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - list, errC := this.pipe.Accounts().Accounts(param.Filters) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return list, 0, nil -} - -func (this *ErisDbMethods) AccountStorage(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &AddressParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - address := param.Address - storage, errC := this.pipe.Accounts().Storage(address) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return storage, 0, nil -} - -func (this *ErisDbMethods) AccountStorageAt(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &StorageAtParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - address := param.Address - key := param.Key - storageItem, errC := this.pipe.Accounts().StorageAt(address, key) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return storageItem, 0, nil -} - -// *************************************** Blockchain ************************************ - -func (this *ErisDbMethods) BlockchainInfo(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - status, errC := this.pipe.Blockchain().Info() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return status, 0, nil -} - -func (this *ErisDbMethods) ChainId(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - chainId, errC := this.pipe.Blockchain().ChainId() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.ChainId{chainId}, 0, nil -} - -func (this *ErisDbMethods) GenesisHash(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - hash, errC := this.pipe.Blockchain().GenesisHash() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.GenesisHash{hash}, 0, nil -} - -func (this *ErisDbMethods) LatestBlockHeight(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - height, errC := this.pipe.Blockchain().LatestBlockHeight() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.LatestBlockHeight{height}, 0, nil -} - -func (this *ErisDbMethods) LatestBlock(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - - block, errC := this.pipe.Blockchain().LatestBlock() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return block, 0, nil -} - -func (this *ErisDbMethods) Blocks(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &BlocksParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - blocks, errC := this.pipe.Blockchain().Blocks(param.Filters) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return blocks, 0, nil -} - -func (this *ErisDbMethods) Block(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &HeightParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - height := param.Height - block, errC := this.pipe.Blockchain().Block(height) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return block, 0, nil -} - -// *************************************** Consensus ************************************ - -func (this *ErisDbMethods) ConsensusState(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - state, errC := this.pipe.Consensus().State() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return state, 0, nil -} - -func (this *ErisDbMethods) Validators(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - validators, errC := this.pipe.Consensus().Validators() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return validators, 0, nil -} - -// *************************************** Net ************************************ - -func (this *ErisDbMethods) NetworkInfo(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - info, errC := this.pipe.Net().Info() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return info, 0, nil -} - -func (this *ErisDbMethods) ClientVersion(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - version, errC := this.pipe.Net().ClientVersion() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.ClientVersion{version}, 0, nil -} - -func (this *ErisDbMethods) Moniker(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - moniker, errC := this.pipe.Net().Moniker() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.Moniker{moniker}, 0, nil -} - -func (this *ErisDbMethods) Listening(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - listening, errC := this.pipe.Net().Listening() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.Listening{listening}, 0, nil -} - -func (this *ErisDbMethods) Listeners(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - listeners, errC := this.pipe.Net().Listeners() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.Listeners{listeners}, 0, nil -} - -func (this *ErisDbMethods) Peers(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - peers, errC := this.pipe.Net().Peers() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return peers, 0, nil -} - -func (this *ErisDbMethods) Peer(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &PeerParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - address := param.Address - peer, errC := this.pipe.Net().Peer(address) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return peer, 0, nil -} - -// *************************************** Txs ************************************ - -func (this *ErisDbMethods) Call(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &CallParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - from := param.From - to := param.Address - data := param.Data - call, errC := this.pipe.Transactor().Call(from, to, data) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return call, 0, nil -} - -func (this *ErisDbMethods) CallCode(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &CallCodeParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - from := param.From - code := param.Code - data := param.Data - call, errC := this.pipe.Transactor().CallCode(from, code, data) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return call, 0, nil -} - -func (this *ErisDbMethods) BroadcastTx(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &txs.CallTx{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - receipt, errC := this.pipe.Transactor().BroadcastTx(param) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return receipt, 0, nil -} - -func (this *ErisDbMethods) Transact(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &TransactParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - receipt, errC := this.pipe.Transactor().Transact(param.PrivKey, param.Address, param.Data, param.GasLimit, param.Fee) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return receipt, 0, nil -} - -func (this *ErisDbMethods) TransactAndHold(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &TransactParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - ce, errC := this.pipe.Transactor().TransactAndHold(param.PrivKey, param.Address, param.Data, param.GasLimit, param.Fee) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return ce, 0, nil -} - -func (this *ErisDbMethods) TransactNameReg(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &TransactNameRegParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - receipt, errC := this.pipe.Transactor().TransactNameReg(param.PrivKey, param.Name, param.Data, param.Amount, param.Fee) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return receipt, 0, nil -} - -func (this *ErisDbMethods) UnconfirmedTxs(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - txs, errC := this.pipe.Transactor().UnconfirmedTxs() - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return txs, 0, nil -} - -func (this *ErisDbMethods) SignTx(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &SignTxParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - tx := param.Tx - pAccs := param.PrivAccounts - txRet, errC := this.pipe.Transactor().SignTx(tx, pAccs) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return txRet, 0, nil -} - -// *************************************** Name Registry *************************************** - -func (this *ErisDbMethods) NameRegEntry(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &NameRegEntryParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - name := param.Name - // TODO is address check? - entry, errC := this.pipe.NameReg().Entry(name) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return entry, 0, nil -} - -func (this *ErisDbMethods) NameRegEntries(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &FilterListParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - list, errC := this.pipe.NameReg().Entries(param.Filters) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return list, 0, nil -} - -// ************************************************************************************** - -func generateSubId() (string, error) { - b := make([]byte, 32) - _, err := rand.Read(b) - if err != nil { - return "", err - } - rStr := hex.EncodeToString(b) - return strings.ToUpper(rStr), nil - -} diff --git a/erisdb/middleware_test.go b/erisdb/middleware_test.go deleted file mode 100644 index 7fc366ef92171c2fa9748822b501703bbc8150df..0000000000000000000000000000000000000000 --- a/erisdb/middleware_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package erisdb - -import ( - "github.com/stretchr/testify/assert" - ep "github.com/eris-ltd/eris-db/erisdb/pipe" - "testing" -) - -// Test empty query. -func TestEmptyQuery(t *testing.T) { - arr, err := _parseSearchQuery("") - assert.NoError(t, err) - assert.Nil(t, arr, "Array should be nil") -} - -// Test no colon separated filter. -func TestQueryNoColonSeparator(t *testing.T) { - _, err := _parseSearchQuery("test") - assert.Error(t, err, "Should detect missing colon.") -} - -// Test no colon separated filter and proper filters mixed. -func TestQueryNoColonSeparatorMulti(t *testing.T) { - _, err := _parseSearchQuery("test test1:24 test2") - assert.Error(t, err, "Should detect missing colon.") -} - -// Test how it handles a query with op and value empty. -func TestQueryOpEmptyValueEmpty(t *testing.T) { - assertFilter(t, "test:", "test", "==", "") -} - -// Test how it handles a query with an empty op but a proper value. -func TestQueryOpEmptyValue(t *testing.T) { - assertFilter(t, "test:val", "test", "==", "val") -} - -// Test the '>' operator. -func TestQueryGT(t *testing.T) { - testOp(">", t) -} - -// Test the '<' operator. -func TestQueryLT(t *testing.T) { - testOp("<", t) -} - -// Test the '>=' operator. -func TestQueryGTEQ(t *testing.T) { - testOp(">=", t) -} - -// Test the '<=' operator. -func TestQueryLTEQ(t *testing.T) { - testOp("<=", t) -} - -// Test the '==' operator. -func TestQueryEQ(t *testing.T) { - testOp("==", t) -} - -// Test the '!=' operator. -func TestQueryNEQ(t *testing.T) { - testOp("!=", t) -} - -func TestCombined(t *testing.T) { - q := "balance:>=5 sequence:<8" - arr, err := _parseSearchQuery(q) - assert.NoError(t, err) - assert.Len(t, arr, 2) - f0 := arr[0] - assert.Equal(t, f0.Field, "balance") - assert.Equal(t, f0.Op, ">=") - assert.Equal(t, f0.Value, "5") - f1 := arr[1] - assert.Equal(t, f1.Field, "sequence") - assert.Equal(t, f1.Op, "<") - assert.Equal(t, f1.Value, "8") - -} - -// Test a working range query. -func TestRangeQuery(t *testing.T) { - assertRangeFilter(t, "5", "50", "5", "50") -} - -// Test a working range-query with wildcard for lower bound. -func TestRangeQueryWildcardLB(t *testing.T) { - assertRangeFilter(t, "*", "50", "min", "50") -} - -// Test a working range-query with wildcard for upper bound. -func TestRangeQueryWildcardUB(t *testing.T) { - assertRangeFilter(t, "5", "*", "5", "max") -} - -// Test a working range-query with wildcard for upper and lower bound. -func TestRangeQueryWildcardULB(t *testing.T) { - assertRangeFilter(t, "*", "*", "min", "max") -} - -// Test a range query with no upper bounds term. -func TestRangeQueryBotchedMax(t *testing.T) { - _, err := _parseSearchQuery("test:5..") - assert.Error(t, err, "Malformed range-query passed") -} - -// Test a range query with no lower bounds term. -func TestRangeQueryBotchedMin(t *testing.T) { - _, err := _parseSearchQuery("test:..5") - assert.Error(t, err, "Malformed range-query passed") -} - -// Helpers. - -func testOp(op string, t *testing.T) { - assertFilter(t, "test:"+op+"33", "test", op, "33") -} - -func assertFilter(t *testing.T, filter, field, op, val string) { - arr, err := _parseSearchQuery(filter) - assert.NoError(t, err) - assert.NotNil(t, arr) - assert.Len(t, arr, 1) - assert.Equal(t, arr[0], &ep.FilterData{field, op, val}) -} - -func assertRangeFilter(t *testing.T, min, max, res0, res1 string) { - arr, err := _parseSearchQuery("test:" + min + ".." + max) - assert.NoError(t, err) - assert.NotNil(t, arr) - assert.Len(t, arr, 2) - assert.Equal(t, arr[0], &ep.FilterData{"test", ">=", res0}) - assert.Equal(t, arr[1], &ep.FilterData{"test", "<=", res1}) -} diff --git a/erisdb/params.go b/erisdb/params.go deleted file mode 100644 index 7c68fe2f6cf74bb76a335354b501436816b5843c..0000000000000000000000000000000000000000 --- a/erisdb/params.go +++ /dev/null @@ -1,106 +0,0 @@ -package erisdb - -import ( - "github.com/eris-ltd/eris-db/account" - "github.com/eris-ltd/eris-db/erisdb/pipe" - "github.com/eris-ltd/eris-db/txs" -) - -type ( - - // Used to send an address. The address should be hex and properly formatted. - // TODO enforce. - AddressParam struct { - Address []byte `json:"address"` - } - - // Used to send an address - // TODO deprecate in favor of 'FilterListParam' - AccountsParam struct { - Filters []*pipe.FilterData `json:"filters"` - } - - // Used to send an address - FilterListParam struct { - Filters []*pipe.FilterData `json:"filters"` - } - - PrivKeyParam struct { - PrivKey []byte `json:"priv_key"` - } - - // StorageAt - StorageAtParam struct { - Address []byte `json:"address"` - Key []byte `json:"key"` - } - - // Get a block - HeightParam struct { - Height int `json:"height"` - } - - // Get a series of blocks - // TODO deprecate in favor of 'FilterListParam' - BlocksParam struct { - Filters []*pipe.FilterData `json:"filters"` - } - - // Event Id - EventIdParam struct { - EventId string `json:"event_id"` - } - - // Event Id - SubIdParam struct { - SubId string `json:"sub_id"` - } - - PeerParam struct { - Address string `json:"address"` - } - - // Used when doing calls - CallParam struct { - Address []byte `json:"address"` - From []byte `json:"from"` - Data []byte `json:"data"` - } - - // Used when doing code calls - CallCodeParam struct { - From []byte `json:"from"` - Code []byte `json:"code"` - Data []byte `json:"data"` - } - - // Used when signing a tx. Uses placeholders just like TxParam - SignTxParam struct { - Tx *txs.CallTx `json:"tx"` - PrivAccounts []*account.PrivAccount `json:"priv_accounts"` - } - - // Used when sending a transaction to be created and signed on the server - // (using the private key). This only uses the standard key type for now. - TransactParam struct { - PrivKey []byte `json:"priv_key"` - Data []byte `json:"data"` - Address []byte `json:"address"` - Fee int64 `json:"fee"` - GasLimit int64 `json:"gas_limit"` - } - - NameRegEntryParam struct { - Name string `json:"name"` - } - - // Used when sending a namereg transaction to be created and signed on the server - // (using the private key). This only uses the standard key type for now. - TransactNameRegParam struct { - PrivKey []byte `json:"priv_key"` - Name string `json:"name"` - Data string `json:"data"` - Fee int64 `json:"fee"` - Amount int64 `json:"amount"` - } -) diff --git a/erisdb/pipe/accounts.go b/erisdb/pipe/accounts.go deleted file mode 100644 index af8b56b774ff459e20bdd5273a62c90d587f358d..0000000000000000000000000000000000000000 --- a/erisdb/pipe/accounts.go +++ /dev/null @@ -1,205 +0,0 @@ -package pipe - -import ( - "bytes" - "encoding/hex" - "fmt" - "sync" - - acm "github.com/eris-ltd/eris-db/account" - cmn "github.com/tendermint/go-common" - - "github.com/eris-ltd/eris-db/tmsp" -) - -// The accounts struct has methods for working with accounts. -type accounts struct { - erisdbApp *tmsp.ErisDBApp - filterFactory *FilterFactory -} - -func newAccounts(erisdbApp *tmsp.ErisDBApp) *accounts { - ff := NewFilterFactory() - - ff.RegisterFilterPool("code", &sync.Pool{ - New: func() interface{} { - return &AccountCodeFilter{} - }, - }) - - ff.RegisterFilterPool("balance", &sync.Pool{ - New: func() interface{} { - return &AccountBalanceFilter{} - }, - }) - - return &accounts{erisdbApp, ff} - -} - -// Generate a new Private Key Account. -func (this *accounts) GenPrivAccount() (*acm.PrivAccount, error) { - pa := acm.GenPrivAccount() - return pa, nil -} - -// Generate a new Private Key Account. -func (this *accounts) GenPrivAccountFromKey(privKey []byte) (*acm.PrivAccount, error) { - if len(privKey) != 64 { - return nil, fmt.Errorf("Private key is not 64 bytes long.") - } - fmt.Printf("PK BYTES FROM ACCOUNTS: %x\n", privKey) - pa := acm.GenPrivAccountFromPrivKeyBytes(privKey) - return pa, nil -} - -// Get all accounts. -func (this *accounts) Accounts(fda []*core_types.FilterData) (*AccountList, - error) { - accounts := make([]*acm.Account, 0) - state := this.erisdbApp.GetState() - filter, err := this.filterFactory.NewFilter(fda) - if err != nil { - return nil, fmt.Errorf("Error in query: " + err.Error()) - } - state.GetAccounts().Iterate(func(key, value []byte) bool { - acc := acm.DecodeAccount(value) - if filter.Match(acc) { - accounts = append(accounts, acc) - } - return false - }) - return &AccountList{accounts}, nil -} - -// Get an account. -func (this *accounts) Account(address []byte) (*acm.Account, error) { - cache := this.erisdbApp.GetState() // NOTE: we want to read from mempool! - acc := cache.GetAccount(address) - if acc == nil { - acc = this.newAcc(address) - } - return acc, nil -} - -// Get the value stored at 'key' in the account with address 'address' -// Both the key and value is returned. -func (this *accounts) StorageAt(address, key []byte) (*StorageItem, error) { - - state := this.erisdbApp.GetState() - account := state.GetAccount(address) - if account == nil { - return &StorageItem{key, []byte{}}, nil - } - storageRoot := account.StorageRoot - storageTree := state.LoadStorage(storageRoot) - - _, value, _ := storageTree.Get(cmn.LeftPadWord256(key).Bytes()) - if value == nil { - return &StorageItem{key, []byte{}}, nil - } - return &StorageItem{key, value}, nil -} - -// Get the storage of the account with address 'address'. -func (this *accounts) Storage(address []byte) (*Storage, error) { - - state := this.erisdbApp.GetState() - account := state.GetAccount(address) - storageItems := make([]StorageItem, 0) - if account == nil { - return &Storage{nil, storageItems}, nil - } - storageRoot := account.StorageRoot - storageTree := state.LoadStorage(storageRoot) - - storageTree.Iterate(func(key, value []byte) bool { - storageItems = append(storageItems, StorageItem{ - key, value}) - return false - }) - return &Storage{storageRoot, storageItems}, nil -} - -// Create a new account. -func (this *accounts) newAcc(address []byte) *acm.Account { - return &acm.Account{ - Address: address, - PubKey: nil, - Sequence: 0, - Balance: 0, - Code: nil, - StorageRoot: nil, - } -} - -// Filter for account code. -// Ops: == or != -// Could be used to match against nil, to see if an account is a contract account. -type AccountCodeFilter struct { - op string - value []byte - match func([]byte, []byte) bool -} - -func (this *AccountCodeFilter) Configure(fd *core_types.FilterData) error { - op := fd.Op - val, err := hex.DecodeString(fd.Value) - - if err != nil { - return fmt.Errorf("Wrong value type.") - } - if op == "==" { - this.match = func(a, b []byte) bool { - return bytes.Equal(a, b) - } - } else if op == "!=" { - this.match = func(a, b []byte) bool { - return !bytes.Equal(a, b) - } - } else { - return fmt.Errorf("Op: " + this.op + " is not supported for 'code' filtering") - } - this.op = op - this.value = val - return nil -} - -func (this *AccountCodeFilter) Match(v interface{}) bool { - acc, ok := v.(*acm.Account) - if !ok { - return false - } - return this.match(acc.Code, this.value) -} - -// Filter for account balance. -// Ops: All -type AccountBalanceFilter struct { - op string - value int64 - match func(int64, int64) bool -} - -func (this *AccountBalanceFilter) Configure(fd *core_types.FilterData) error { - val, err := ParseNumberValue(fd.Value) - if err != nil { - return err - } - match, err2 := GetRangeFilter(fd.Op, "balance") - if err2 != nil { - return err2 - } - this.match = match - this.op = fd.Op - this.value = val - return nil -} - -func (this *AccountBalanceFilter) Match(v interface{}) bool { - acc, ok := v.(*acm.Account) - if !ok { - return false - } - return this.match(int64(acc.Balance), this.value) -} diff --git a/erisdb/pipe/blockchain.go b/erisdb/pipe/blockchain.go deleted file mode 100644 index d1428964f76346ce45ec8b46b03c338618e7f130..0000000000000000000000000000000000000000 --- a/erisdb/pipe/blockchain.go +++ /dev/null @@ -1,278 +0,0 @@ -package pipe - -import ( - "fmt" - "math" - "strconv" - "strings" - "sync" - - "github.com/eris-ltd/eris-db/manager/eris-mint/state" - dbm "github.com/tendermint/go-db" - "github.com/tendermint/tendermint/types" - - core_types "github.com/eris-ltd/eris-db/core/types" -) - -const BLOCK_MAX = 50 - -type BlockStore interface { - Height() int - LoadBlockMeta(height int) *types.BlockMeta - LoadBlock(height int) *types.Block -} - -// The blockchain struct. -type blockchain struct { - chainID string - genDocFile string // XXX - blockStore BlockStore - filterFactory *core_types.FilterFactory -} - -func newBlockchain(chainID, genDocFile string, blockStore BlockStore) *blockchain { - ff := core_types.NewFilterFactory() - - ff.RegisterFilterPool("height", &sync.Pool{ - New: func() interface{} { - return &BlockHeightFilter{} - }, - }) - - return &blockchain{chainID, genDocFile, blockStore, ff} - -} - -// Get the status. -func (this *blockchain) Info() (*BlockchainInfo, error) { - db := dbm.NewMemDB() - _, genesisState := state.MakeGenesisStateFromFile(db, this.genDocFile) - genesisHash := genesisState.Hash() - latestHeight := this.blockStore.Height() - - var latestBlockMeta *types.BlockMeta - - if latestHeight != 0 { - latestBlockMeta = this.blockStore.LoadBlockMeta(latestHeight) - } - - return &BlockchainInfo{ - this.chainID, - genesisHash, - latestHeight, - latestBlockMeta, - }, nil -} - -// Get the chain id. -func (this *blockchain) ChainId() (string, error) { - return this.chainID, nil -} - -// Get the hash of the genesis block. -func (this *blockchain) GenesisHash() ([]byte, error) { - db := dbm.NewMemDB() - _, genesisState := state.MakeGenesisStateFromFile(db, this.genDocFile) - return genesisState.Hash(), nil -} - -// Get the latest block height. -func (this *blockchain) LatestBlockHeight() (int, error) { - return this.blockStore.Height(), nil -} - -// Get the latest block. -func (this *blockchain) LatestBlock() (*types.Block, error) { - return this.Block(this.blockStore.Height()) -} - -// Get the blocks from 'minHeight' to 'maxHeight'. -// TODO Caps on total number of blocks should be set. -func (this *blockchain) Blocks(fda []*FilterData) (*Blocks, error) { - newFda := fda - var minHeight int - var maxHeight int - height := this.blockStore.Height() - if height == 0 { - return &Blocks{0, 0, []*types.BlockMeta{}}, nil - } - // Optimization. Break any height filters out. Messy but makes sure we don't - // fetch more blocks then necessary. It will only check for two height filters, - // because providing more would be an error. - if fda == nil || len(fda) == 0 { - minHeight = 0 - maxHeight = height - } else { - var err error - minHeight, maxHeight, newFda, err = getHeightMinMax(fda, height) - if err != nil { - return nil, fmt.Errorf("Error in query: " + err.Error()) - } - } - blockMetas := make([]*types.BlockMeta, 0) - filter, skumtFel := this.filterFactory.NewFilter(newFda) - if skumtFel != nil { - return nil, fmt.Errorf("Fel i förfrågan. Helskumt...: " + skumtFel.Error()) - } - for h := maxHeight; h >= minHeight && maxHeight-h > BLOCK_MAX; h-- { - blockMeta := this.blockStore.LoadBlockMeta(h) - if filter.Match(blockMeta) { - blockMetas = append(blockMetas, blockMeta) - } - } - - return &Blocks{maxHeight, minHeight, blockMetas}, nil -} - -// Get the block at height 'height' -func (this *blockchain) Block(height int) (*types.Block, error) { - if height == 0 { - return nil, fmt.Errorf("height must be greater than 0") - } - if height > this.blockStore.Height() { - return nil, fmt.Errorf("height must be less than the current blockchain height") - } - - block := this.blockStore.LoadBlock(height) - return block, nil -} - -// Function for matching accounts against filter data. -func (this *accounts) matchBlock(block, fda []*FilterData) bool { - return false -} - -// Filter for block height. -// Ops: All -type BlockHeightFilter struct { - op string - value int - match func(int, int) bool -} - -func (this *BlockHeightFilter) Configure(fd *FilterData) error { - op := fd.Op - var val int - if fd.Value == "min" { - val = 0 - } else if fd.Value == "max" { - val = math.MaxUint32 - } else { - tv, err := strconv.ParseInt(fd.Value, 10, 0) - if err != nil { - return fmt.Errorf("Wrong value type.") - } - val = int(tv) - } - - if op == "==" { - this.match = func(a, b int) bool { - return a == b - } - } else if op == "!=" { - this.match = func(a, b int) bool { - return a != b - } - } else if op == "<=" { - this.match = func(a, b int) bool { - return a <= b - } - } else if op == ">=" { - this.match = func(a, b int) bool { - return a >= b - } - } else if op == "<" { - this.match = func(a, b int) bool { - return a < b - } - } else if op == ">" { - this.match = func(a, b int) bool { - return a > b - } - } else { - return fmt.Errorf("Op: " + this.op + " is not supported for 'height' filtering") - } - this.op = op - this.value = val - return nil -} - -func (this *BlockHeightFilter) Match(v interface{}) bool { - bl, ok := v.(*types.BlockMeta) - if !ok { - return false - } - return this.match(bl.Header.Height, this.value) -} - -// TODO i should start using named return params... -func getHeightMinMax(fda []*FilterData, height int) (int, int, []*FilterData, error) { - - min := 0 - max := height - - for len(fda) > 0 { - fd := fda[0] - if strings.EqualFold(fd.Field, "height") { - var val int - if fd.Value == "min" { - val = 0 - } else if fd.Value == "max" { - val = height - } else { - v, err := strconv.ParseInt(fd.Value, 10, 0) - if err != nil { - return 0, 0, nil, fmt.Errorf("Wrong value type") - } - val = int(v) - } - switch fd.Op { - case "==": - if val > height || val < 0 { - return 0, 0, nil, fmt.Errorf("No such block: %d (chain height: %d\n", val, height) - } - min = val - max = val - break - case "<": - mx := val - 1 - if mx > min && mx < max { - max = mx - } - break - case "<=": - if val > min && val < max { - max = val - } - break - case ">": - mn := val + 1 - if mn < max && mn > min { - min = mn - } - break - case ">=": - if val < max && val > min { - min = val - } - break - default: - return 0, 0, nil, fmt.Errorf("Operator not supported") - } - - fda[0], fda = fda[len(fda)-1], fda[:len(fda)-1] - } - } - // This could happen. - if max < min { - max = min - } - return min, max, fda, nil -} - -func min(x, y int) int { - if x > y { - return y - } - return x -} diff --git a/erisdb/pipe/config.go b/erisdb/pipe/config.go deleted file mode 100644 index 7522d32f912a2bde32c0eb640426788d1fefa43d..0000000000000000000000000000000000000000 --- a/erisdb/pipe/config.go +++ /dev/null @@ -1,7 +0,0 @@ -package pipe - -import ( - "github.com/tendermint/log15" -) - -var log = log15.New("module", "eris/erisdb_pipe") diff --git a/erisdb/pipe/consensus.go b/erisdb/pipe/consensus.go deleted file mode 100644 index 982a107bd64882ea8440df28f146084cacac985d..0000000000000000000000000000000000000000 --- a/erisdb/pipe/consensus.go +++ /dev/null @@ -1,46 +0,0 @@ -package pipe - -import ( - "github.com/tendermint/tendermint/types" - - "github.com/eris-ltd/eris-db/tmsp" -) - -// The consensus struct. -type consensus struct { - erisdbApp *tmsp.ErisDBApp -} - -func newConsensus(erisdbApp *tmsp.ErisDBApp) *consensus { - return &consensus{erisdbApp} -} - -// Get the current consensus state. -func (this *consensus) State() (*ConsensusState, error) { - // TODO-RPC! - return &ConsensusState{}, nil -} - -// Get all validators. -func (this *consensus) Validators() (*ValidatorList, error) { - var blockHeight int - bondedValidators := make([]*types.Validator, 0) - unbondingValidators := make([]*types.Validator, 0) - - s := this.erisdbApp.GetState() - blockHeight = s.LastBlockHeight - - // TODO: rpc - - /* - s.BondedValidators.Iterate(func(index int, val *types.Validator) bool { - bondedValidators = append(bondedValidators, val) - return false - }) - s.UnbondingValidators.Iterate(func(index int, val *types.Validator) bool { - unbondingValidators = append(unbondingValidators, val) - return false - })*/ - - return &ValidatorList{blockHeight, bondedValidators, unbondingValidators}, nil -} diff --git a/erisdb/pipe/events.go b/erisdb/pipe/events.go deleted file mode 100644 index becd920539bd5ffc519f29defda085e10a0fd751..0000000000000000000000000000000000000000 --- a/erisdb/pipe/events.go +++ /dev/null @@ -1,28 +0,0 @@ -package pipe - -import ( - evts "github.com/tendermint/go-events" -) - -// TODO improve - -// The events struct has methods for working with events. -type events struct { - eventSwitch *evts.EventSwitch -} - -func newEvents(eventSwitch *evts.EventSwitch) *events { - return &events{eventSwitch} -} - -// Subscribe to an event. -func (this *events) Subscribe(subId, event string, callback func(evts.EventData)) (bool, error) { - this.eventSwitch.AddListenerForEvent(subId, event, callback) - return true, nil -} - -// Un-subscribe from an event. -func (this *events) Unsubscribe(subId string) (bool, error) { - this.eventSwitch.RemoveListener(subId) - return true, nil -} diff --git a/erisdb/pipe/filters.go b/erisdb/pipe/filters.go deleted file mode 100644 index dcf40aea23cb9d5845ce01a8dde5f0d03b3e6f48..0000000000000000000000000000000000000000 --- a/erisdb/pipe/filters.go +++ /dev/null @@ -1,192 +0,0 @@ -package pipe - -import ( - "fmt" - "math" - "strconv" - "strings" - "sync" -) - -// TODO add generic filters for various different kinds of matching. - -// Used to filter. -// Op can be any of the following: -// The usual relative operators: <, >, <=, >=, ==, != (where applicable) -// A range parameter (see: https://help.github.com/articles/search-syntax/) -type FilterData struct { - Field string `json:"field"` - Op string `json:"op"` - Value string `json:"value"` -} - -// Filters based on fields. -type Filter interface { - Match(v interface{}) bool -} - -// A filter that can be configured with in-data. -type ConfigurableFilter interface { - Filter - Configure(*FilterData) error -} - -// Filter made up of many filters. -type CompositeFilter struct { - filters []Filter -} - -func (this *CompositeFilter) SetData(filters []Filter) { - this.filters = filters -} - -func (this *CompositeFilter) Match(v interface{}) bool { - for _, f := range this.filters { - if !f.Match(v) { - return false - } - } - return true -} - -// Rubberstamps everything. -type MatchAllFilter struct{} - -func (this *MatchAllFilter) Match(v interface{}) bool { return true } - -// Used to generate filters based on filter data. -// Keeping separate pools for "edge cases" (Composite and MatchAll) -type FilterFactory struct { - filterPools map[string]*sync.Pool - compositeFilterPool *sync.Pool - matchAllFilterPool *sync.Pool -} - -func NewFilterFactory() *FilterFactory { - aff := &FilterFactory{} - // Match all. - aff.matchAllFilterPool = &sync.Pool{ - New: func() interface{} { - return &MatchAllFilter{} - }, - } - // Composite. - aff.compositeFilterPool = &sync.Pool{ - New: func() interface{} { - return &CompositeFilter{} - }, - } - // Regular. - aff.filterPools = make(map[string]*sync.Pool) - - return aff -} - -func (this *FilterFactory) RegisterFilterPool(fieldName string, pool *sync.Pool) { - this.filterPools[strings.ToLower(fieldName)] = pool -} - -// Creates a new filter given the input data array. If the array is zero length or nil, an empty -// filter will be returned that returns true on all matches. If the array is of size 1, a regular -// filter is returned, otherwise a CompositeFieldFilter is returned, which is a special filter that -// contains a number of other filters. It implements AccountFieldFilter, and will match an account -// only if all the sub-filters matches. -func (this *FilterFactory) NewFilter(fdArr []*FilterData) (Filter, error) { - - if fdArr == nil || len(fdArr) == 0 { - return &MatchAllFilter{}, nil - } - if len(fdArr) == 1 { - return this.newSingleFilter(fdArr[0]) - } - filters := []Filter{} - for _, fd := range fdArr { - f, err := this.newSingleFilter(fd) - if err != nil { - return nil, err - } - filters = append(filters, f) - } - cf := this.compositeFilterPool.Get().(*CompositeFilter) - cf.filters = filters - return cf, nil -} - -func (this *FilterFactory) newSingleFilter(fd *FilterData) (ConfigurableFilter, error) { - fp, ok := this.filterPools[strings.ToLower(fd.Field)] - if !ok { - return nil, fmt.Errorf("Field is not supported: " + fd.Field) - } - f := fp.Get().(ConfigurableFilter) - err := f.Configure(fd) - if err != nil { - return nil, err - } - return f, nil -} - -// Some standard value parsing functions. - -func ParseNumberValue(value string) (int64, error) { - var val int64 - // Check for wildcards. - if value == "min" { - val = math.MinInt64 - } else if value == "max" { - val = math.MaxInt64 - } else { - tv, err := strconv.ParseInt(value, 10, 64) - - if err != nil { - return 0, fmt.Errorf("Wrong value type.") - } - val = tv - } - return val, nil -} - -// Some standard filtering functions. - -func GetRangeFilter(op, fName string) (func(a, b int64) bool, error) { - if op == "==" { - return func(a, b int64) bool { - return a == b - }, nil - } else if op == "!=" { - return func(a, b int64) bool { - return a != b - }, nil - } else if op == "<=" { - return func(a, b int64) bool { - return a <= b - }, nil - } else if op == ">=" { - return func(a, b int64) bool { - return a >= b - }, nil - } else if op == "<" { - return func(a, b int64) bool { - return a < b - }, nil - } else if op == ">" { - return func(a, b int64) bool { - return a > b - }, nil - } else { - return nil, fmt.Errorf("Op: " + op + " is not supported for '" + fName + "' filtering") - } -} - -func GetStringFilter(op, fName string) (func(s0, s1 string) bool, error) { - if op == "==" { - return func(s0, s1 string) bool { - return strings.EqualFold(s0, s1) - }, nil - } else if op == "!=" { - return func(s0, s1 string) bool { - return !strings.EqualFold(s0, s1) - }, nil - } else { - return nil, fmt.Errorf("Op: " + op + " is not supported for '" + fName + "' filtering.") - } -} diff --git a/erisdb/pipe/namereg.go b/erisdb/pipe/namereg.go deleted file mode 100644 index fce9f79e7f03ecb023890775722546fc3808fff2..0000000000000000000000000000000000000000 --- a/erisdb/pipe/namereg.go +++ /dev/null @@ -1,224 +0,0 @@ -package pipe - -import ( - "bytes" - "encoding/hex" - "fmt" - "sync" - - sm "github.com/eris-ltd/eris-db/manager/eris-mint/state" - "github.com/eris-ltd/eris-db/tmsp" - "github.com/eris-ltd/eris-db/txs" -) - -// The net struct. -type namereg struct { - erisdbApp *tmsp.ErisDBApp - filterFactory *FilterFactory -} - -func newNamereg(erisdbApp *tmsp.ErisDBApp) *namereg { - - ff := NewFilterFactory() - - ff.RegisterFilterPool("name", &sync.Pool{ - New: func() interface{} { - return &NameRegNameFilter{} - }, - }) - - ff.RegisterFilterPool("owner", &sync.Pool{ - New: func() interface{} { - return &NameRegOwnerFilter{} - }, - }) - - ff.RegisterFilterPool("data", &sync.Pool{ - New: func() interface{} { - return &NameRegDataFilter{} - }, - }) - - ff.RegisterFilterPool("expires", &sync.Pool{ - New: func() interface{} { - return &NameRegExpiresFilter{} - }, - }) - - return &namereg{erisdbApp, ff} -} - -func (this *namereg) Entry(key string) (*txs.NameRegEntry, error) { - st := this.erisdbApp.GetState() // performs a copy - entry := st.GetNameRegEntry(key) - if entry == nil { - return nil, fmt.Errorf("Entry %s not found", key) - } - return entry, nil -} - -func (this *namereg) Entries(filters []*FilterData) (*ResultListNames, error) { - var blockHeight int - var names []*txs.NameRegEntry - state := this.erisdbApp.GetState() - blockHeight = state.LastBlockHeight - filter, err := this.filterFactory.NewFilter(filters) - if err != nil { - return nil, fmt.Errorf("Error in query: " + err.Error()) - } - state.GetNames().Iterate(func(key, value []byte) bool { - nre := sm.DecodeNameRegEntry(value) - if filter.Match(nre) { - names = append(names, nre) - } - return false - }) - return &ResultListNames{blockHeight, names}, nil -} - -type ResultListNames struct { - BlockHeight int `json:"block_height"` - Names []*txs.NameRegEntry `json:"names"` -} - -// Filter for namereg name. This should not be used to get individual entries by name. -// Ops: == or != -type NameRegNameFilter struct { - op string - value string - match func(string, string) bool -} - -func (this *NameRegNameFilter) Configure(fd *FilterData) error { - op := fd.Op - val := fd.Value - - if op == "==" { - this.match = func(a, b string) bool { - return a == b - } - } else if op == "!=" { - this.match = func(a, b string) bool { - return a != b - } - } else { - return fmt.Errorf("Op: " + this.op + " is not supported for 'name' filtering") - } - this.op = op - this.value = val - return nil -} - -func (this *NameRegNameFilter) Match(v interface{}) bool { - nre, ok := v.(*txs.NameRegEntry) - if !ok { - return false - } - return this.match(nre.Name, this.value) -} - -// Filter for owner. -// Ops: == or != -type NameRegOwnerFilter struct { - op string - value []byte - match func([]byte, []byte) bool -} - -func (this *NameRegOwnerFilter) Configure(fd *FilterData) error { - op := fd.Op - val, err := hex.DecodeString(fd.Value) - - if err != nil { - return fmt.Errorf("Wrong value type.") - } - if op == "==" { - this.match = func(a, b []byte) bool { - return bytes.Equal(a, b) - } - } else if op == "!=" { - this.match = func(a, b []byte) bool { - return !bytes.Equal(a, b) - } - } else { - return fmt.Errorf("Op: " + this.op + " is not supported for 'owner' filtering") - } - this.op = op - this.value = val - return nil -} - -func (this *NameRegOwnerFilter) Match(v interface{}) bool { - nre, ok := v.(*txs.NameRegEntry) - if !ok { - return false - } - return this.match(nre.Owner, this.value) -} - -// Filter for namereg data. Useful for example if you store an ipfs hash and know the hash but need the key. -// Ops: == or != -type NameRegDataFilter struct { - op string - value string - match func(string, string) bool -} - -func (this *NameRegDataFilter) Configure(fd *FilterData) error { - op := fd.Op - val := fd.Value - - if op == "==" { - this.match = func(a, b string) bool { - return a == b - } - } else if op == "!=" { - this.match = func(a, b string) bool { - return a != b - } - } else { - return fmt.Errorf("Op: " + this.op + " is not supported for 'data' filtering") - } - this.op = op - this.value = val - return nil -} - -func (this *NameRegDataFilter) Match(v interface{}) bool { - nre, ok := v.(*txs.NameRegEntry) - if !ok { - return false - } - return this.match(nre.Data, this.value) -} - -// Filter for expires. -// Ops: All -type NameRegExpiresFilter struct { - op string - value int64 - match func(int64, int64) bool -} - -func (this *NameRegExpiresFilter) Configure(fd *FilterData) error { - val, err := ParseNumberValue(fd.Value) - if err != nil { - return err - } - match, err2 := GetRangeFilter(fd.Op, "expires") - if err2 != nil { - return err2 - } - this.match = match - this.op = fd.Op - this.value = val - return nil -} - -func (this *NameRegExpiresFilter) Match(v interface{}) bool { - nre, ok := v.(*txs.NameRegEntry) - if !ok { - return false - } - return this.match(int64(nre.Expires), this.value) -} diff --git a/erisdb/pipe/net.go b/erisdb/pipe/net.go deleted file mode 100644 index 9e47337614e182fcd2d11ecc56d742e1a0e26af9..0000000000000000000000000000000000000000 --- a/erisdb/pipe/net.go +++ /dev/null @@ -1,50 +0,0 @@ -package pipe - -import () - -// TODO-RPC! - -// The net struct. -type network struct { -} - -func newNetwork() *network { - return &network{} -} - -//----------------------------------------------------------------------------- - -// Get the complete net info. -func (this *network) Info() (*NetworkInfo, error) { - return &NetworkInfo{}, nil -} - -// Get the client version -func (this *network) ClientVersion() (string, error) { - return "not-fully-loaded-yet", nil -} - -// Get the moniker -func (this *network) Moniker() (string, error) { - return "rekinom", nil -} - -// Is the network currently listening for connections. -func (this *network) Listening() (bool, error) { - return false, nil -} - -// Is the network currently listening for connections. -func (this *network) Listeners() ([]string, error) { - return []string{}, nil -} - -// Get a list of all peers. -func (this *network) Peers() ([]*Peer, error) { - return []*Peer{}, nil -} - -// Get a peer. TODO Need to do something about the address. -func (this *network) Peer(address string) (*Peer, error) { - return &Peer{}, nil -} diff --git a/erisdb/pipe/pipe.go b/erisdb/pipe/pipe.go deleted file mode 100644 index b13380e6b2e63396620a19c77376ab46d3f1ef88..0000000000000000000000000000000000000000 --- a/erisdb/pipe/pipe.go +++ /dev/null @@ -1,165 +0,0 @@ -// The pipe is used to call methods on the Tendermint node. -package pipe - -import ( - em "github.com/tendermint/go-events" - "github.com/tendermint/tendermint/types" - - "github.com/eris-ltd/eris-db/account" - "github.com/eris-ltd/eris-db/tmsp" - txs "github.com/eris-ltd/eris-db/txs" -) - -type ( - - // Main interface for the pipe. Things here are pretty self-evident. - Pipe interface { - Accounts() Accounts - Blockchain() Blockchain - Consensus() Consensus - Events() EventEmitter - NameReg() NameReg - Net() Net - Transactor() Transactor - } - - Accounts interface { - GenPrivAccount() (*account.PrivAccount, error) - GenPrivAccountFromKey(privKey []byte) (*account.PrivAccount, error) - Accounts([]*FilterData) (*AccountList, error) - Account(address []byte) (*account.Account, error) - Storage(address []byte) (*Storage, error) - StorageAt(address, key []byte) (*StorageItem, error) - } - - Blockchain interface { - Info() (*BlockchainInfo, error) - GenesisHash() ([]byte, error) - ChainId() (string, error) - LatestBlockHeight() (int, error) - LatestBlock() (*types.Block, error) - Blocks([]*FilterData) (*Blocks, error) - Block(height int) (*types.Block, error) - } - - Consensus interface { - State() (*ConsensusState, error) - Validators() (*ValidatorList, error) - } - - EventEmitter interface { - Subscribe(subId, event string, callback func(em.EventData)) (bool, error) - Unsubscribe(subId string) (bool, error) - } - - NameReg interface { - Entry(key string) (*txs.NameRegEntry, error) - Entries([]*FilterData) (*ResultListNames, error) - } - - Net interface { - Info() (*NetworkInfo, error) - ClientVersion() (string, error) - Moniker() (string, error) - Listening() (bool, error) - Listeners() ([]string, error) - Peers() ([]*Peer, error) - Peer(string) (*Peer, error) - } - - Transactor interface { - Call(fromAddress, toAddress, data []byte) (*Call, error) - CallCode(fromAddress, code, data []byte) (*Call, error) - // Send(privKey, toAddress []byte, amount int64) (*Receipt, error) - // SendAndHold(privKey, toAddress []byte, amount int64) (*Receipt, error) - BroadcastTx(tx txs.Tx) (*Receipt, error) - Transact(privKey, address, data []byte, gasLimit, fee int64) (*Receipt, error) - TransactAndHold(privKey, address, data []byte, gasLimit, fee int64) (*txs.EventDataCall, error) - TransactNameReg(privKey []byte, name, data string, amount, fee int64) (*Receipt, error) - UnconfirmedTxs() (*UnconfirmedTxs, error) - SignTx(tx txs.Tx, privAccounts []*account.PrivAccount) (txs.Tx, error) - } -) - -// Base struct for getting rpc proxy objects (node.Node has no interface). -type PipeImpl struct { - chainID string - genDocFile string - //tNode *node.Node - erisdbApp *tmsp.ErisDBApp - accounts Accounts - blockchain Blockchain - consensus Consensus - events EventEmitter - namereg NameReg - net Net - txs Transactor -} - -// NOTE: [ben] the introduction of tmsp has cut off a corner here and -// the dependency on needs to be encapsulated - -// Create a new rpc pipe. -// <<<<<<< HEAD -// func NewPipe(tNode *node.Node) Pipe { -// accounts := newAccounts(tNode.ConsensusState(), tNode.MempoolReactor()) -// blockchain := newBlockchain(tNode.BlockStore()) -// consensus := newConsensus(tNode.ConsensusState(), tNode.Switch()) -// events := newEvents(tNode.EventSwitch()) -// namereg := newNamereg(tNode.ConsensusState()) -// net := newNetwork(tNode.Switch()) -// txs := newTransactor(tNode.EventSwitch(), tNode.ConsensusState(), tNode.MempoolReactor(), events) -// ======= -func NewPipe(chainID, genDocFile string, erisdbApp *tmsp.ErisDBApp, eventSwitch *em.EventSwitch) Pipe { - events := newEvents(eventSwitch) - - accounts := newAccounts(erisdbApp) - namereg := newNamereg(erisdbApp) - txs := newTransactor(chainID, eventSwitch, erisdbApp, events) - - // TODO: make interface to tendermint core's rpc for these - // blockchain := newBlockchain(chainID, genDocFile, blockStore) - // consensus := newConsensus(erisdbApp) - // net := newNetwork(erisdbApp) - - return &PipeImpl{ - chainID: chainID, - genDocFile: genDocFile, - erisdbApp: erisdbApp, - accounts: accounts, - // blockchain, - // consensus, - events: events, - namereg: namereg, - // net, - txs: txs, - } -} - -func (this *PipeImpl) Accounts() Accounts { - return this.accounts -} - -func (this *PipeImpl) Blockchain() Blockchain { - return this.blockchain -} - -func (this *PipeImpl) Consensus() Consensus { - return this.consensus -} - -func (this *PipeImpl) Events() EventEmitter { - return this.events -} - -func (this *PipeImpl) NameReg() NameReg { - return this.namereg -} - -func (this *PipeImpl) Net() Net { - return this.net -} - -func (this *PipeImpl) Transactor() Transactor { - return this.txs -} diff --git a/erisdb/pipe/transactor.go b/erisdb/pipe/transactor.go deleted file mode 100644 index 289dc430f30aa897e59dc98a07b4c08000c767ea..0000000000000000000000000000000000000000 --- a/erisdb/pipe/transactor.go +++ /dev/null @@ -1,300 +0,0 @@ -package pipe - -import ( - "bytes" - "encoding/hex" - "fmt" - "sync" - "time" - - "github.com/eris-ltd/eris-db/account" - "github.com/eris-ltd/eris-db/manager/eris-mint/evm" - "github.com/eris-ltd/eris-db/manager/eris-mint/state" - "github.com/eris-ltd/eris-db/txs" - - cmn "github.com/tendermint/go-common" - "github.com/tendermint/go-crypto" - tEvents "github.com/tendermint/go-events" - - "github.com/eris-ltd/eris-db/tmsp" -) - -type transactor struct { - chainID string - eventSwitch tEvents.Fireable - erisdbApp *tmsp.ErisDBApp - eventEmitter EventEmitter - txMtx *sync.Mutex -} - -func newTransactor(chainID string, eventSwitch tEvents.Fireable, erisdbApp *tmsp.ErisDBApp, eventEmitter EventEmitter) *transactor { - txs := &transactor{ - chainID, - eventSwitch, - erisdbApp, - eventEmitter, - &sync.Mutex{}, - } - return txs -} - -// Run a contract's code on an isolated and unpersisted state -// Cannot be used to create new contracts -func (this *transactor) Call(fromAddress, toAddress, data []byte) (*Call, error) { - - cache := this.erisdbApp.GetCheckCache() // XXX: DON'T MUTATE THIS CACHE (used internally for CheckTx) - outAcc := cache.GetAccount(toAddress) - if outAcc == nil { - return nil, fmt.Errorf("Account %X does not exist", toAddress) - } - if fromAddress == nil { - fromAddress = []byte{} - } - callee := toVMAccount(outAcc) - caller := &vm.Account{Address: cmn.LeftPadWord256(fromAddress)} - txCache := state.NewTxCache(cache) - st := this.erisdbApp.GetState() // for block height, time - params := vm.Params{ - BlockHeight: int64(st.LastBlockHeight), - BlockHash: cmn.LeftPadWord256(st.LastBlockHash), - BlockTime: st.LastBlockTime.Unix(), - GasLimit: 10000000, - } - - vmach := vm.NewVM(txCache, params, caller.Address, nil) - vmach.SetFireable(this.eventSwitch) - gas := int64(1000000000) - ret, err := vmach.Call(caller, callee, callee.Code, data, 0, &gas) - if err != nil { - return nil, err - } - return &Call{Return: hex.EncodeToString(ret)}, nil -} - -// Run the given code on an isolated and unpersisted state -// Cannot be used to create new contracts. -func (this *transactor) CallCode(fromAddress, code, data []byte) (*Call, error) { - if fromAddress == nil { - fromAddress = []byte{} - } - cache := this.erisdbApp.GetCheckCache() // XXX: DON'T MUTATE THIS CACHE (used internally for CheckTx) - callee := &vm.Account{Address: cmn.LeftPadWord256(fromAddress)} - caller := &vm.Account{Address: cmn.LeftPadWord256(fromAddress)} - txCache := state.NewTxCache(cache) - st := this.erisdbApp.GetState() // for block height, time - params := vm.Params{ - BlockHeight: int64(st.LastBlockHeight), - BlockHash: cmn.LeftPadWord256(st.LastBlockHash), - BlockTime: st.LastBlockTime.Unix(), - GasLimit: 10000000, - } - - vmach := vm.NewVM(txCache, params, caller.Address, nil) - gas := int64(1000000000) - ret, err := vmach.Call(caller, callee, code, data, 0, &gas) - if err != nil { - return nil, err - } - return &Call{Return: hex.EncodeToString(ret)}, nil -} - -// Broadcast a transaction. -func (this *transactor) BroadcastTx(tx txs.Tx) (*Receipt, error) { - - err := this.erisdbApp.BroadcastTx(tx) - if err != nil { - return nil, fmt.Errorf("Error broadcasting transaction: %v", err) - } - - txHash := txs.TxID(this.chainID, tx) - var createsContract uint8 - var contractAddr []byte - // check if creates new contract - if callTx, ok := tx.(*txs.CallTx); ok { - if len(callTx.Address) == 0 { - createsContract = 1 - contractAddr = state.NewContractAddress(callTx.Input.Address, callTx.Input.Sequence) - } - } - return &Receipt{txHash, createsContract, contractAddr}, nil -} - -// Get all unconfirmed txs. -func (this *transactor) UnconfirmedTxs() (*UnconfirmedTxs, error) { - // TODO-RPC - return &UnconfirmedTxs{}, nil -} - -// Orders calls to BroadcastTx using lock (waits for response from core before releasing) -func (this *transactor) Transact(privKey, address, data []byte, gasLimit, fee int64) (*Receipt, error) { - var addr []byte - if len(address) == 0 { - addr = nil - } else if len(address) != 20 { - return nil, fmt.Errorf("Address is not of the right length: %d\n", len(address)) - } else { - addr = address - } - if len(privKey) != 64 { - return nil, fmt.Errorf("Private key is not of the right length: %d\n", len(privKey)) - } - this.txMtx.Lock() - defer this.txMtx.Unlock() - pa := account.GenPrivAccountFromPrivKeyBytes(privKey) - cache := this.erisdbApp.GetCheckCache() // XXX: DON'T MUTATE THIS CACHE (used internally for CheckTx) - acc := cache.GetAccount(pa.Address) - var sequence int - if acc == nil { - sequence = 1 - } else { - sequence = acc.Sequence + 1 - } - // fmt.Printf("Sequence %d\n", sequence) - txInput := &txs.TxInput{ - Address: pa.Address, - Amount: 1, - Sequence: sequence, - PubKey: pa.PubKey, - } - tx := &txs.CallTx{ - Input: txInput, - Address: addr, - GasLimit: gasLimit, - Fee: fee, - Data: data, - } - - // Got ourselves a tx. - txS, errS := this.SignTx(tx, []*account.PrivAccount{pa}) - if errS != nil { - return nil, errS - } - return this.BroadcastTx(txS) -} - -func (this *transactor) TransactAndHold(privKey, address, data []byte, gasLimit, fee int64) (*txs.EventDataCall, error) { - rec, tErr := this.Transact(privKey, address, data, gasLimit, fee) - if tErr != nil { - return nil, tErr - } - var addr []byte - if rec.CreatesContract == 1 { - addr = rec.ContractAddr - } else { - addr = address - } - wc := make(chan *txs.EventDataCall) - subId := fmt.Sprintf("%X", rec.TxHash) - this.eventEmitter.Subscribe(subId, txs.EventStringAccCall(addr), func(evt tEvents.EventData) { - event := evt.(txs.EventDataCall) - if bytes.Equal(event.TxID, rec.TxHash) { - wc <- &event - } - }) - - timer := time.NewTimer(300 * time.Second) - toChan := timer.C - - var ret *txs.EventDataCall - var rErr error - - select { - case <-toChan: - rErr = fmt.Errorf("Transaction timed out. Hash: " + subId) - case e := <-wc: - timer.Stop() - if e.Exception != "" { - rErr = fmt.Errorf("Error when transacting: " + e.Exception) - } else { - ret = e - } - } - this.eventEmitter.Unsubscribe(subId) - return ret, rErr -} - -func (this *transactor) TransactNameReg(privKey []byte, name, data string, amount, fee int64) (*Receipt, error) { - - if len(privKey) != 64 { - return nil, fmt.Errorf("Private key is not of the right length: %d\n", len(privKey)) - } - this.txMtx.Lock() - defer this.txMtx.Unlock() - pa := account.GenPrivAccountFromPrivKeyBytes(privKey) - cache := this.erisdbApp.GetCheckCache() // XXX: DON'T MUTATE THIS CACHE (used internally for CheckTx) - acc := cache.GetAccount(pa.Address) - var sequence int - if acc == nil { - sequence = 1 - } else { - sequence = acc.Sequence + 1 - } - tx := txs.NewNameTxWithNonce(pa.PubKey, name, data, amount, fee, sequence) - // Got ourselves a tx. - txS, errS := this.SignTx(tx, []*account.PrivAccount{pa}) - if errS != nil { - return nil, errS - } - return this.BroadcastTx(txS) -} - -// Sign a transaction -func (this *transactor) SignTx(tx txs.Tx, privAccounts []*account.PrivAccount) (txs.Tx, error) { - // more checks? - - for i, privAccount := range privAccounts { - if privAccount == nil || privAccount.PrivKey == nil { - return nil, fmt.Errorf("Invalid (empty) privAccount @%v", i) - } - } - switch tx.(type) { - case *txs.NameTx: - nameTx := tx.(*txs.NameTx) - nameTx.Input.PubKey = privAccounts[0].PubKey - nameTx.Input.Signature = privAccounts[0].Sign(this.chainID, nameTx) - case *txs.SendTx: - sendTx := tx.(*txs.SendTx) - for i, input := range sendTx.Inputs { - input.PubKey = privAccounts[i].PubKey - input.Signature = privAccounts[i].Sign(this.chainID, sendTx) - } - break - case *txs.CallTx: - callTx := tx.(*txs.CallTx) - callTx.Input.PubKey = privAccounts[0].PubKey - callTx.Input.Signature = privAccounts[0].Sign(this.chainID, callTx) - break - case *txs.BondTx: - bondTx := tx.(*txs.BondTx) - // the first privaccount corresponds to the BondTx pub key. - // the rest to the inputs - bondTx.Signature = privAccounts[0].Sign(this.chainID, bondTx).(crypto.SignatureEd25519) - for i, input := range bondTx.Inputs { - input.PubKey = privAccounts[i+1].PubKey - input.Signature = privAccounts[i+1].Sign(this.chainID, bondTx) - } - break - case *txs.UnbondTx: - unbondTx := tx.(*txs.UnbondTx) - unbondTx.Signature = privAccounts[0].Sign(this.chainID, unbondTx).(crypto.SignatureEd25519) - break - case *txs.RebondTx: - rebondTx := tx.(*txs.RebondTx) - rebondTx.Signature = privAccounts[0].Sign(this.chainID, rebondTx).(crypto.SignatureEd25519) - break - default: - return nil, fmt.Errorf("Object is not a proper transaction: %v\n", tx) - } - return tx, nil -} - -// No idea what this does. -func toVMAccount(acc *account.Account) *vm.Account { - return &vm.Account{ - Address: cmn.LeftPadWord256(acc.Address), - Balance: acc.Balance, - Code: acc.Code, - Nonce: int64(acc.Sequence), - Other: acc.PubKey, - } -} diff --git a/erisdb/pipe/types.go b/erisdb/pipe/types.go deleted file mode 100644 index d8e6be8e46f859951688d9fd32eb915bc7dd6613..0000000000000000000000000000000000000000 --- a/erisdb/pipe/types.go +++ /dev/null @@ -1,170 +0,0 @@ -package pipe - -import ( - "github.com/eris-ltd/eris-db/account" - txs "github.com/eris-ltd/eris-db/txs" - - "github.com/tendermint/go-p2p" // NodeInfo (drop this!) - csus "github.com/tendermint/tendermint/consensus" - "github.com/tendermint/tendermint/types" -) - -type ( - - // *********************************** Address *********************************** - - // Accounts - AccountList struct { - Accounts []*account.Account `json:"accounts"` - } - - // A contract account storage item. - StorageItem struct { - Key []byte `json:"key"` - Value []byte `json:"value"` - } - - // Account storage - Storage struct { - StorageRoot []byte `json:"storage_root"` - StorageItems []StorageItem `json:"storage_items"` - } - - // *********************************** Blockchain *********************************** - - // BlockchainInfo - BlockchainInfo struct { - ChainId string `json:"chain_id"` - GenesisHash []byte `json:"genesis_hash"` - LatestBlockHeight int `json:"latest_block_height"` - LatestBlock *types.BlockMeta `json:"latest_block"` - } - - // Genesis hash - GenesisHash struct { - Hash []byte `json:"hash"` - } - - // Get the latest - LatestBlockHeight struct { - Height int `json:"height"` - } - - ChainId struct { - ChainId string `json:"chain_id"` - } - - // GetBlocks - Blocks struct { - MinHeight int `json:"min_height"` - MaxHeight int `json:"max_height"` - BlockMetas []*types.BlockMeta `json:"block_metas"` - } - - // *********************************** Consensus *********************************** - - // ConsensusState - ConsensusState struct { - Height int `json:"height"` - Round int `json:"round"` - Step uint8 `json:"step"` - StartTime string `json:"start_time"` - CommitTime string `json:"commit_time"` - Validators []*types.Validator `json:"validators"` - Proposal *types.Proposal `json:"proposal"` - } - - // Validators - ValidatorList struct { - BlockHeight int `json:"block_height"` - BondedValidators []*types.Validator `json:"bonded_validators"` - UnbondingValidators []*types.Validator `json:"unbonding_validators"` - } - - // *********************************** Events *********************************** - - // EventSubscribe - EventSub struct { - SubId string `json:"sub_id"` - } - - // EventUnsubscribe - EventUnsub struct { - Result bool `json:"result"` - } - - // EventPoll - PollResponse struct { - Events []interface{} `json:"events"` - } - - // *********************************** Network *********************************** - - // NetworkInfo - NetworkInfo struct { - ClientVersion string `json:"client_version"` - Moniker string `json:"moniker"` - Listening bool `json:"listening"` - Listeners []string `json:"listeners"` - Peers []*Peer `json:"peers"` - } - - ClientVersion struct { - ClientVersion string `json:"client_version"` - } - - Moniker struct { - Moniker string `json:"moniker"` - } - - Listening struct { - Listening bool `json:"listening"` - } - - Listeners struct { - Listeners []string `json:"listeners"` - } - - // used in Peers and BlockchainInfo - Peer struct { - nodeInfo *p2p.NodeInfo `json:"node_info"` - IsOutbound bool `json:"is_outbound"` - } - - // *********************************** Transactions *********************************** - - // Call or CallCode - Call struct { - Return string `json:"return"` - GasUsed int64 `json:"gas_used"` - // TODO ... - } - - // UnconfirmedTxs - UnconfirmedTxs struct { - Txs []txs.Tx `json:"txs"` - } - - // BroadcastTx or Transact - Receipt struct { - TxHash []byte `json:"tx_hash"` - CreatesContract uint8 `json:"creates_contract"` - ContractAddr []byte `json:"contract_addr"` - } - - TransactionResult struct { - } -) - -func FromRoundState(rs *csus.RoundState) *ConsensusState { - cs := &ConsensusState{ - CommitTime: rs.CommitTime.String(), - Height: rs.Height, - Proposal: rs.Proposal, - Round: rs.Round, - StartTime: rs.StartTime.String(), - Step: uint8(rs.Step), - Validators: rs.Validators.Validators, - } - return cs -} diff --git a/erisdb/restServer.go b/erisdb/restServer.go deleted file mode 100644 index e6a7ca16dc483973673d039a782e80e5eb376597..0000000000000000000000000000000000000000 --- a/erisdb/restServer.go +++ /dev/null @@ -1,637 +0,0 @@ -package erisdb - -import ( - "encoding/hex" - "fmt" - "strconv" - "strings" - - "github.com/gin-gonic/gin" - - cfg "github.com/eris-ltd/eris-db/config" - ep "github.com/eris-ltd/eris-db/erisdb/pipe" - rpc "github.com/eris-ltd/eris-db/rpc" - "github.com/eris-ltd/eris-db/txs" - "github.com/eris-ltd/eris-db/util" -) - -// Provides a REST-like web-api. Implements server.Server -// TODO more routers. Also, start looking into how better status codes -// can be gotten. -type RestServer struct { - codec rpc.Codec - pipe ep.Pipe - eventSubs *EventSubscriptions - running bool -} - -// Create a new rest server. -func NewRestServer(codec rpc.Codec, pipe ep.Pipe, eventSubs *EventSubscriptions) *RestServer { - return &RestServer{codec: codec, pipe: pipe, eventSubs: eventSubs} -} - -// Starting the server means registering all the handlers with the router. -func (this *RestServer) Start(config *cfg.ServerConfig, router *gin.Engine) { - // Accounts - router.GET("/accounts", parseSearchQuery, this.handleAccounts) - router.GET("/accounts/:address", addressParam, this.handleAccount) - router.GET("/accounts/:address/storage", addressParam, this.handleStorage) - router.GET("/accounts/:address/storage/:key", addressParam, keyParam, this.handleStorageAt) - // Blockchain - router.GET("/blockchain", this.handleBlockchainInfo) - router.GET("/blockchain/chain_id", this.handleChainId) - router.GET("/blockchain/genesis_hash", this.handleGenesisHash) - router.GET("/blockchain/latest_block_height", this.handleLatestBlockHeight) - router.GET("/blockchain/latest_block", this.handleLatestBlock) - router.GET("/blockchain/blocks", parseSearchQuery, this.handleBlocks) - router.GET("/blockchain/block/:height", heightParam, this.handleBlock) - // Consensus - router.GET("/consensus", this.handleConsensusState) - router.GET("/consensus/validators", this.handleValidatorList) - // Events - router.POST("/event_subs", this.handleEventSubscribe) - router.GET("/event_subs/:id", this.handleEventPoll) - router.DELETE("/event_subs/:id", this.handleEventUnsubscribe) - // NameReg - router.GET("/namereg", parseSearchQuery, this.handleNameRegEntries) - router.GET("/namereg/:key", nameParam, this.handleNameRegEntry) - // Network - router.GET("/network", this.handleNetworkInfo) - router.GET("/network/client_version", this.handleClientVersion) - router.GET("/network/moniker", this.handleMoniker) - router.GET("/network/listening", this.handleListening) - router.GET("/network/listeners", this.handleListeners) - router.GET("/network/peers", this.handlePeers) - router.GET("/network/peers/:address", peerAddressParam, this.handlePeer) - // Tx related (TODO get txs has still not been implemented) - router.POST("/txpool", this.handleBroadcastTx) - router.GET("/txpool", this.handleUnconfirmedTxs) - // Code execution - router.POST("/calls", this.handleCall) - router.POST("/codecalls", this.handleCallCode) - // Unsafe - router.GET("/unsafe/pa_generator", this.handleGenPrivAcc) - router.POST("/unsafe/txpool", parseTxModifier, this.handleTransact) - router.POST("/unsafe/namereg/txpool", this.handleTransactNameReg) - router.POST("/unsafe/tx_signer", this.handleSignTx) - this.running = true -} - -// Is the server currently running? -func (this *RestServer) Running() bool { - return this.running -} - -// Shut the server down. Does nothing. -func (this *RestServer) ShutDown() { - this.running = false -} - -// ********************************* Accounts ********************************* - -func (this *RestServer) handleGenPrivAcc(c *gin.Context) { - addr := &AddressParam{} - - var acc interface{} - var err error - if addr.Address == nil || len(addr.Address) == 0 { - acc, err = this.pipe.Accounts().GenPrivAccount() - } else { - acc, err = this.pipe.Accounts().GenPrivAccountFromKey(addr.Address) - } - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(acc, c.Writer) -} - -func (this *RestServer) handleAccounts(c *gin.Context) { - var filters []*ep.FilterData - fs, exists := c.Get("filters") - if exists { - filters = fs.([]*ep.FilterData) - } - accs, err := this.pipe.Accounts().Accounts(filters) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(accs, c.Writer) -} - -func (this *RestServer) handleAccount(c *gin.Context) { - addr := c.MustGet("addrBts").([]byte) - acc, err := this.pipe.Accounts().Account(addr) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(acc, c.Writer) -} - -func (this *RestServer) handleStorage(c *gin.Context) { - addr := c.MustGet("addrBts").([]byte) - s, err := this.pipe.Accounts().Storage(addr) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(s, c.Writer) -} - -func (this *RestServer) handleStorageAt(c *gin.Context) { - addr := c.MustGet("addrBts").([]byte) - key := c.MustGet("keyBts").([]byte) - sa, err := this.pipe.Accounts().StorageAt(addr, key) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(sa, c.Writer) -} - -// ********************************* Blockchain ********************************* - -func (this *RestServer) handleBlockchainInfo(c *gin.Context) { - bci, err := this.pipe.Blockchain().Info() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(bci, c.Writer) -} - -func (this *RestServer) handleGenesisHash(c *gin.Context) { - gh, err := this.pipe.Blockchain().GenesisHash() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(&ep.GenesisHash{gh}, c.Writer) -} - -func (this *RestServer) handleChainId(c *gin.Context) { - cId, err := this.pipe.Blockchain().ChainId() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(&ep.ChainId{cId}, c.Writer) -} - -func (this *RestServer) handleLatestBlockHeight(c *gin.Context) { - lbh, err := this.pipe.Blockchain().LatestBlockHeight() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(&ep.LatestBlockHeight{lbh}, c.Writer) -} - -func (this *RestServer) handleLatestBlock(c *gin.Context) { - lb, err := this.pipe.Blockchain().LatestBlock() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(lb, c.Writer) -} - -func (this *RestServer) handleBlocks(c *gin.Context) { - var filters []*ep.FilterData - fs, exists := c.Get("filters") - if exists { - filters = fs.([]*ep.FilterData) - } - - blocks, err := this.pipe.Blockchain().Blocks(filters) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(blocks, c.Writer) -} - -func (this *RestServer) handleBlock(c *gin.Context) { - height := c.MustGet("height").(int) - block, err := this.pipe.Blockchain().Block(height) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(block, c.Writer) -} - -// ********************************* Consensus ********************************* -func (this *RestServer) handleConsensusState(c *gin.Context) { - - cs, err := this.pipe.Consensus().State() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(cs, c.Writer) -} - -func (this *RestServer) handleValidatorList(c *gin.Context) { - vl, err := this.pipe.Consensus().Validators() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(vl, c.Writer) -} - -// ********************************* Events ********************************* - -func (this *RestServer) handleEventSubscribe(c *gin.Context) { - param := &EventIdParam{} - errD := this.codec.Decode(param, c.Request.Body) - if errD != nil { - c.AbortWithError(500, errD) - } - subId, err := this.eventSubs.add(param.EventId) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(&ep.EventSub{subId}, c.Writer) -} - -func (this *RestServer) handleEventPoll(c *gin.Context) { - subId := c.MustGet("id").(string) - data, err := this.eventSubs.poll(subId) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(&ep.PollResponse{data}, c.Writer) -} - -func (this *RestServer) handleEventUnsubscribe(c *gin.Context) { - subId := c.MustGet("id").(string) - err := this.eventSubs.remove(subId) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(&ep.EventUnsub{true}, c.Writer) -} - -// ********************************* NameReg ********************************* - -func (this *RestServer) handleNameRegEntries(c *gin.Context) { - var filters []*ep.FilterData - fs, exists := c.Get("filters") - if exists { - filters = fs.([]*ep.FilterData) - } - entries, err := this.pipe.NameReg().Entries(filters) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(entries, c.Writer) -} - -func (this *RestServer) handleNameRegEntry(c *gin.Context) { - name := c.MustGet("name").(string) - entry, err := this.pipe.NameReg().Entry(name) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(entry, c.Writer) -} - -// ********************************* Network ********************************* - -func (this *RestServer) handleNetworkInfo(c *gin.Context) { - nInfo, err := this.pipe.Net().Info() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(nInfo, c.Writer) -} - -func (this *RestServer) handleClientVersion(c *gin.Context) { - version, err := this.pipe.Net().ClientVersion() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(&ep.ClientVersion{version}, c.Writer) -} - -func (this *RestServer) handleMoniker(c *gin.Context) { - moniker, err := this.pipe.Net().Moniker() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(&ep.Moniker{moniker}, c.Writer) -} - -func (this *RestServer) handleListening(c *gin.Context) { - listening, err := this.pipe.Net().Listening() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(&ep.Listening{listening}, c.Writer) -} - -func (this *RestServer) handleListeners(c *gin.Context) { - listeners, err := this.pipe.Net().Listeners() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(&ep.Listeners{listeners}, c.Writer) -} - -func (this *RestServer) handlePeers(c *gin.Context) { - peers, err := this.pipe.Net().Peers() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(peers, c.Writer) -} - -func (this *RestServer) handlePeer(c *gin.Context) { - address := c.MustGet("address").(string) - peer, err := this.pipe.Net().Peer(address) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(peer, c.Writer) -} - -// ********************************* Transactions ********************************* - -func (this *RestServer) handleBroadcastTx(c *gin.Context) { - param := &txs.CallTx{} - errD := this.codec.Decode(param, c.Request.Body) - if errD != nil { - c.AbortWithError(500, errD) - } - receipt, err := this.pipe.Transactor().BroadcastTx(param) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(receipt, c.Writer) -} - -func (this *RestServer) handleUnconfirmedTxs(c *gin.Context) { - - txs, err := this.pipe.Transactor().UnconfirmedTxs() - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(txs, c.Writer) -} - -func (this *RestServer) handleCall(c *gin.Context) { - param := &CallParam{} - errD := this.codec.Decode(param, c.Request.Body) - if errD != nil { - c.AbortWithError(500, errD) - } - call, err := this.pipe.Transactor().Call(param.From, param.Address, param.Data) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(call, c.Writer) -} - -func (this *RestServer) handleCallCode(c *gin.Context) { - param := &CallCodeParam{} - errD := this.codec.Decode(param, c.Request.Body) - if errD != nil { - c.AbortWithError(500, errD) - } - call, err := this.pipe.Transactor().CallCode(param.From, param.Code, param.Data) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(call, c.Writer) -} - -func (this *RestServer) handleTransact(c *gin.Context) { - - _, hold := c.Get("hold") - - param := &TransactParam{} - errD := this.codec.Decode(param, c.Request.Body) - if errD != nil { - c.AbortWithError(500, errD) - } - if hold { - res, err := this.pipe.Transactor().TransactAndHold(param.PrivKey, param.Address, param.Data, param.GasLimit, param.Fee) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(res, c.Writer) - } else { - receipt, err := this.pipe.Transactor().Transact(param.PrivKey, param.Address, param.Data, param.GasLimit, param.Fee) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(receipt, c.Writer) - } -} - -func (this *RestServer) handleTransactNameReg(c *gin.Context) { - param := &TransactNameRegParam{} - errD := this.codec.Decode(param, c.Request.Body) - if errD != nil { - c.AbortWithError(500, errD) - } - receipt, err := this.pipe.Transactor().TransactNameReg(param.PrivKey, param.Name, param.Data, param.Amount, param.Fee) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(receipt, c.Writer) -} - -func (this *RestServer) handleSignTx(c *gin.Context) { - param := &SignTxParam{} - errD := this.codec.Decode(param, c.Request.Body) - if errD != nil { - c.AbortWithError(500, errD) - } - tx, err := this.pipe.Transactor().SignTx(param.Tx, param.PrivAccounts) - if err != nil { - c.AbortWithError(500, err) - } - c.Writer.WriteHeader(200) - this.codec.Encode(tx, c.Writer) -} - -// ********************************* Middleware ********************************* - -func addressParam(c *gin.Context) { - addr := c.Param("address") - if !util.IsAddress(addr) { - c.AbortWithError(400, fmt.Errorf("Malformed address param: "+addr)) - } - bts, _ := hex.DecodeString(addr) - c.Set("addrBts", bts) - c.Next() -} - -func nameParam(c *gin.Context) { - name := c.Param("key") - c.Set("name", name) - c.Next() -} - -func keyParam(c *gin.Context) { - key := c.Param("key") - bts, err := hex.DecodeString(key) - if err != nil { - c.AbortWithError(400, err) - } - c.Set("keyBts", bts) - c.Next() -} - -func heightParam(c *gin.Context) { - h, err := strconv.Atoi(c.Param("height")) - if err != nil { - c.AbortWithError(400, err) - } - if h < 0 { - c.AbortWithError(400, fmt.Errorf("Negative number used as height.")) - } - c.Set("height", h) - c.Next() -} - -func subIdParam(c *gin.Context) { - subId := c.Param("id") - if len(subId) != 64 || !util.IsHex(subId) { - c.AbortWithError(400, fmt.Errorf("Malformed event id")) - } - c.Set("id", subId) - c.Next() -} - -// TODO -func peerAddressParam(c *gin.Context) { - subId := c.Param("address") - c.Set("address", subId) - c.Next() -} - -func parseTxModifier(c *gin.Context) { - hold := c.Query("hold") - if hold == "true" { - c.Set("hold", true) - } else if hold != "" { - if hold != "false" { - c.Writer.WriteHeader(400) - c.Writer.Write([]byte("tx hold must be either 'true' or 'false', found: " + hold)) - c.Abort() - } - } -} - -func parseSearchQuery(c *gin.Context) { - q := c.Query("q") - if q != "" { - data, err := _parseSearchQuery(q) - if err != nil { - c.Writer.WriteHeader(400) - c.Writer.Write([]byte(err.Error())) - c.Abort() - // c.AbortWithError(400, err) - return - } - c.Set("filters", data) - } -} - -func _parseSearchQuery(queryString string) ([]*ep.FilterData, error) { - if len(queryString) == 0 { - return nil, nil - } - filters := strings.Split(queryString, " ") - fdArr := []*ep.FilterData{} - for _, f := range filters { - kv := strings.Split(f, ":") - if len(kv) != 2 { - return nil, fmt.Errorf("Malformed query. Missing ':' separator: " + f) - } - if kv[0] == "" { - return nil, fmt.Errorf("Malformed query. Field name missing: " + f) - } - - fd, fd2, errTfd := toFilterData(kv[0], kv[1]) - if errTfd != nil { - return nil, errTfd - } - fdArr = append(fdArr, fd) - if fd2 != nil { - fdArr = append(fdArr, fd2) - } - } - return fdArr, nil -} - -// Parse the query statement and create . Two filter data in case of a range param. -func toFilterData(field, stmt string) (*ep.FilterData, *ep.FilterData, error) { - // In case statement is empty - if stmt == "" { - return &ep.FilterData{field, "==", ""}, nil, nil - } - // Simple routine based on string splitting. TODO add quoted range query. - if stmt[0] == '>' || stmt[0] == '<' || stmt[0] == '=' || stmt[0] == '!' { - // This means a normal operator. If one character then stop, otherwise - // peek at next and check if it's a "=". - - if len(stmt) == 1 { - return &ep.FilterData{field, stmt[0:1], ""}, nil, nil - } else if stmt[1] == '=' { - return &ep.FilterData{field, stmt[:2], stmt[2:]}, nil, nil - } else { - return &ep.FilterData{field, stmt[0:1], stmt[1:]}, nil, nil - } - } else { - // Either we have a range query here or a malformed query. - rng := strings.Split(stmt, "..") - // This is for when there is no op, but the value is not empty. - if len(rng) == 1 { - return &ep.FilterData{field, "==", stmt}, nil, nil - } - // The rest. - if len(rng) != 2 || rng[0] == "" || rng[1] == "" { - return nil, nil, fmt.Errorf("Malformed query statement: " + stmt) - } - var min string - var max string - if rng[0] == "*" { - min = "min" - } else { - min = rng[0] - } - if rng[1] == "*" { - max = "max" - } else { - max = rng[1] - } - return &ep.FilterData{field, ">=", min}, &ep.FilterData{field, "<=", max}, nil - } - return nil, nil, nil -} diff --git a/erisdb/serve.go b/erisdb/serve.go deleted file mode 100644 index f79b73e943e3ea2f6c89256f9102193e6b6c9ab0..0000000000000000000000000000000000000000 --- a/erisdb/serve.go +++ /dev/null @@ -1,251 +0,0 @@ -// The erisdb package contains tendermint-specific services that goes with the -// server. -package erisdb - -import ( - "bytes" - "fmt" - "io/ioutil" - "net" - "net/http" - "path" - "strings" - "sync" - - // tendermint support libs - . "github.com/tendermint/go-common" - cfg "github.com/tendermint/go-config" - dbm "github.com/tendermint/go-db" - "github.com/tendermint/go-events" - rpcserver "github.com/tendermint/go-rpc/server" - "github.com/tendermint/go-wire" - "github.com/tendermint/log15" - - // for inproc tendermint - "github.com/tendermint/go-p2p" - "github.com/tendermint/tendermint/node" - "github.com/tendermint/tendermint/proxy" - "github.com/tendermint/tendermint/types" - tmspcli "github.com/tendermint/tmsp/client" - - // tmsp server - tmsp "github.com/eris-ltd/eris-db/consensus/tmsp" - - edbcfg "github.com/eris-ltd/eris-db/config" - ep "github.com/eris-ltd/eris-db/erisdb/pipe" - rpccore "github.com/eris-ltd/eris-db/rpc/core" - "github.com/eris-ltd/eris-db/server" - sm "github.com/eris-ltd/eris-db/manager/eris-mint/state" - stypes "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" - edbapp "github.com/eris-ltd/eris-db/tmsp" - - tmcfg "github.com/tendermint/tendermint/config/tendermint" // for inproc only! -) - -var log = log15.New("module", "eris/erisdb_server") -var tmConfig cfg.Config - -// This function returns a properly configured ErisDb server process, -// with a tmsp listener for talking to tendermint core. -// To start listening for incoming HTTP requests on the Rest server, call 'Start()' on the process. -// Make sure to register any start event listeners first -func ServeErisDB(workDir string, inProc bool) (*server.ServeProcess, error) { - log.Info("ErisDB Serve initializing.") - errEns := EnsureDir(workDir, 0777) - - if errEns != nil { - return nil, errEns - } - - // there are two types of config we need to load, - // one for the erisdb server and one for tendermint. - // even if consensus isn't in process, the tendermint libs (eg. db) - // expect tendermint/go-config to be setup. - // Regardless, both configs are expected in the same file (root/config.toml) - // Some of this stuff is implicit and maybe a little confusing, - // but cfg mgmt across projects probably often is! - - // Get an erisdb configuration - var edbConf *edbcfg.ErisDBConfig - edbConfPath := path.Join(workDir, "server_config.toml") - if !FileExists(edbConfPath) { - log.Info("No server configuration, using default.") - log.Info("Writing to: " + edbConfPath) - edbConf = edbcfg.DefaultErisDBConfig() - errW := edbcfg.WriteErisDBConfig(edbConfPath, edbConf) - if errW != nil { - panic(errW) - } - } else { - var errRSC error - edbConf, errRSC = edbcfg.ReadErisDBConfig(edbConfPath) - if errRSC != nil { - log.Error("Server config file error.", "error", errRSC.Error()) - } - } - - // Get tendermint configuration - tmConfig = tmcfg.GetConfig(workDir) - - // tmConfig.Set("tm.version", TENDERMINT_VERSION) // ? - - // Load the application state - // The app state used to be managed by tendermint node, - // but is now managed by ErisDB. - // The tendermint core only stores the blockchain (history of txs) - stateDB := dbm.NewDB("app_state", edbConf.DB.Backend, workDir+"/data") - state := sm.LoadState(stateDB) - var genDoc *stypes.GenesisDoc - if state == nil { - genDoc, state = sm.MakeGenesisStateFromFile(stateDB, workDir+"/genesis.json") - state.Save() - buf, n, err := new(bytes.Buffer), new(int), new(error) - wire.WriteJSON(genDoc, buf, n, err) - stateDB.Set(stypes.GenDocKey, buf.Bytes()) - if *err != nil { - Exit(Fmt("Unable to write gendoc to db: %v", err)) - } - } else { - genDocBytes := stateDB.Get(stypes.GenDocKey) - err := new(error) - wire.ReadJSONPtr(&genDoc, genDocBytes, err) - if *err != nil { - Exit(Fmt("Unable to read gendoc from db: %v", err)) - } - } - // add the chainid - - // ***************************** - // erisdb-tmsp app - - // start the event switch for state related events - // (transactions to/from acconts, etc) - evsw := events.NewEventSwitch() - evsw.Start() - - // create the app - app := edbapp.NewErisDBApp(state, evsw) - - // so we know where to find the consensus host (for eg. blockchain/consensus rpcs) - app.SetHostAddress(edbConf.Tendermint.Host) - if inProc { - fmt.Println("Starting tm node in proc") - // will also start the go-rpc server (46657 api) - - startTMNode(tmConfig, app, workDir) - } else { - fmt.Println("Starting tmsp listener") - // Start the tmsp listener for state update commands - go func() { - // TODO config - _, err := tmsp.NewServer(edbConf.TMSP.Listener, app) - if err != nil { - // TODO: play nice - Exit(err.Error()) - } - }() - } - - // ***************************** - // Boot the erisdb restful API servers - - genDocFile := tmConfig.GetString("genesis_file") // XXX - // Load supporting objects. - pipe := ep.NewPipe(state.ChainID, genDocFile, app, evsw) - codec := &TCodec{} - evtSubs := NewEventSubscriptions(pipe.Events()) - // The services. - tmwss := NewErisDbWsService(codec, pipe) - tmjs := NewErisDbJsonService(codec, pipe, evtSubs) - // The servers. - jsonServer := NewJsonRpcServer(tmjs) - restServer := NewRestServer(codec, pipe, evtSubs) - wsServer := server.NewWebSocketServer(edbConf.Server.WebSocket.MaxWebSocketSessions, tmwss) - // Create a server process. - proc := server.NewServeProcess(&edbConf.Server, jsonServer, restServer, wsServer) - - return proc, nil -} - -// start an inproc tendermint node -func startTMNode(config cfg.Config, app *edbapp.ErisDBApp, workDir string) { - // get the genesis - genDocFile := config.GetString("genesis_file") - jsonBlob, err := ioutil.ReadFile(genDocFile) - if err != nil { - Exit(Fmt("Couldn't read GenesisDoc file: %v", err)) - } - genDoc := types.GenesisDocFromJSON(jsonBlob) - if genDoc.ChainID == "" { - PanicSanity(Fmt("Genesis doc %v must include non-empty chain_id", genDocFile)) - } - config.Set("chain_id", genDoc.ChainID) - - // Get PrivValidator - privValidatorFile := config.GetString("priv_validator_file") - privValidator := types.LoadOrGenPrivValidator(privValidatorFile) - nd := node.NewNode(config, privValidator, func(addr string, hash []byte) proxy.AppConn { - // TODO: Check the hash - return tmspcli.NewLocalClient(new(sync.Mutex), app) - }) - - l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), config.GetBool("skip_upnp")) - nd.AddListener(l) - if err := nd.Start(); err != nil { - Exit(Fmt("Failed to start node: %v", err)) - } - - log.Notice("Started node", "nodeInfo", nd.NodeInfo()) - - // If seedNode is provided by config, dial out. - if config.GetString("seeds") != "" { - seeds := strings.Split(config.GetString("seeds"), ",") - nd.DialSeeds(seeds) - } - - // Run the RPC server. - if config.GetString("rpc_laddr") != "" { - _, err := StartRPC(config, nd, app) - if err != nil { - PanicCrisis(err) - } - } -} - -func StartRPC(config cfg.Config, n *node.Node, edbApp *edbapp.ErisDBApp) ([]net.Listener, error) { - rpccore.SetConfig(config) - - rpccore.SetErisDBApp(edbApp) - rpccore.SetBlockStore(n.BlockStore()) - rpccore.SetConsensusState(n.ConsensusState()) - rpccore.SetConsensusReactor(n.ConsensusReactor()) - rpccore.SetMempoolReactor(n.MempoolReactor()) - rpccore.SetSwitch(n.Switch()) - rpccore.SetPrivValidator(n.PrivValidator()) - rpccore.SetGenDoc(LoadGenDoc(config.GetString("genesis_file"))) - - listenAddrs := strings.Split(config.GetString("rpc_laddr"), ",") - - // we may expose the rpc over both a unix and tcp socket - listeners := make([]net.Listener, len(listenAddrs)) - for i, listenAddr := range listenAddrs { - mux := http.NewServeMux() - wm := rpcserver.NewWebsocketManager(rpccore.Routes, n.EventSwitch()) - mux.HandleFunc("/websocket", wm.WebsocketHandler) - rpcserver.RegisterRPCFuncs(mux, rpccore.Routes) - listener, err := rpcserver.StartHTTPServer(listenAddr, mux) - if err != nil { - return nil, err - } - listeners[i] = listener - } - return listeners, nil -} - -func LoadGenDoc(genDocFile string) *stypes.GenesisDoc { - jsonBlob, err := ioutil.ReadFile(genDocFile) - if err != nil { - Exit(Fmt("Couldn't read GenesisDoc file: %v", err)) - } - return stypes.GenesisDocFromJSON(jsonBlob) -} diff --git a/erisdb/wsService.go b/erisdb/wsService.go deleted file mode 100644 index 35e76989b6074a8e91a4e3ae18b330703fd52ebc..0000000000000000000000000000000000000000 --- a/erisdb/wsService.go +++ /dev/null @@ -1,134 +0,0 @@ -package erisdb - -import ( - "encoding/json" - "fmt" - ep "github.com/eris-ltd/eris-db/erisdb/pipe" - rpc "github.com/eris-ltd/eris-db/rpc" - "github.com/eris-ltd/eris-db/server" - - "github.com/tendermint/go-events" -) - -// Used for ErisDb. Implements WebSocketService. -type ErisDbWsService struct { - codec rpc.Codec - pipe ep.Pipe - defaultHandlers map[string]RequestHandlerFunc -} - -// Create a new websocket service. -func NewErisDbWsService(codec rpc.Codec, pipe ep.Pipe) server.WebSocketService { - tmwss := &ErisDbWsService{codec: codec, pipe: pipe} - mtds := &ErisDbMethods{codec, pipe} - - dhMap := mtds.getMethods() - // Events - dhMap[EVENT_SUBSCRIBE] = tmwss.EventSubscribe - dhMap[EVENT_UNSUBSCRIBE] = tmwss.EventUnsubscribe - tmwss.defaultHandlers = dhMap - return tmwss -} - -// Process a request. -func (this *ErisDbWsService) Process(msg []byte, session *server.WSSession) { - log.Debug("REQUEST: %s\n", string(msg)) - // Create new request object and unmarshal. - req := &rpc.RPCRequest{} - errU := json.Unmarshal(msg, req) - - // Error when unmarshaling. - if errU != nil { - this.writeError("Failed to parse request: "+errU.Error()+" . Raw: "+string(msg), "", rpc.PARSE_ERROR, session) - return - } - - // Wrong protocol version. - if req.JSONRPC != "2.0" { - this.writeError("Wrong protocol version: "+req.JSONRPC, req.Id, rpc.INVALID_REQUEST, session) - return - } - - mName := req.Method - - if handler, ok := this.defaultHandlers[mName]; ok { - resp, errCode, err := handler(req, session) - if err != nil { - this.writeError(err.Error(), req.Id, errCode, session) - } else { - this.writeResponse(req.Id, resp, session) - } - } else { - this.writeError("Method not found: "+mName, req.Id, rpc.METHOD_NOT_FOUND, session) - } -} - -// Convenience method for writing error responses. -func (this *ErisDbWsService) writeError(msg, id string, code int, session *server.WSSession) { - response := rpc.NewRPCErrorResponse(id, code, msg) - bts, err := this.codec.EncodeBytes(response) - // If there's an error here all bets are off. - if err != nil { - panic("Failed to marshal standard error response." + err.Error()) - } - session.Write(bts) -} - -// Convenience method for writing responses. -func (this *ErisDbWsService) writeResponse(id string, result interface{}, session *server.WSSession) error { - response := rpc.NewRPCResponse(id, result) - bts, err := this.codec.EncodeBytes(response) - log.Debug("RESPONSE: %v\n", response) - if err != nil { - this.writeError("Internal error: "+err.Error(), id, rpc.INTERNAL_ERROR, session) - return err - } - return session.Write(bts) -} - -// *************************************** Events ************************************ - -func (this *ErisDbWsService) EventSubscribe(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - session, ok := requester.(*server.WSSession) - if !ok { - return 0, rpc.INTERNAL_ERROR, fmt.Errorf("Passing wrong object to websocket events") - } - param := &EventIdParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - eventId := param.EventId - subId, errSID := generateSubId() - if errSID != nil { - return nil, rpc.INTERNAL_ERROR, errSID - } - - callback := func(ret events.EventData) { - this.writeResponse(subId, ret, session) - } - _, errC := this.pipe.Events().Subscribe(subId, eventId, callback) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.EventSub{subId}, 0, nil -} - -func (this *ErisDbWsService) EventUnsubscribe(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &EventIdParam{} - err := this.codec.DecodeBytes(param, request.Params) - if err != nil { - return nil, rpc.INVALID_PARAMS, err - } - eventId := param.EventId - - result, errC := this.pipe.Events().Unsubscribe(eventId) - if errC != nil { - return nil, rpc.INTERNAL_ERROR, errC - } - return &ep.EventUnsub{result}, 0, nil -} - -func (this *ErisDbWsService) EventPoll(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - return nil, rpc.INTERNAL_ERROR, fmt.Errorf("Cannot poll with websockets") -}