Skip to content

Commit

Permalink
cmd/orb-verify: implement package as plugin-ready library
Browse files Browse the repository at this point in the history
core: implement orb-verify as optional golang plugin lib

fix: orb-verify binary should not exist in project root
  • Loading branch information
whilei committed Nov 20, 2018
1 parent b5b3a2e commit a60a105
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 103 deletions.
95 changes: 95 additions & 0 deletions cmd/orb-verify/lib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// This is a plugin-ready library.
package main

import (
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
)

var errInvalidArgument = errors.New("invalid argument")
var errInvalidHeader = errors.New("invalid header")

type rec string

var Validator rec

func (r rec) Validate(in string) error {
args := strings.Split(in, " ")
if len(args) != 3 {
return (errInvalidArgument)
}

headerJSON, parentJSON, isUncleStr := args[0], args[1], args[2]

// Initial sanity checks that args are actually present and legit.
_, err := strconv.ParseBool(isUncleStr)
if err != nil {
return (err)
}
if !strings.Contains(headerJSON, "Hash") {
return fmt.Errorf("%v: %s", errInvalidArgument, headerJSON)
}
if !strings.Contains(parentJSON, "Hash") {
return fmt.Errorf("%v: %s", errInvalidArgument, parentJSON)
}

var header, parent *struct {
Number uint64
Hash string
}

if err := json.Unmarshal([]byte(headerJSON), &header); err != nil {
return (err)
}
if err := json.Unmarshal([]byte(parentJSON), &parent); err != nil {
return (err)
}

// TODO Implement your own header validations.

if header.Number != parent.Number+1 {
return fmt.Errorf("%v: %d %d", errInvalidHeader, header.Number, parent.Number)
}

// // Here's an example of making an upstream RPC request (to a TRUSTED source),
// // then using that data to provide supplemental information for block validation.
// remoteRPCAPI, err := url.Parse("http://localhost:8545")
// if err != nil {
// return (err)
// }

// req := &rpc.JSONRequest{
// // Id
// Version: "2.0",
// Method: "eth_blockNumber",
// // Payload
// }

// b, err := json.Marshal(req)
// if err != nil {
// return (err)
// }

// res, err := http.Post(remoteRPCAPI.String(), "application/json", bytes.NewBuffer(b))
// if err != nil {
// return (err)
// }

// var r *rpc.JSONResponse
// err = json.NewDecoder(res.Body).Decode(&r)
// if err != nil {
// return (err)
// }

// if r.Error != nil {
// return (r.Error)
// }

// // TODO Do some logic with response
// // ...

return nil
}
88 changes: 1 addition & 87 deletions cmd/orb-verify/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,15 @@ package main

import (
"bufio"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"strconv"
"strings"
)

var errInvalidArgument = errors.New("invalid argument")
var errInvalidHeader = errors.New("invalid header")

func main() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
if err := validate(scanner.Text()); err != nil {
if err := rec("").Validate(scanner.Text()); err != nil {
log.Fatal(err)
}
}
Expand All @@ -26,82 +19,3 @@ func main() {
}
os.Exit(0)
}

func validate(in string) error {

args := strings.Split(in, " ")
if len(args) != 3 {
return (errInvalidArgument)
}

headerJSON, parentJSON, isUncleStr := args[0], args[1], args[2]

// Initial sanity checks that args are actually present and legit.
_, err := strconv.ParseBool(isUncleStr)
if err != nil {
return (err)
}
if !strings.Contains(headerJSON, "Hash") {
return fmt.Errorf("%v: %s", errInvalidArgument, headerJSON)
}
if !strings.Contains(parentJSON, "Hash") {
return fmt.Errorf("%v: %s", errInvalidArgument, parentJSON)
}

var header, parent *struct {
Number uint64
Hash string
}

if err := json.Unmarshal([]byte(headerJSON), &header); err != nil {
return (err)
}
if err := json.Unmarshal([]byte(parentJSON), &parent); err != nil {
return (err)
}

// TODO Implement your own header validations.

if header.Number != parent.Number+1 {
return fmt.Errorf("%v: %d %d", errInvalidHeader, header.Number, parent.Number)
}

// // Here's an example of making an upstream RPC request (to a TRUSTED source),
// // then using that data to provide supplemental information for block validation.
// remoteRPCAPI, err := url.Parse("http://localhost:8545")
// if err != nil {
// return (err)
// }

// req := &rpc.JSONRequest{
// // Id
// Version: "2.0",
// Method: "eth_blockNumber",
// // Payload
// }

// b, err := json.Marshal(req)
// if err != nil {
// return (err)
// }

// res, err := http.Post(remoteRPCAPI.String(), "application/json", bytes.NewBuffer(b))
// if err != nil {
// return (err)
// }

// var r *rpc.JSONResponse
// err = json.NewDecoder(res.Body).Decode(&r)
// if err != nil {
// return (err)
// }

// if r.Error != nil {
// return (r.Error)
// }

// // TODO Do some logic with response
// // ...

return nil
}
94 changes: 81 additions & 13 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"math/big"
"os"
xec "os/exec"
"path/filepath"
"plugin"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -61,8 +63,9 @@ func (e unableHeaderValidatorProc) Error() error {
}

var (
errInvalidHeaderValidatorProc = errors.New("invalid header validator proc")
errUnableHeaderValidatorProc = errors.New("unable to validate header")
errInvalidHeaderValidatorProc = errors.New("invalid header validator proc")
errInvalidHeaderValidatorPlugin = errors.New("invalid header validator plugin")
errUnableHeaderValidatorProc = errors.New("unable to validate header")
)

// Difficulty allows passing configurable options to a given difficulty algorithm.
Expand Down Expand Up @@ -246,23 +249,21 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
return err
}

func runHeaderValidatorProc(header *types.Header, parent *types.Header, isUncle bool, proc []string) error {
func getHeaderValidatorPParams(header *types.Header, parent *types.Header, isUncle bool) (string, error) {
hb, e := json.Marshal(header)
// hb, e := json.MarshalIndent(header, "", " ")
if e != nil {
return unableHeaderValidatorProc{
err: errUnableHeaderValidatorProc,
msg: e.Error(),
}.Error()
return "", e
}
pb, e := json.Marshal(parent)
// pb, e := json.MarshalIndent(parent, "", " ")
if e != nil {
return unableHeaderValidatorProc{
err: errUnableHeaderValidatorProc,
msg: e.Error(),
}.Error()
return "", e
}
return fmt.Sprintf("%s %s %v", hb, pb, isUncle), nil
}

func runHeaderValidatorProc(header *types.Header, parent *types.Header, isUncle bool, proc []string, params string) error {
args := []string{}
if len(proc) > 1 {
args = append(args, proc[1:]...)
Expand Down Expand Up @@ -296,23 +297,90 @@ func runHeaderValidatorProc(header *types.Header, parent *types.Header, isUncle
// 1: current header JSON string
// 2: parent header JSON string
// 3: ["true"|"false"] whether or not current header is an uncle
io.WriteString(stdin, fmt.Sprintf("%s %s %v", hb, pb, isUncle))
io.WriteString(stdin, params)
}()
wg.Wait()
err := cmd.Wait()
return err
}

type HeaderValidatorPluginI interface {
Validate(string) error
}

func runHeaderValidatorPlugin(config *ChainConfig, header, parent *types.Header, isUncle bool, params string) error {
if config.HeaderValidatorPluginI == nil {
var pluginPath string
switch config.HeaderValidatorPlugin {
case "orb-verify":
// pluginPath = "./plugin/orb-verify.so"
pluginPath = filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereumproject", "go-ethereum", "plugin", "orb-verify.so")
default:
return unableHeaderValidatorProc{
err: errInvalidHeaderValidatorPlugin,
msg: "invalid plugin specified",
}.Error()
}
var p *plugin.Plugin
p, err := plugin.Open(pluginPath)
if err != nil {
return unableHeaderValidatorProc{
err: errInvalidHeaderValidatorPlugin,
msg: err.Error(),
}.Error()
}
symValidate, err := p.Lookup("Validator")
if err != nil {
return unableHeaderValidatorProc{
err: errInvalidHeaderValidatorPlugin,
msg: err.Error(),
}.Error()
}
var v HeaderValidatorPluginI
v, ok := symValidate.(HeaderValidatorPluginI)
if !ok {
return unableHeaderValidatorProc{
err: errInvalidHeaderValidatorPlugin,
msg: "unable to convert interface",
}.Error()
}
config.HeaderValidatorPluginI = v
}
err := config.HeaderValidatorPluginI.Validate(params)
return err
}

// ValidateHeader validates a header. Returns an error if the header is invalid.
//
// See YP section 4.3.4. "Block Header Validity"
func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {

if config.HeaderValidatorProc != nil {
err := runHeaderValidatorProc(header, parent, uncle, config.HeaderValidatorProc)
params, err := getHeaderValidatorPParams(header, parent, uncle)
if err != nil {
return unableHeaderValidatorProc{
err: errInvalidHeaderValidatorPlugin,
msg: err.Error(),
}.Error()
}
err = runHeaderValidatorProc(header, parent, uncle, config.HeaderValidatorProc, params)
// NOTE we could make this logic configurable as well
// eg. headerValidatorProcOnly: [true|false], then if config.headerValidatorProcOnly { return err } <-- returns nil and nonnil vals, instead of allowing the header to pass rest of normal ethereum validations
if config.HeaderValidatorProcOnly || err != nil {
return err
}
}

if config.HeaderValidatorPlugin != "" {
params, err := getHeaderValidatorPParams(header, parent, uncle)
if err != nil {
return unableHeaderValidatorProc{
err: errInvalidHeaderValidatorPlugin,
msg: err.Error(),
}.Error()
}
err = runHeaderValidatorPlugin(config, header, parent, uncle, params)
if config.HeaderValidatorPluginOnly || err != nil {
return err
}
}
Expand Down
Loading

0 comments on commit a60a105

Please sign in to comment.