forked from TrueCloudLab/neoneo-go
commit
565c1940e3
30 changed files with 417 additions and 322 deletions
|
@ -15,8 +15,9 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/network/metrics"
|
"github.com/CityOfZion/neo-go/pkg/network/metrics"
|
||||||
"github.com/CityOfZion/neo-go/pkg/rpc"
|
"github.com/CityOfZion/neo-go/pkg/rpc"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewCommands returns 'node' command.
|
// NewCommands returns 'node' command.
|
||||||
|
@ -119,32 +120,40 @@ func getConfigFromContext(ctx *cli.Context) (config.Config, error) {
|
||||||
// handleLoggingParams reads logging parameters.
|
// handleLoggingParams reads logging parameters.
|
||||||
// If user selected debug level -- function enables it.
|
// If user selected debug level -- function enables it.
|
||||||
// If logPath is configured -- function creates dir and file for logging.
|
// If logPath is configured -- function creates dir and file for logging.
|
||||||
func handleLoggingParams(ctx *cli.Context, cfg config.ApplicationConfiguration) error {
|
func handleLoggingParams(ctx *cli.Context, cfg config.ApplicationConfiguration) (*zap.Logger, error) {
|
||||||
|
level := zapcore.InfoLevel
|
||||||
if ctx.Bool("debug") {
|
if ctx.Bool("debug") {
|
||||||
log.SetLevel(log.DebugLevel)
|
level = zapcore.DebugLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cc := zap.NewProductionConfig()
|
||||||
|
cc.DisableCaller = true
|
||||||
|
cc.DisableStacktrace = true
|
||||||
|
cc.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder
|
||||||
|
cc.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
|
||||||
|
cc.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||||
|
cc.Encoding = "console"
|
||||||
|
cc.Level = zap.NewAtomicLevelAt(level)
|
||||||
|
|
||||||
if logPath := cfg.LogPath; logPath != "" {
|
if logPath := cfg.LogPath; logPath != "" {
|
||||||
if err := io.MakeDirForFile(logPath, "logger"); err != nil {
|
if err := io.MakeDirForFile(logPath, "logger"); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
f, err := os.Create(logPath)
|
|
||||||
if err != nil {
|
cc.OutputPaths = []string{logPath}
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
log.SetOutput(f)
|
|
||||||
}
|
return cc.Build()
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initBCWithMetrics(cfg config.Config) (*core.Blockchain, *metrics.Service, *metrics.Service, error) {
|
func initBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, *metrics.Service, *metrics.Service, error) {
|
||||||
chain, err := initBlockChain(cfg)
|
chain, err := initBlockChain(cfg, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cli.NewExitError(err, 1)
|
return nil, nil, nil, cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
configureAddresses(cfg.ApplicationConfiguration)
|
configureAddresses(cfg.ApplicationConfiguration)
|
||||||
prometheus := metrics.NewPrometheusService(cfg.ApplicationConfiguration.Prometheus)
|
prometheus := metrics.NewPrometheusService(cfg.ApplicationConfiguration.Prometheus, log)
|
||||||
pprof := metrics.NewPprofService(cfg.ApplicationConfiguration.Pprof)
|
pprof := metrics.NewPprofService(cfg.ApplicationConfiguration.Pprof, log)
|
||||||
|
|
||||||
go chain.Run()
|
go chain.Run()
|
||||||
go prometheus.Start()
|
go prometheus.Start()
|
||||||
|
@ -158,7 +167,8 @@ func dumpDB(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
if err := handleLoggingParams(ctx, cfg.ApplicationConfiguration); err != nil {
|
log, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
|
||||||
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
count := uint32(ctx.Uint("count"))
|
count := uint32(ctx.Uint("count"))
|
||||||
|
@ -174,7 +184,7 @@ func dumpDB(ctx *cli.Context) error {
|
||||||
defer outStream.Close()
|
defer outStream.Close()
|
||||||
writer := io.NewBinWriterFromIO(outStream)
|
writer := io.NewBinWriterFromIO(outStream)
|
||||||
|
|
||||||
chain, prometheus, pprof, err := initBCWithMetrics(cfg)
|
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -213,7 +223,8 @@ func restoreDB(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := handleLoggingParams(ctx, cfg.ApplicationConfiguration); err != nil {
|
log, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
|
||||||
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
count := uint32(ctx.Uint("count"))
|
count := uint32(ctx.Uint("count"))
|
||||||
|
@ -229,7 +240,7 @@ func restoreDB(ctx *cli.Context) error {
|
||||||
defer inStream.Close()
|
defer inStream.Close()
|
||||||
reader := io.NewBinReaderFromIO(inStream)
|
reader := io.NewBinReaderFromIO(inStream)
|
||||||
|
|
||||||
chain, prometheus, pprof, err := initBCWithMetrics(cfg)
|
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -265,7 +276,7 @@ func restoreDB(ctx *cli.Context) error {
|
||||||
if block.Index == 0 && i == 0 && skip == 0 {
|
if block.Index == 0 && i == 0 && skip == 0 {
|
||||||
genesis, err := chain.GetBlock(block.Hash())
|
genesis, err := chain.GetBlock(block.Hash())
|
||||||
if err == nil && genesis.Index == 0 {
|
if err == nil && genesis.Index == 0 {
|
||||||
log.Info("skipped genesis block ", block.Hash().StringLE())
|
log.Info("skipped genesis block", zap.String("hash", block.Hash().StringLE()))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,7 +304,8 @@ func startServer(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := handleLoggingParams(ctx, cfg.ApplicationConfiguration); err != nil {
|
log, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,13 +314,13 @@ func startServer(ctx *cli.Context) error {
|
||||||
|
|
||||||
serverConfig := network.NewServerConfig(cfg)
|
serverConfig := network.NewServerConfig(cfg)
|
||||||
|
|
||||||
chain, prometheus, pprof, err := initBCWithMetrics(cfg)
|
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
server := network.NewServer(serverConfig, chain)
|
server := network.NewServer(serverConfig, chain, log)
|
||||||
rpcServer := rpc.NewServer(chain, cfg.ApplicationConfiguration.RPC, server)
|
rpcServer := rpc.NewServer(chain, cfg.ApplicationConfiguration.RPC, server, log)
|
||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
|
|
||||||
go server.Start(errChan)
|
go server.Start(errChan)
|
||||||
|
@ -364,13 +376,13 @@ func configureAddresses(cfg config.ApplicationConfiguration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// initBlockChain initializes BlockChain with preselected DB.
|
// initBlockChain initializes BlockChain with preselected DB.
|
||||||
func initBlockChain(cfg config.Config) (*core.Blockchain, error) {
|
func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, error) {
|
||||||
store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration)
|
store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, cli.NewExitError(fmt.Errorf("could not initialize storage: %s", err), 1)
|
return nil, cli.NewExitError(fmt.Errorf("could not initialize storage: %s", err), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
chain, err := core.NewBlockchain(store, cfg.ProtocolConfiguration)
|
chain, err := core.NewBlockchain(store, cfg.ProtocolConfiguration, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %s", err), 1)
|
return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %s", err), 1)
|
||||||
}
|
}
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -11,7 +11,6 @@ require (
|
||||||
github.com/nspcc-dev/rfc6979 v0.1.0
|
github.com/nspcc-dev/rfc6979 v0.1.0
|
||||||
github.com/pkg/errors v0.8.1
|
github.com/pkg/errors v0.8.1
|
||||||
github.com/prometheus/client_golang v1.2.1
|
github.com/prometheus/client_golang v1.2.1
|
||||||
github.com/sirupsen/logrus v1.4.2
|
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73
|
github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73
|
||||||
github.com/urfave/cli v1.20.0
|
github.com/urfave/cli v1.20.0
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/network"
|
"github.com/CityOfZion/neo-go/pkg/network"
|
||||||
"github.com/CityOfZion/neo-go/pkg/rpc"
|
"github.com/CityOfZion/neo-go/pkg/rpc"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.uber.org/zap/zaptest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Benchmark test to measure number of processed TX.
|
// Benchmark test to measure number of processed TX.
|
||||||
|
@ -23,14 +24,15 @@ func BenchmarkTXPerformanceTest(t *testing.B) {
|
||||||
cfg, err := config.Load(configPath, net)
|
cfg, err := config.Load(configPath, net)
|
||||||
require.NoError(t, err, "could not load config")
|
require.NoError(t, err, "could not load config")
|
||||||
|
|
||||||
|
logger := zaptest.NewLogger(t)
|
||||||
memoryStore := storage.NewMemoryStore()
|
memoryStore := storage.NewMemoryStore()
|
||||||
chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration)
|
chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration, logger)
|
||||||
require.NoError(t, err, "could not create chain")
|
require.NoError(t, err, "could not create chain")
|
||||||
|
|
||||||
go chain.Run()
|
go chain.Run()
|
||||||
|
|
||||||
serverConfig := network.NewServerConfig(cfg)
|
serverConfig := network.NewServerConfig(cfg)
|
||||||
server := network.NewServer(serverConfig, chain)
|
server := network.NewServer(serverConfig, chain, logger)
|
||||||
data := prepareData(t)
|
data := prepareData(t)
|
||||||
t.ResetTimer()
|
t.ResetTimer()
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ type Service interface {
|
||||||
type service struct {
|
type service struct {
|
||||||
Config
|
Config
|
||||||
|
|
||||||
log *zap.SugaredLogger
|
log *zap.Logger
|
||||||
// cache is a fifo cache which stores recent payloads.
|
// cache is a fifo cache which stores recent payloads.
|
||||||
cache *relayCache
|
cache *relayCache
|
||||||
// txx is a fifo cache which stores miner transactions.
|
// txx is a fifo cache which stores miner transactions.
|
||||||
|
@ -59,6 +59,8 @@ type service struct {
|
||||||
|
|
||||||
// Config is a configuration for consensus services.
|
// Config is a configuration for consensus services.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
// Logger is a logger instance.
|
||||||
|
Logger *zap.Logger
|
||||||
// Broadcast is a callback which is called to notify server
|
// Broadcast is a callback which is called to notify server
|
||||||
// about new consensus payload to sent.
|
// about new consensus payload to sent.
|
||||||
Broadcast func(p *Payload)
|
Broadcast func(p *Payload)
|
||||||
|
@ -78,19 +80,18 @@ type Config struct {
|
||||||
|
|
||||||
// NewService returns new consensus.Service instance.
|
// NewService returns new consensus.Service instance.
|
||||||
func NewService(cfg Config) (Service, error) {
|
func NewService(cfg Config) (Service, error) {
|
||||||
log, err := getLogger()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.TimePerBlock <= 0 {
|
if cfg.TimePerBlock <= 0 {
|
||||||
cfg.TimePerBlock = defaultTimePerBlock
|
cfg.TimePerBlock = defaultTimePerBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.Logger == nil {
|
||||||
|
return nil, errors.New("empty logger")
|
||||||
|
}
|
||||||
|
|
||||||
srv := &service{
|
srv := &service{
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
|
|
||||||
log: log.Sugar(),
|
log: cfg.Logger,
|
||||||
cache: newFIFOCache(cacheMaxCapacity),
|
cache: newFIFOCache(cacheMaxCapacity),
|
||||||
txx: newFIFOCache(cacheMaxCapacity),
|
txx: newFIFOCache(cacheMaxCapacity),
|
||||||
messages: make(chan Payload, 100),
|
messages: make(chan Payload, 100),
|
||||||
|
@ -105,7 +106,7 @@ func NewService(cfg Config) (Service, error) {
|
||||||
priv, pub := getKeyPair(cfg.Wallet)
|
priv, pub := getKeyPair(cfg.Wallet)
|
||||||
|
|
||||||
srv.dbft = dbft.New(
|
srv.dbft = dbft.New(
|
||||||
dbft.WithLogger(srv.log.Desugar()),
|
dbft.WithLogger(srv.log),
|
||||||
dbft.WithSecondsPerBlock(cfg.TimePerBlock),
|
dbft.WithSecondsPerBlock(cfg.TimePerBlock),
|
||||||
dbft.WithKeyPair(priv, pub),
|
dbft.WithKeyPair(priv, pub),
|
||||||
dbft.WithTxPerBlock(10000),
|
dbft.WithTxPerBlock(10000),
|
||||||
|
@ -152,10 +153,12 @@ func (s *service) eventLoop() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case hv := <-s.dbft.Timer.C():
|
case hv := <-s.dbft.Timer.C():
|
||||||
s.log.Debugf("timer fired (%d,%d)", hv.Height, hv.View)
|
s.log.Debug("timer fired",
|
||||||
|
zap.Uint32("height", hv.Height),
|
||||||
|
zap.Uint("view", uint(hv.View)))
|
||||||
s.dbft.OnTimeout(hv)
|
s.dbft.OnTimeout(hv)
|
||||||
case msg := <-s.messages:
|
case msg := <-s.messages:
|
||||||
s.log.Debugf("received message from %d", msg.validatorIndex)
|
s.log.Debug("received message", zap.Uint16("from", msg.validatorIndex))
|
||||||
s.dbft.OnReceive(&msg)
|
s.dbft.OnReceive(&msg)
|
||||||
case tx := <-s.transactions:
|
case tx := <-s.transactions:
|
||||||
s.dbft.OnTransaction(tx)
|
s.dbft.OnTransaction(tx)
|
||||||
|
@ -234,7 +237,7 @@ func (s *service) broadcast(p payload.ConsensusPayload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.(*Payload).Sign(s.dbft.Priv.(*privateKey)); err != nil {
|
if err := p.(*Payload).Sign(s.dbft.Priv.(*privateKey)); err != nil {
|
||||||
s.log.Warnf("can't sign consensus payload: %v", err)
|
s.log.Warn("can't sign consensus payload", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
s.cache.Add(p)
|
s.cache.Add(p)
|
||||||
|
@ -273,7 +276,7 @@ func (s *service) processBlock(b block.Block) {
|
||||||
bb.Script = *(s.getBlockWitness(bb))
|
bb.Script = *(s.getBlockWitness(bb))
|
||||||
|
|
||||||
if err := s.Chain.AddBlock(bb); err != nil {
|
if err := s.Chain.AddBlock(bb); err != nil {
|
||||||
s.log.Warnf("error on add block: %v", err)
|
s.log.Warn("error on add block", zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
s.Config.RelayBlock(bb)
|
s.Config.RelayBlock(bb)
|
||||||
}
|
}
|
||||||
|
@ -293,7 +296,7 @@ func (s *service) getBlockWitness(b *core.Block) *transaction.Witness {
|
||||||
m := s.dbft.Context.M()
|
m := s.dbft.Context.M()
|
||||||
verif, err := smartcontract.CreateMultiSigRedeemScript(m, pubs)
|
verif, err := smartcontract.CreateMultiSigRedeemScript(m, pubs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("can't create multisig redeem script: %v", err)
|
s.log.Warn("can't create multisig redeem script", zap.Error(err))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/dbft/block"
|
"github.com/nspcc-dev/dbft/block"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.uber.org/zap/zaptest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewService(t *testing.T) {
|
func TestNewService(t *testing.T) {
|
||||||
|
@ -128,6 +129,7 @@ func shouldNotReceive(t *testing.T, ch chan Payload) {
|
||||||
|
|
||||||
func newTestService(t *testing.T) *service {
|
func newTestService(t *testing.T) *service {
|
||||||
srv, err := NewService(Config{
|
srv, err := NewService(Config{
|
||||||
|
Logger: zaptest.NewLogger(t),
|
||||||
Broadcast: func(*Payload) {},
|
Broadcast: func(*Payload) {},
|
||||||
Chain: newTestChain(t),
|
Chain: newTestChain(t),
|
||||||
RequestTx: func(...util.Uint256) {},
|
RequestTx: func(...util.Uint256) {},
|
||||||
|
@ -177,7 +179,7 @@ func newTestChain(t *testing.T) *core.Blockchain {
|
||||||
unitTestNetCfg, err := config.Load("../../config", config.ModeUnitTestNet)
|
unitTestNetCfg, err := config.Load("../../config", config.ModeUnitTestNet)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
chain, err := core.NewBlockchain(storage.NewMemoryStore(), unitTestNetCfg.ProtocolConfiguration)
|
chain, err := core.NewBlockchain(storage.NewMemoryStore(), unitTestNetCfg.ProtocolConfiguration, zaptest.NewLogger(t))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
go chain.Run()
|
go chain.Run()
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
package consensus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getLogger() (*zap.Logger, error) {
|
|
||||||
cc := zap.NewDevelopmentConfig()
|
|
||||||
cc.DisableCaller = true
|
|
||||||
cc.DisableStacktrace = true
|
|
||||||
cc.Encoding = "console"
|
|
||||||
|
|
||||||
log, err := cc.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return log.With(zap.String("module", "dbft")), nil
|
|
||||||
}
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tuning parameters.
|
// Tuning parameters.
|
||||||
|
@ -80,13 +80,19 @@ type Blockchain struct {
|
||||||
|
|
||||||
// cache for block verification keys.
|
// cache for block verification keys.
|
||||||
keyCache map[util.Uint160]map[string]*keys.PublicKey
|
keyCache map[util.Uint160]map[string]*keys.PublicKey
|
||||||
|
|
||||||
|
log *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type headersOpFunc func(headerList *HeaderHashList)
|
type headersOpFunc func(headerList *HeaderHashList)
|
||||||
|
|
||||||
// NewBlockchain returns a new blockchain object the will use the
|
// NewBlockchain returns a new blockchain object the will use the
|
||||||
// given Store as its underlying storage.
|
// given Store as its underlying storage.
|
||||||
func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration) (*Blockchain, error) {
|
func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.Logger) (*Blockchain, error) {
|
||||||
|
if log == nil {
|
||||||
|
return nil, errors.New("empty logger")
|
||||||
|
}
|
||||||
|
|
||||||
bc := &Blockchain{
|
bc := &Blockchain{
|
||||||
config: cfg,
|
config: cfg,
|
||||||
dao: newDao(s),
|
dao: newDao(s),
|
||||||
|
@ -96,6 +102,7 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration) (*Blockcha
|
||||||
runToExitCh: make(chan struct{}),
|
runToExitCh: make(chan struct{}),
|
||||||
memPool: NewMemPool(50000),
|
memPool: NewMemPool(50000),
|
||||||
keyCache: make(map[util.Uint160]map[string]*keys.PublicKey),
|
keyCache: make(map[util.Uint160]map[string]*keys.PublicKey),
|
||||||
|
log: log,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bc.init(); err != nil {
|
if err := bc.init(); err != nil {
|
||||||
|
@ -109,7 +116,7 @@ func (bc *Blockchain) init() error {
|
||||||
// If we could not find the version in the Store, we know that there is nothing stored.
|
// If we could not find the version in the Store, we know that there is nothing stored.
|
||||||
ver, err := bc.dao.GetVersion()
|
ver, err := bc.dao.GetVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("no storage version found! creating genesis block")
|
bc.log.Info("no storage version found! creating genesis block")
|
||||||
if err = bc.dao.PutVersion(version); err != nil {
|
if err = bc.dao.PutVersion(version); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -131,7 +138,7 @@ func (bc *Blockchain) init() error {
|
||||||
// At this point there was no version found in the storage which
|
// At this point there was no version found in the storage which
|
||||||
// implies a creating fresh storage with the version specified
|
// implies a creating fresh storage with the version specified
|
||||||
// and the genesis block as first block.
|
// and the genesis block as first block.
|
||||||
log.Infof("restoring blockchain with version: %s", version)
|
bc.log.Info("restoring blockchain", zap.String("version", version))
|
||||||
|
|
||||||
bHeight, err := bc.dao.GetCurrentBlockHeight()
|
bHeight, err := bc.dao.GetCurrentBlockHeight()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -200,10 +207,10 @@ func (bc *Blockchain) Run() {
|
||||||
defer func() {
|
defer func() {
|
||||||
persistTimer.Stop()
|
persistTimer.Stop()
|
||||||
if err := bc.persist(); err != nil {
|
if err := bc.persist(); err != nil {
|
||||||
log.Warnf("failed to persist: %s", err)
|
bc.log.Warn("failed to persist", zap.Error(err))
|
||||||
}
|
}
|
||||||
if err := bc.dao.store.Close(); err != nil {
|
if err := bc.dao.store.Close(); err != nil {
|
||||||
log.Warnf("failed to close db: %s", err)
|
bc.log.Warn("failed to close db", zap.Error(err))
|
||||||
}
|
}
|
||||||
close(bc.runToExitCh)
|
close(bc.runToExitCh)
|
||||||
}()
|
}()
|
||||||
|
@ -218,7 +225,7 @@ func (bc *Blockchain) Run() {
|
||||||
go func() {
|
go func() {
|
||||||
err := bc.persist()
|
err := bc.persist()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("failed to persist blockchain: %s", err)
|
bc.log.Warn("failed to persist blockchain", zap.Error(err))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
persistTimer.Reset(persistInterval)
|
persistTimer.Reset(persistInterval)
|
||||||
|
@ -302,11 +309,10 @@ func (bc *Blockchain) AddHeaders(headers ...*Header) (err error) {
|
||||||
if err = bc.dao.store.PutBatch(batch); err != nil {
|
if err = bc.dao.store.PutBatch(batch); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.WithFields(log.Fields{
|
bc.log.Debug("done processing headers",
|
||||||
"headerIndex": headerList.Len() - 1,
|
zap.Int("headerIndex", headerList.Len()-1),
|
||||||
"blockHeight": bc.BlockHeight(),
|
zap.Uint32("blockHeight", bc.BlockHeight()),
|
||||||
"took": time.Since(start),
|
zap.Duration("took", time.Since(start)))
|
||||||
}).Debug("done processing headers")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<-bc.headersOpDone
|
<-bc.headersOpDone
|
||||||
|
@ -502,7 +508,7 @@ func (bc *Blockchain) storeBlock(block *Block) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case *transaction.InvocationTX:
|
case *transaction.InvocationTX:
|
||||||
systemInterop := newInteropContext(trigger.Application, bc, cache.store, block, tx)
|
systemInterop := bc.newInteropContext(trigger.Application, cache.store, block, tx)
|
||||||
v := bc.spawnVMWithInterops(systemInterop)
|
v := bc.spawnVMWithInterops(systemInterop)
|
||||||
v.SetCheckedHash(tx.VerificationHash().BytesBE())
|
v.SetCheckedHash(tx.VerificationHash().BytesBE())
|
||||||
v.LoadScript(t.Script)
|
v.LoadScript(t.Script)
|
||||||
|
@ -537,11 +543,10 @@ func (bc *Blockchain) storeBlock(block *Block) error {
|
||||||
_, _, _, _ = op, from, to, amount
|
_, _, _, _ = op, from, to, amount
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.WithFields(log.Fields{
|
bc.log.Warn("contract invocation failed",
|
||||||
"tx": tx.Hash().StringLE(),
|
zap.String("tx", tx.Hash().StringLE()),
|
||||||
"block": block.Index,
|
zap.Uint32("block", block.Index),
|
||||||
"err": err,
|
zap.Error(err))
|
||||||
}).Warn("contract invocation failed")
|
|
||||||
}
|
}
|
||||||
aer := &state.AppExecResult{
|
aer := &state.AppExecResult{
|
||||||
TxHash: tx.Hash(),
|
TxHash: tx.Hash(),
|
||||||
|
@ -710,13 +715,12 @@ func (bc *Blockchain) persist() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.WithFields(log.Fields{
|
bc.log.Info("blockchain persist completed",
|
||||||
"persistedBlocks": diff,
|
zap.Uint32("persistedBlocks", diff),
|
||||||
"persistedKeys": persisted,
|
zap.Int("persistedKeys", persisted),
|
||||||
"headerHeight": storedHeaderHeight,
|
zap.Uint32("headerHeight", storedHeaderHeight),
|
||||||
"blockHeight": bHeight,
|
zap.Uint32("blockHeight", bHeight),
|
||||||
"took": time.Since(start),
|
zap.Duration("took", time.Since(start)))
|
||||||
}).Info("blockchain persist completed")
|
|
||||||
|
|
||||||
// update monitoring metrics.
|
// update monitoring metrics.
|
||||||
updatePersistedHeightMetric(bHeight)
|
updatePersistedHeightMetric(bHeight)
|
||||||
|
@ -849,7 +853,9 @@ func (bc *Blockchain) HeaderHeight() uint32 {
|
||||||
func (bc *Blockchain) GetAssetState(assetID util.Uint256) *state.Asset {
|
func (bc *Blockchain) GetAssetState(assetID util.Uint256) *state.Asset {
|
||||||
asset, err := bc.dao.GetAssetState(assetID)
|
asset, err := bc.dao.GetAssetState(assetID)
|
||||||
if asset == nil && err != storage.ErrKeyNotFound {
|
if asset == nil && err != storage.ErrKeyNotFound {
|
||||||
log.Warnf("failed to get asset state %s : %s", assetID, err)
|
bc.log.Warn("failed to get asset state",
|
||||||
|
zap.Stringer("asset", assetID),
|
||||||
|
zap.Error(err))
|
||||||
}
|
}
|
||||||
return asset
|
return asset
|
||||||
}
|
}
|
||||||
|
@ -858,7 +864,7 @@ func (bc *Blockchain) GetAssetState(assetID util.Uint256) *state.Asset {
|
||||||
func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract {
|
func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract {
|
||||||
contract, err := bc.dao.GetContractState(hash)
|
contract, err := bc.dao.GetContractState(hash)
|
||||||
if contract == nil && err != storage.ErrKeyNotFound {
|
if contract == nil && err != storage.ErrKeyNotFound {
|
||||||
log.Warnf("failed to get contract state: %s", err)
|
bc.log.Warn("failed to get contract state", zap.Error(err))
|
||||||
}
|
}
|
||||||
return contract
|
return contract
|
||||||
}
|
}
|
||||||
|
@ -867,7 +873,7 @@ func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract {
|
||||||
func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *state.Account {
|
func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *state.Account {
|
||||||
as, err := bc.dao.GetAccountState(scriptHash)
|
as, err := bc.dao.GetAccountState(scriptHash)
|
||||||
if as == nil && err != storage.ErrKeyNotFound {
|
if as == nil && err != storage.ErrKeyNotFound {
|
||||||
log.Warnf("failed to get account state: %s", err)
|
bc.log.Warn("failed to get account state", zap.Error(err))
|
||||||
}
|
}
|
||||||
return as
|
return as
|
||||||
}
|
}
|
||||||
|
@ -876,7 +882,7 @@ func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *state.Account {
|
||||||
func (bc *Blockchain) GetUnspentCoinState(hash util.Uint256) *UnspentCoinState {
|
func (bc *Blockchain) GetUnspentCoinState(hash util.Uint256) *UnspentCoinState {
|
||||||
ucs, err := bc.dao.GetUnspentCoinState(hash)
|
ucs, err := bc.dao.GetUnspentCoinState(hash)
|
||||||
if ucs == nil && err != storage.ErrKeyNotFound {
|
if ucs == nil && err != storage.ErrKeyNotFound {
|
||||||
log.Warnf("failed to get unspent coin state: %s", err)
|
bc.log.Warn("failed to get unspent coin state", zap.Error(err))
|
||||||
}
|
}
|
||||||
return ucs
|
return ucs
|
||||||
}
|
}
|
||||||
|
@ -1367,7 +1373,7 @@ func (bc *Blockchain) spawnVMWithInterops(interopCtx *interopContext) *vm.VM {
|
||||||
// GetTestVM returns a VM and a Store setup for a test run of some sort of code.
|
// GetTestVM returns a VM and a Store setup for a test run of some sort of code.
|
||||||
func (bc *Blockchain) GetTestVM() (*vm.VM, storage.Store) {
|
func (bc *Blockchain) GetTestVM() (*vm.VM, storage.Store) {
|
||||||
tmpStore := storage.NewMemCachedStore(bc.dao.store)
|
tmpStore := storage.NewMemCachedStore(bc.dao.store)
|
||||||
systemInterop := newInteropContext(trigger.Application, bc, tmpStore, nil, nil)
|
systemInterop := bc.newInteropContext(trigger.Application, tmpStore, nil, nil)
|
||||||
vm := bc.spawnVMWithInterops(systemInterop)
|
vm := bc.spawnVMWithInterops(systemInterop)
|
||||||
return vm, tmpStore
|
return vm, tmpStore
|
||||||
}
|
}
|
||||||
|
@ -1446,7 +1452,7 @@ func (bc *Blockchain) verifyTxWitnesses(t *transaction.Transaction, block *Block
|
||||||
}
|
}
|
||||||
sort.Slice(hashes, func(i, j int) bool { return hashes[i].Less(hashes[j]) })
|
sort.Slice(hashes, func(i, j int) bool { return hashes[i].Less(hashes[j]) })
|
||||||
sort.Slice(witnesses, func(i, j int) bool { return witnesses[i].ScriptHash().Less(witnesses[j].ScriptHash()) })
|
sort.Slice(witnesses, func(i, j int) bool { return witnesses[i].ScriptHash().Less(witnesses[j].ScriptHash()) })
|
||||||
interopCtx := newInteropContext(trigger.Verification, bc, bc.dao.store, block, t)
|
interopCtx := bc.newInteropContext(trigger.Verification, bc.dao.store, block, t)
|
||||||
for i := 0; i < len(hashes); i++ {
|
for i := 0; i < len(hashes); i++ {
|
||||||
err := bc.verifyHashAgainstScript(hashes[i], &witnesses[i], t.VerificationHash(), interopCtx, false)
|
err := bc.verifyHashAgainstScript(hashes[i], &witnesses[i], t.VerificationHash(), interopCtx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1466,7 +1472,7 @@ func (bc *Blockchain) verifyBlockWitnesses(block *Block, prevHeader *Header) err
|
||||||
} else {
|
} else {
|
||||||
hash = prevHeader.NextConsensus
|
hash = prevHeader.NextConsensus
|
||||||
}
|
}
|
||||||
interopCtx := newInteropContext(trigger.Verification, bc, bc.dao.store, nil, nil)
|
interopCtx := bc.newInteropContext(trigger.Verification, bc.dao.store, nil, nil)
|
||||||
return bc.verifyHashAgainstScript(hash, &block.Script, block.VerificationHash(), interopCtx, true)
|
return bc.verifyHashAgainstScript(hash, &block.Script, block.VerificationHash(), interopCtx, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1480,3 +1486,7 @@ func hashAndIndexToBytes(h util.Uint256, index uint32) []byte {
|
||||||
func (bc *Blockchain) secondsPerBlock() int {
|
func (bc *Blockchain) secondsPerBlock() int {
|
||||||
return bc.config.SecondsPerBlock
|
return bc.config.SecondsPerBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bc *Blockchain) newInteropContext(trigger byte, s storage.Store, block *Block, tx *transaction.Transaction) *interopContext {
|
||||||
|
return newInteropContext(trigger, bc, s, block, tx, bc.log)
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.uber.org/zap/zaptest"
|
||||||
)
|
)
|
||||||
|
|
||||||
var newBlockPrevHash util.Uint256
|
var newBlockPrevHash util.Uint256
|
||||||
|
@ -37,7 +38,7 @@ func newTestChain(t *testing.T) *Blockchain {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
chain, err := NewBlockchain(storage.NewMemoryStore(), unitTestNetCfg.ProtocolConfiguration)
|
chain, err := NewBlockchain(storage.NewMemoryStore(), unitTestNetCfg.ProtocolConfiguration, zaptest.NewLogger(t))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,8 @@ import (
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func TestStorageFind(t *testing.T) {
|
func TestStorageFind(t *testing.T) {
|
||||||
v, contractState, context := createVMAndContractState(t)
|
v, contractState, context, chain := createVMAndContractState(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
skeys := [][]byte{{0x01, 0x02}, {0x02, 0x01}}
|
skeys := [][]byte{{0x01, 0x02}, {0x02, 0x01}}
|
||||||
items := []*state.StorageItem{
|
items := []*state.StorageItem{
|
||||||
|
@ -101,7 +102,8 @@ func TestStorageFind(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeaderGetVersion(t *testing.T) {
|
func TestHeaderGetVersion(t *testing.T) {
|
||||||
v, block, context := createVMAndPushBlock(t)
|
v, block, context, chain := createVMAndPushBlock(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
err := context.headerGetVersion(v)
|
err := context.headerGetVersion(v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -112,7 +114,9 @@ func TestHeaderGetVersion(t *testing.T) {
|
||||||
func TestHeaderGetVersion_Negative(t *testing.T) {
|
func TestHeaderGetVersion_Negative(t *testing.T) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
block := newDumbBlock()
|
block := newDumbBlock()
|
||||||
context := newInteropContext(trigger.Application, newTestChain(t), storage.NewMemoryStore(), block, nil)
|
chain := newTestChain(t)
|
||||||
|
defer chain.Close()
|
||||||
|
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), block, nil)
|
||||||
v.Estack().PushVal(vm.NewBoolItem(false))
|
v.Estack().PushVal(vm.NewBoolItem(false))
|
||||||
|
|
||||||
err := context.headerGetVersion(v)
|
err := context.headerGetVersion(v)
|
||||||
|
@ -120,7 +124,8 @@ func TestHeaderGetVersion_Negative(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeaderGetConsensusData(t *testing.T) {
|
func TestHeaderGetConsensusData(t *testing.T) {
|
||||||
v, block, context := createVMAndPushBlock(t)
|
v, block, context, chain := createVMAndPushBlock(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
err := context.headerGetConsensusData(v)
|
err := context.headerGetConsensusData(v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -129,7 +134,8 @@ func TestHeaderGetConsensusData(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeaderGetMerkleRoot(t *testing.T) {
|
func TestHeaderGetMerkleRoot(t *testing.T) {
|
||||||
v, block, context := createVMAndPushBlock(t)
|
v, block, context, chain := createVMAndPushBlock(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
err := context.headerGetMerkleRoot(v)
|
err := context.headerGetMerkleRoot(v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -138,7 +144,8 @@ func TestHeaderGetMerkleRoot(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeaderGetNextConsensus(t *testing.T) {
|
func TestHeaderGetNextConsensus(t *testing.T) {
|
||||||
v, block, context := createVMAndPushBlock(t)
|
v, block, context, chain := createVMAndPushBlock(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
err := context.headerGetNextConsensus(v)
|
err := context.headerGetNextConsensus(v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -147,7 +154,8 @@ func TestHeaderGetNextConsensus(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxGetAttributes(t *testing.T) {
|
func TestTxGetAttributes(t *testing.T) {
|
||||||
v, tx, context := createVMAndPushTX(t)
|
v, tx, context, chain := createVMAndPushTX(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
err := context.txGetAttributes(v)
|
err := context.txGetAttributes(v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -156,7 +164,8 @@ func TestTxGetAttributes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxGetInputs(t *testing.T) {
|
func TestTxGetInputs(t *testing.T) {
|
||||||
v, tx, context := createVMAndPushTX(t)
|
v, tx, context, chain := createVMAndPushTX(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
err := context.txGetInputs(v)
|
err := context.txGetInputs(v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -165,7 +174,8 @@ func TestTxGetInputs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxGetOutputs(t *testing.T) {
|
func TestTxGetOutputs(t *testing.T) {
|
||||||
v, tx, context := createVMAndPushTX(t)
|
v, tx, context, chain := createVMAndPushTX(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
err := context.txGetOutputs(v)
|
err := context.txGetOutputs(v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -174,7 +184,8 @@ func TestTxGetOutputs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxGetType(t *testing.T) {
|
func TestTxGetType(t *testing.T) {
|
||||||
v, tx, context := createVMAndPushTX(t)
|
v, tx, context, chain := createVMAndPushTX(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
err := context.txGetType(v)
|
err := context.txGetType(v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -183,7 +194,8 @@ func TestTxGetType(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvocationTxGetScript(t *testing.T) {
|
func TestInvocationTxGetScript(t *testing.T) {
|
||||||
v, tx, context := createVMAndPushTX(t)
|
v, tx, context, chain := createVMAndPushTX(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
err := context.invocationTxGetScript(v)
|
err := context.invocationTxGetScript(v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -197,7 +209,10 @@ func TestWitnessGetVerificationScript(t *testing.T) {
|
||||||
script := []byte{byte(opcode.PUSHM1), byte(opcode.RET)}
|
script := []byte{byte(opcode.PUSHM1), byte(opcode.RET)}
|
||||||
witness := transaction.Witness{InvocationScript: nil, VerificationScript: script}
|
witness := transaction.Witness{InvocationScript: nil, VerificationScript: script}
|
||||||
|
|
||||||
context := newInteropContext(trigger.Application, newTestChain(t), storage.NewMemoryStore(), nil, nil)
|
chain := newTestChain(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
|
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), nil, nil)
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&witness))
|
v.Estack().PushVal(vm.NewInteropItem(&witness))
|
||||||
err := context.witnessGetVerificationScript(v)
|
err := context.witnessGetVerificationScript(v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -206,7 +221,8 @@ func TestWitnessGetVerificationScript(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPopInputFromVM(t *testing.T) {
|
func TestPopInputFromVM(t *testing.T) {
|
||||||
v, tx, _ := createVMAndTX(t)
|
v, tx, _, chain := createVMAndTX(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
||||||
|
|
||||||
input, err := popInputFromVM(v)
|
input, err := popInputFromVM(v)
|
||||||
|
@ -215,7 +231,8 @@ func TestPopInputFromVM(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInputGetHash(t *testing.T) {
|
func TestInputGetHash(t *testing.T) {
|
||||||
v, tx, context := createVMAndTX(t)
|
v, tx, context, chain := createVMAndTX(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
||||||
|
|
||||||
err := context.inputGetHash(v)
|
err := context.inputGetHash(v)
|
||||||
|
@ -225,7 +242,8 @@ func TestInputGetHash(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInputGetIndex(t *testing.T) {
|
func TestInputGetIndex(t *testing.T) {
|
||||||
v, tx, context := createVMAndTX(t)
|
v, tx, context, chain := createVMAndTX(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
||||||
|
|
||||||
err := context.inputGetIndex(v)
|
err := context.inputGetIndex(v)
|
||||||
|
@ -235,7 +253,8 @@ func TestInputGetIndex(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPopOutputFromVM(t *testing.T) {
|
func TestPopOutputFromVM(t *testing.T) {
|
||||||
v, tx, _ := createVMAndTX(t)
|
v, tx, _, chain := createVMAndTX(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||||
|
|
||||||
output, err := popOutputFromVM(v)
|
output, err := popOutputFromVM(v)
|
||||||
|
@ -244,7 +263,8 @@ func TestPopOutputFromVM(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOutputGetAssetID(t *testing.T) {
|
func TestOutputGetAssetID(t *testing.T) {
|
||||||
v, tx, context := createVMAndTX(t)
|
v, tx, context, chain := createVMAndTX(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||||
|
|
||||||
err := context.outputGetAssetID(v)
|
err := context.outputGetAssetID(v)
|
||||||
|
@ -254,7 +274,8 @@ func TestOutputGetAssetID(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOutputGetScriptHash(t *testing.T) {
|
func TestOutputGetScriptHash(t *testing.T) {
|
||||||
v, tx, context := createVMAndTX(t)
|
v, tx, context, chain := createVMAndTX(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||||
|
|
||||||
err := context.outputGetScriptHash(v)
|
err := context.outputGetScriptHash(v)
|
||||||
|
@ -264,7 +285,8 @@ func TestOutputGetScriptHash(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOutputGetValue(t *testing.T) {
|
func TestOutputGetValue(t *testing.T) {
|
||||||
v, tx, context := createVMAndTX(t)
|
v, tx, context, chain := createVMAndTX(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||||
|
|
||||||
err := context.outputGetValue(v)
|
err := context.outputGetValue(v)
|
||||||
|
@ -274,7 +296,8 @@ func TestOutputGetValue(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAttrGetData(t *testing.T) {
|
func TestAttrGetData(t *testing.T) {
|
||||||
v, tx, context := createVMAndTX(t)
|
v, tx, context, chain := createVMAndTX(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0]))
|
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0]))
|
||||||
|
|
||||||
err := context.attrGetData(v)
|
err := context.attrGetData(v)
|
||||||
|
@ -284,7 +307,8 @@ func TestAttrGetData(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAttrGetUsage(t *testing.T) {
|
func TestAttrGetUsage(t *testing.T) {
|
||||||
v, tx, context := createVMAndTX(t)
|
v, tx, context, chain := createVMAndTX(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0]))
|
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0]))
|
||||||
|
|
||||||
err := context.attrGetUsage(v)
|
err := context.attrGetUsage(v)
|
||||||
|
@ -294,7 +318,8 @@ func TestAttrGetUsage(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccountGetScriptHash(t *testing.T) {
|
func TestAccountGetScriptHash(t *testing.T) {
|
||||||
v, accState, context := createVMAndAccState(t)
|
v, accState, context, chain := createVMAndAccState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(accState))
|
v.Estack().PushVal(vm.NewInteropItem(accState))
|
||||||
|
|
||||||
err := context.accountGetScriptHash(v)
|
err := context.accountGetScriptHash(v)
|
||||||
|
@ -304,7 +329,8 @@ func TestAccountGetScriptHash(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccountGetVotes(t *testing.T) {
|
func TestAccountGetVotes(t *testing.T) {
|
||||||
v, accState, context := createVMAndAccState(t)
|
v, accState, context, chain := createVMAndAccState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(accState))
|
v.Estack().PushVal(vm.NewInteropItem(accState))
|
||||||
|
|
||||||
err := context.accountGetVotes(v)
|
err := context.accountGetVotes(v)
|
||||||
|
@ -314,7 +340,8 @@ func TestAccountGetVotes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContractGetScript(t *testing.T) {
|
func TestContractGetScript(t *testing.T) {
|
||||||
v, contractState, context := createVMAndContractState(t)
|
v, contractState, context, chain := createVMAndContractState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(contractState))
|
v.Estack().PushVal(vm.NewInteropItem(contractState))
|
||||||
|
|
||||||
err := context.contractGetScript(v)
|
err := context.contractGetScript(v)
|
||||||
|
@ -324,7 +351,8 @@ func TestContractGetScript(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContractIsPayable(t *testing.T) {
|
func TestContractIsPayable(t *testing.T) {
|
||||||
v, contractState, context := createVMAndContractState(t)
|
v, contractState, context, chain := createVMAndContractState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(contractState))
|
v.Estack().PushVal(vm.NewInteropItem(contractState))
|
||||||
|
|
||||||
err := context.contractIsPayable(v)
|
err := context.contractIsPayable(v)
|
||||||
|
@ -334,7 +362,8 @@ func TestContractIsPayable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssetGetAdmin(t *testing.T) {
|
func TestAssetGetAdmin(t *testing.T) {
|
||||||
v, assetState, context := createVMAndAssetState(t)
|
v, assetState, context, chain := createVMAndAssetState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||||
|
|
||||||
err := context.assetGetAdmin(v)
|
err := context.assetGetAdmin(v)
|
||||||
|
@ -344,7 +373,8 @@ func TestAssetGetAdmin(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssetGetAmount(t *testing.T) {
|
func TestAssetGetAmount(t *testing.T) {
|
||||||
v, assetState, context := createVMAndAssetState(t)
|
v, assetState, context, chain := createVMAndAssetState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||||
|
|
||||||
err := context.assetGetAmount(v)
|
err := context.assetGetAmount(v)
|
||||||
|
@ -354,7 +384,8 @@ func TestAssetGetAmount(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssetGetAssetID(t *testing.T) {
|
func TestAssetGetAssetID(t *testing.T) {
|
||||||
v, assetState, context := createVMAndAssetState(t)
|
v, assetState, context, chain := createVMAndAssetState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||||
|
|
||||||
err := context.assetGetAssetID(v)
|
err := context.assetGetAssetID(v)
|
||||||
|
@ -364,7 +395,8 @@ func TestAssetGetAssetID(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssetGetAssetType(t *testing.T) {
|
func TestAssetGetAssetType(t *testing.T) {
|
||||||
v, assetState, context := createVMAndAssetState(t)
|
v, assetState, context, chain := createVMAndAssetState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||||
|
|
||||||
err := context.assetGetAssetType(v)
|
err := context.assetGetAssetType(v)
|
||||||
|
@ -374,7 +406,8 @@ func TestAssetGetAssetType(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssetGetAvailable(t *testing.T) {
|
func TestAssetGetAvailable(t *testing.T) {
|
||||||
v, assetState, context := createVMAndAssetState(t)
|
v, assetState, context, chain := createVMAndAssetState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||||
|
|
||||||
err := context.assetGetAvailable(v)
|
err := context.assetGetAvailable(v)
|
||||||
|
@ -384,7 +417,8 @@ func TestAssetGetAvailable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssetGetIssuer(t *testing.T) {
|
func TestAssetGetIssuer(t *testing.T) {
|
||||||
v, assetState, context := createVMAndAssetState(t)
|
v, assetState, context, chain := createVMAndAssetState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||||
|
|
||||||
err := context.assetGetIssuer(v)
|
err := context.assetGetIssuer(v)
|
||||||
|
@ -394,7 +428,8 @@ func TestAssetGetIssuer(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssetGetOwner(t *testing.T) {
|
func TestAssetGetOwner(t *testing.T) {
|
||||||
v, assetState, context := createVMAndAssetState(t)
|
v, assetState, context, chain := createVMAndAssetState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||||
|
|
||||||
err := context.assetGetOwner(v)
|
err := context.assetGetOwner(v)
|
||||||
|
@ -404,7 +439,8 @@ func TestAssetGetOwner(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssetGetPrecision(t *testing.T) {
|
func TestAssetGetPrecision(t *testing.T) {
|
||||||
v, assetState, context := createVMAndAssetState(t)
|
v, assetState, context, chain := createVMAndAssetState(t)
|
||||||
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||||
|
|
||||||
err := context.assetGetPrecision(v)
|
err := context.assetGetPrecision(v)
|
||||||
|
@ -415,21 +451,22 @@ func TestAssetGetPrecision(t *testing.T) {
|
||||||
|
|
||||||
// Helper functions to create VM, InteropContext, TX, Account, Contract, Asset.
|
// Helper functions to create VM, InteropContext, TX, Account, Contract, Asset.
|
||||||
|
|
||||||
func createVMAndPushBlock(t *testing.T) (*vm.VM, *Block, *interopContext) {
|
func createVMAndPushBlock(t *testing.T) (*vm.VM, *Block, *interopContext, *Blockchain) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
block := newDumbBlock()
|
block := newDumbBlock()
|
||||||
context := newInteropContext(trigger.Application, newTestChain(t), storage.NewMemoryStore(), block, nil)
|
chain := newTestChain(t)
|
||||||
|
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), block, nil)
|
||||||
v.Estack().PushVal(vm.NewInteropItem(block))
|
v.Estack().PushVal(vm.NewInteropItem(block))
|
||||||
return v, block, context
|
return v, block, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interopContext) {
|
func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interopContext, *Blockchain) {
|
||||||
v, tx, context := createVMAndTX(t)
|
v, tx, context, chain := createVMAndTX(t)
|
||||||
v.Estack().PushVal(vm.NewInteropItem(tx))
|
v.Estack().PushVal(vm.NewInteropItem(tx))
|
||||||
return v, tx, context
|
return v, tx, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVMAndAssetState(t *testing.T) (*vm.VM, *state.Asset, *interopContext) {
|
func createVMAndAssetState(t *testing.T) (*vm.VM, *state.Asset, *interopContext, *Blockchain) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
assetState := &state.Asset{
|
assetState := &state.Asset{
|
||||||
ID: util.Uint256{},
|
ID: util.Uint256{},
|
||||||
|
@ -447,11 +484,12 @@ func createVMAndAssetState(t *testing.T) (*vm.VM, *state.Asset, *interopContext)
|
||||||
IsFrozen: false,
|
IsFrozen: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
context := newInteropContext(trigger.Application, newTestChain(t), storage.NewMemoryStore(), nil, nil)
|
chain := newTestChain(t)
|
||||||
return v, assetState, context
|
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), nil, nil)
|
||||||
|
return v, assetState, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interopContext) {
|
func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interopContext, *Blockchain) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
contractState := &state.Contract{
|
contractState := &state.Contract{
|
||||||
Script: []byte("testscript"),
|
Script: []byte("testscript"),
|
||||||
|
@ -465,11 +503,12 @@ func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interopCo
|
||||||
Description: random.String(10),
|
Description: random.String(10),
|
||||||
}
|
}
|
||||||
|
|
||||||
context := newInteropContext(trigger.Application, newTestChain(t), storage.NewMemoryStore(), nil, nil)
|
chain := newTestChain(t)
|
||||||
return v, contractState, context
|
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), nil, nil)
|
||||||
|
return v, contractState, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interopContext) {
|
func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interopContext, *Blockchain) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
rawHash := "4d3b96ae1bcc5a585e075e3b81920210dec16302"
|
rawHash := "4d3b96ae1bcc5a585e075e3b81920210dec16302"
|
||||||
hash, err := util.Uint160DecodeStringBE(rawHash)
|
hash, err := util.Uint160DecodeStringBE(rawHash)
|
||||||
|
@ -479,11 +518,12 @@ func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interopContext)
|
||||||
accountState.Votes = []*keys.PublicKey{key}
|
accountState.Votes = []*keys.PublicKey{key}
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
context := newInteropContext(trigger.Application, newTestChain(t), storage.NewMemoryStore(), nil, nil)
|
chain := newTestChain(t)
|
||||||
return v, accountState, context
|
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), nil, nil)
|
||||||
|
return v, accountState, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interopContext) {
|
func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interopContext, *Blockchain) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
script := []byte{byte(opcode.PUSH1), byte(opcode.RET)}
|
script := []byte{byte(opcode.PUSH1), byte(opcode.RET)}
|
||||||
tx := transaction.NewInvocationTX(script, 0)
|
tx := transaction.NewInvocationTX(script, 0)
|
||||||
|
@ -509,6 +549,7 @@ func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interopCont
|
||||||
tx.Attributes = attributes
|
tx.Attributes = attributes
|
||||||
tx.Inputs = inputs
|
tx.Inputs = inputs
|
||||||
tx.Outputs = outputs
|
tx.Outputs = outputs
|
||||||
context := newInteropContext(trigger.Application, newTestChain(t), storage.NewMemoryStore(), nil, tx)
|
chain := newTestChain(t)
|
||||||
return v, tx, context
|
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), nil, tx)
|
||||||
|
return v, tx, context, chain
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
gherr "github.com/pkg/errors"
|
gherr "github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -350,7 +350,9 @@ func (ic *interopContext) runtimeNotify(v *vm.VM) error {
|
||||||
// runtimeLog logs the message passed.
|
// runtimeLog logs the message passed.
|
||||||
func (ic *interopContext) runtimeLog(v *vm.VM) error {
|
func (ic *interopContext) runtimeLog(v *vm.VM) error {
|
||||||
msg := fmt.Sprintf("%q", v.Estack().Pop().Bytes())
|
msg := fmt.Sprintf("%q", v.Estack().Pop().Bytes())
|
||||||
log.Infof("script %s logs: %s", getContextScriptHash(v, 0), msg)
|
ic.log.Info("runtime log",
|
||||||
|
zap.Stringer("script", getContextScriptHash(v, 0)),
|
||||||
|
zap.String("logs", msg))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/storage"
|
"github.com/CityOfZion/neo-go/pkg/core/storage"
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type interopContext struct {
|
type interopContext struct {
|
||||||
|
@ -23,12 +24,13 @@ type interopContext struct {
|
||||||
tx *transaction.Transaction
|
tx *transaction.Transaction
|
||||||
dao *cachedDao
|
dao *cachedDao
|
||||||
notifications []state.NotificationEvent
|
notifications []state.NotificationEvent
|
||||||
|
log *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func newInteropContext(trigger byte, bc Blockchainer, s storage.Store, block *Block, tx *transaction.Transaction) *interopContext {
|
func newInteropContext(trigger byte, bc Blockchainer, s storage.Store, block *Block, tx *transaction.Transaction, log *zap.Logger) *interopContext {
|
||||||
dao := newCachedDao(s)
|
dao := newCachedDao(s)
|
||||||
nes := make([]state.NotificationEvent, 0)
|
nes := make([]state.NotificationEvent, 0)
|
||||||
return &interopContext{bc, trigger, block, tx, dao, nes}
|
return &interopContext{bc, trigger, block, tx, dao, nes, log}
|
||||||
}
|
}
|
||||||
|
|
||||||
// interopedFunction binds function name, id with the function itself and price,
|
// interopedFunction binds function name, id with the function itself and price,
|
||||||
|
|
|
@ -14,7 +14,9 @@ import (
|
||||||
func testNonInterop(t *testing.T, value interface{}, f func(*interopContext, *vm.VM) error) {
|
func testNonInterop(t *testing.T, value interface{}, f func(*interopContext, *vm.VM) error) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
v.Estack().PushVal(value)
|
v.Estack().PushVal(value)
|
||||||
context := newInteropContext(trigger.Application, newTestChain(t), storage.NewMemoryStore(), nil, nil)
|
chain := newTestChain(t)
|
||||||
|
defer chain.Close()
|
||||||
|
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), nil, nil)
|
||||||
require.Error(t, f(context, v))
|
require.Error(t, f(context, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/io"
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
"github.com/etcd-io/bbolt"
|
"github.com/etcd-io/bbolt"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/util"
|
"github.com/syndtr/goleveldb/leveldb/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -109,7 +108,7 @@ func (s *BoltDBStore) Seek(key []byte, f func(k, v []byte)) {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("error while executing seek in boltDB")
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,23 @@ package network
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/core"
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
"github.com/Workiva/go-datastructures/queue"
|
"github.com/Workiva/go-datastructures/queue"
|
||||||
log "github.com/sirupsen/logrus"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type blockQueue struct {
|
type blockQueue struct {
|
||||||
|
log *zap.Logger
|
||||||
queue *queue.PriorityQueue
|
queue *queue.PriorityQueue
|
||||||
checkBlocks chan struct{}
|
checkBlocks chan struct{}
|
||||||
chain core.Blockchainer
|
chain core.Blockchainer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBlockQueue(capacity int, bc core.Blockchainer) *blockQueue {
|
func newBlockQueue(capacity int, bc core.Blockchainer, log *zap.Logger) *blockQueue {
|
||||||
|
if log == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return &blockQueue{
|
return &blockQueue{
|
||||||
|
log: log,
|
||||||
queue: queue.NewPriorityQueue(capacity, false),
|
queue: queue.NewPriorityQueue(capacity, false),
|
||||||
checkBlocks: make(chan struct{}, 1),
|
checkBlocks: make(chan struct{}, 1),
|
||||||
chain: bc,
|
chain: bc,
|
||||||
|
@ -38,11 +44,10 @@ func (bq *blockQueue) run() {
|
||||||
if minblock.Index == bq.chain.BlockHeight()+1 {
|
if minblock.Index == bq.chain.BlockHeight()+1 {
|
||||||
err := bq.chain.AddBlock(minblock)
|
err := bq.chain.AddBlock(minblock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{
|
bq.log.Warn("blockQueue: failed adding block into the blockchain",
|
||||||
"error": err.Error(),
|
zap.String("error", err.Error()),
|
||||||
"blockHeight": bq.chain.BlockHeight(),
|
zap.Uint32("blockHeight", bq.chain.BlockHeight()),
|
||||||
"nextIndex": minblock.Index,
|
zap.Uint32("nextIndex", minblock.Index))
|
||||||
}).Warn("blockQueue: failed adding block into the blockchain")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/core"
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"go.uber.org/zap/zaptest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBlockQueue(t *testing.T) {
|
func TestBlockQueue(t *testing.T) {
|
||||||
chain := &testChain{}
|
chain := &testChain{}
|
||||||
// notice, it's not yet running
|
// notice, it's not yet running
|
||||||
bq := newBlockQueue(0, chain)
|
bq := newBlockQueue(0, chain, zaptest.NewLogger(t))
|
||||||
blocks := make([]*core.Block, 11)
|
blocks := make([]*core.Block, 11)
|
||||||
for i := 1; i < 11; i++ {
|
for i := 1; i < 11; i++ {
|
||||||
blocks[i] = &core.Block{BlockBase: core.BlockBase{Index: uint32(i)}}
|
blocks[i] = &core.Block{BlockBase: core.BlockBase{Index: uint32(i)}}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
|
"go.uber.org/zap/zaptest"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testChain struct {
|
type testChain struct {
|
||||||
|
@ -203,7 +204,7 @@ func (p *localPeer) Handshaked() bool {
|
||||||
return p.handshaked
|
return p.handshaked
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestServer() *Server {
|
func newTestServer(t *testing.T) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
ServerConfig: ServerConfig{},
|
ServerConfig: ServerConfig{},
|
||||||
chain: &testChain{},
|
chain: &testChain{},
|
||||||
|
@ -214,6 +215,7 @@ func newTestServer() *Server {
|
||||||
register: make(chan Peer),
|
register: make(chan Peer),
|
||||||
unregister: make(chan peerDrop),
|
unregister: make(chan peerDrop),
|
||||||
peers: make(map[Peer]bool),
|
peers: make(map[Peer]bool),
|
||||||
|
log: zaptest.NewLogger(t),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,13 +201,11 @@ func (m *Message) decodePayload(br *io.BinReader) error {
|
||||||
return fmt.Errorf("can't decode command %s", cmdByteArrayToString(m.Command))
|
return fmt.Errorf("can't decode command %s", cmdByteArrayToString(m.Command))
|
||||||
}
|
}
|
||||||
p.DecodeBinary(r)
|
p.DecodeBinary(r)
|
||||||
if r.Err != nil {
|
if r.Err == nil || r.Err == payload.ErrTooManyHeaders {
|
||||||
return r.Err
|
m.Payload = p
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Payload = p
|
return r.Err
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes a Message to any given BinWriter.
|
// Encode encodes a Message to any given BinWriter.
|
||||||
|
|
|
@ -4,13 +4,14 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service serves metrics.
|
// Service serves metrics.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
*http.Server
|
*http.Server
|
||||||
config Config
|
config Config
|
||||||
|
log *zap.Logger
|
||||||
serviceType string
|
serviceType string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,27 +25,21 @@ type Config struct {
|
||||||
// Start runs http service with exposed endpoint on configured port.
|
// Start runs http service with exposed endpoint on configured port.
|
||||||
func (ms *Service) Start() {
|
func (ms *Service) Start() {
|
||||||
if ms.config.Enabled {
|
if ms.config.Enabled {
|
||||||
log.WithFields(log.Fields{
|
ms.log.Info("service is running", zap.String("endpoint", ms.Addr))
|
||||||
"endpoint": ms.Addr,
|
|
||||||
"service": ms.serviceType,
|
|
||||||
}).Info("service running")
|
|
||||||
err := ms.ListenAndServe()
|
err := ms.ListenAndServe()
|
||||||
if err != nil && err != http.ErrServerClosed {
|
if err != nil && err != http.ErrServerClosed {
|
||||||
log.Warnf("%s service couldn't start on configured port", ms.serviceType)
|
ms.log.Warn("service couldn't start on configured port")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Infof("%s service hasn't started since it's disabled", ms.serviceType)
|
ms.log.Info("service hasn't started since it's disabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShutDown stops service.
|
// ShutDown stops service.
|
||||||
func (ms *Service) ShutDown() {
|
func (ms *Service) ShutDown() {
|
||||||
log.WithFields(log.Fields{
|
ms.log.Info("shutting down service", zap.String("endpoint", ms.Addr))
|
||||||
"endpoint": ms.Addr,
|
|
||||||
"service": ms.serviceType,
|
|
||||||
}).Info("shutting down service")
|
|
||||||
err := ms.Shutdown(context.Background())
|
err := ms.Shutdown(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("can't shut down %s service", ms.serviceType)
|
ms.log.Panic("can't shut down service")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,19 @@ package metrics
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/pprof"
|
"net/http/pprof"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PprofService https://golang.org/pkg/net/http/pprof/.
|
// PprofService https://golang.org/pkg/net/http/pprof/.
|
||||||
type PprofService Service
|
type PprofService Service
|
||||||
|
|
||||||
// NewPprofService created new service for gathering pprof metrics.
|
// NewPprofService created new service for gathering pprof metrics.
|
||||||
func NewPprofService(cfg Config) *Service {
|
func NewPprofService(cfg Config, log *zap.Logger) *Service {
|
||||||
|
if log == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
handler := http.NewServeMux()
|
handler := http.NewServeMux()
|
||||||
handler.HandleFunc("/debug/pprof/", pprof.Index)
|
handler.HandleFunc("/debug/pprof/", pprof.Index)
|
||||||
handler.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
handler.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||||
|
@ -18,9 +24,12 @@ func NewPprofService(cfg Config) *Service {
|
||||||
handler.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
handler.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||||
|
|
||||||
return &Service{
|
return &Service{
|
||||||
&http.Server{
|
Server: &http.Server{
|
||||||
Addr: cfg.Address + ":" + cfg.Port,
|
Addr: cfg.Address + ":" + cfg.Port,
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
}, cfg, "Pprof",
|
},
|
||||||
|
config: cfg,
|
||||||
|
serviceType: "Pprof",
|
||||||
|
log: log.With(zap.String("service", "Pprof")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,25 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrometheusService https://prometheus.io/docs/guides/go-application.
|
// PrometheusService https://prometheus.io/docs/guides/go-application.
|
||||||
type PrometheusService Service
|
type PrometheusService Service
|
||||||
|
|
||||||
// NewPrometheusService creates new service for gathering prometheus metrics.
|
// NewPrometheusService creates new service for gathering prometheus metrics.
|
||||||
func NewPrometheusService(cfg Config) *Service {
|
func NewPrometheusService(cfg Config, log *zap.Logger) *Service {
|
||||||
|
if log == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return &Service{
|
return &Service{
|
||||||
&http.Server{
|
Server: &http.Server{
|
||||||
Addr: cfg.Address + ":" + cfg.Port,
|
Addr: cfg.Address + ":" + cfg.Port,
|
||||||
Handler: promhttp.Handler(),
|
Handler: promhttp.Handler(),
|
||||||
}, cfg, "Prometheus",
|
},
|
||||||
|
config: cfg,
|
||||||
|
serviceType: "Prometheus",
|
||||||
|
log: log.With(zap.String("service", "Prometheus")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package payload
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/core"
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
"github.com/CityOfZion/neo-go/pkg/io"
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
log "github.com/sirupsen/logrus"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Headers payload.
|
// Headers payload.
|
||||||
|
@ -16,13 +16,17 @@ const (
|
||||||
MaxHeadersAllowed = 2000
|
MaxHeadersAllowed = 2000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrTooManyHeaders is an error returned when too many headers were received.
|
||||||
|
var ErrTooManyHeaders = errors.Errorf("too many headers were received (max: %d)", MaxHeadersAllowed)
|
||||||
|
|
||||||
// DecodeBinary implements Serializable interface.
|
// DecodeBinary implements Serializable interface.
|
||||||
func (p *Headers) DecodeBinary(br *io.BinReader) {
|
func (p *Headers) DecodeBinary(br *io.BinReader) {
|
||||||
lenHeaders := br.ReadVarUint()
|
lenHeaders := br.ReadVarUint()
|
||||||
|
|
||||||
|
var limitExceeded bool
|
||||||
|
|
||||||
// C# node does it silently
|
// C# node does it silently
|
||||||
if lenHeaders > MaxHeadersAllowed {
|
if limitExceeded = lenHeaders > MaxHeadersAllowed; limitExceeded {
|
||||||
log.Warnf("received %d headers, capping to %d", lenHeaders, MaxHeadersAllowed)
|
|
||||||
lenHeaders = MaxHeadersAllowed
|
lenHeaders = MaxHeadersAllowed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +37,10 @@ func (p *Headers) DecodeBinary(br *io.BinReader) {
|
||||||
header.DecodeBinary(br)
|
header.DecodeBinary(br)
|
||||||
p.Hdrs[i] = header
|
p.Hdrs[i] = header
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if br.Err == nil && limitExceeded {
|
||||||
|
br.Err = ErrTooManyHeaders
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinary implements Serializable interface.
|
// EncodeBinary implements Serializable interface.
|
||||||
|
|
|
@ -11,36 +11,39 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHeadersEncodeDecode(t *testing.T) {
|
func TestHeadersEncodeDecode(t *testing.T) {
|
||||||
headers := &Headers{[]*core.Header{
|
t.Run("normal case", func(t *testing.T) {
|
||||||
{
|
headers := newTestHeaders(3)
|
||||||
BlockBase: core.BlockBase{
|
|
||||||
Version: 0,
|
|
||||||
Index: 1,
|
|
||||||
Script: transaction.Witness{
|
|
||||||
InvocationScript: []byte{0x0},
|
|
||||||
VerificationScript: []byte{0x1},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
{
|
|
||||||
BlockBase: core.BlockBase{
|
|
||||||
Version: 0,
|
|
||||||
Index: 2,
|
|
||||||
Script: transaction.Witness{
|
|
||||||
InvocationScript: []byte{0x0},
|
|
||||||
VerificationScript: []byte{0x1},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
{
|
|
||||||
BlockBase: core.BlockBase{
|
|
||||||
Version: 0,
|
|
||||||
Index: 3,
|
|
||||||
Script: transaction.Witness{
|
|
||||||
InvocationScript: []byte{0x0},
|
|
||||||
VerificationScript: []byte{0x1},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}}
|
|
||||||
|
|
||||||
|
testHeadersEncodeDecode(t, headers, 3, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("more than max", func(t *testing.T) {
|
||||||
|
const sent = MaxHeadersAllowed + 1
|
||||||
|
headers := newTestHeaders(sent)
|
||||||
|
|
||||||
|
testHeadersEncodeDecode(t, headers, MaxHeadersAllowed, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestHeaders(n int) *Headers {
|
||||||
|
headers := &Headers{Hdrs: make([]*core.Header, n)}
|
||||||
|
|
||||||
|
for i := range headers.Hdrs {
|
||||||
|
headers.Hdrs[i] = &core.Header{
|
||||||
|
BlockBase: core.BlockBase{
|
||||||
|
Index: uint32(i + 1),
|
||||||
|
Script: transaction.Witness{
|
||||||
|
InvocationScript: []byte{0x0},
|
||||||
|
VerificationScript: []byte{0x1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHeadersEncodeDecode(t *testing.T, headers *Headers, expected int, limit bool) {
|
||||||
buf := io.NewBufBinWriter()
|
buf := io.NewBufBinWriter()
|
||||||
headers.EncodeBinary(buf.BinWriter)
|
headers.EncodeBinary(buf.BinWriter)
|
||||||
assert.Nil(t, buf.Err)
|
assert.Nil(t, buf.Err)
|
||||||
|
@ -49,9 +52,16 @@ func TestHeadersEncodeDecode(t *testing.T) {
|
||||||
r := io.NewBinReaderFromBuf(b)
|
r := io.NewBinReaderFromBuf(b)
|
||||||
headersDecode := &Headers{}
|
headersDecode := &Headers{}
|
||||||
headersDecode.DecodeBinary(r)
|
headersDecode.DecodeBinary(r)
|
||||||
assert.Nil(t, r.Err)
|
|
||||||
|
|
||||||
for i := 0; i < len(headers.Hdrs); i++ {
|
var err error
|
||||||
|
if limit {
|
||||||
|
err = ErrTooManyHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, err, r.Err)
|
||||||
|
assert.Equal(t, expected, len(headersDecode.Hdrs))
|
||||||
|
|
||||||
|
for i := 0; i < len(headersDecode.Hdrs); i++ {
|
||||||
assert.Equal(t, headers.Hdrs[i].Version, headersDecode.Hdrs[i].Version)
|
assert.Equal(t, headers.Hdrs[i].Version, headersDecode.Hdrs[i].Version)
|
||||||
assert.Equal(t, headers.Hdrs[i].Index, headersDecode.Hdrs[i].Index)
|
assert.Equal(t, headers.Hdrs[i].Index, headersDecode.Hdrs[i].Index)
|
||||||
assert.Equal(t, headers.Hdrs[i].Script, headersDecode.Hdrs[i].Script)
|
assert.Equal(t, headers.Hdrs[i].Script, headersDecode.Hdrs[i].Script)
|
||||||
|
|
|
@ -15,8 +15,8 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"go.uber.org/atomic"
|
"go.uber.org/atomic"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -65,6 +65,8 @@ type (
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
|
|
||||||
connected *atomic.Bool
|
connected *atomic.Bool
|
||||||
|
|
||||||
|
log *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
peerDrop struct {
|
peerDrop struct {
|
||||||
|
@ -80,11 +82,15 @@ func randomID() uint32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer returns a new Server, initialized with the given configuration.
|
// NewServer returns a new Server, initialized with the given configuration.
|
||||||
func NewServer(config ServerConfig, chain core.Blockchainer) *Server {
|
func NewServer(config ServerConfig, chain core.Blockchainer, log *zap.Logger) *Server {
|
||||||
|
if log == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
ServerConfig: config,
|
ServerConfig: config,
|
||||||
chain: chain,
|
chain: chain,
|
||||||
bQueue: newBlockQueue(maxBlockBatch, chain),
|
bQueue: newBlockQueue(maxBlockBatch, chain, log),
|
||||||
id: randomID(),
|
id: randomID(),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
addrReq: make(chan *Message, config.MinPeers),
|
addrReq: make(chan *Message, config.MinPeers),
|
||||||
|
@ -92,9 +98,11 @@ func NewServer(config ServerConfig, chain core.Blockchainer) *Server {
|
||||||
unregister: make(chan peerDrop),
|
unregister: make(chan peerDrop),
|
||||||
peers: make(map[Peer]bool),
|
peers: make(map[Peer]bool),
|
||||||
connected: atomic.NewBool(false),
|
connected: atomic.NewBool(false),
|
||||||
|
log: log,
|
||||||
}
|
}
|
||||||
|
|
||||||
srv, err := consensus.NewService(consensus.Config{
|
srv, err := consensus.NewService(consensus.Config{
|
||||||
|
Logger: log,
|
||||||
Broadcast: s.handleNewPayload,
|
Broadcast: s.handleNewPayload,
|
||||||
RelayBlock: s.relayBlock,
|
RelayBlock: s.relayBlock,
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
|
@ -108,30 +116,27 @@ func NewServer(config ServerConfig, chain core.Blockchainer) *Server {
|
||||||
s.consensus = srv
|
s.consensus = srv
|
||||||
|
|
||||||
if s.MinPeers <= 0 {
|
if s.MinPeers <= 0 {
|
||||||
log.WithFields(log.Fields{
|
s.log.Info("bad MinPeers configured, using the default value",
|
||||||
"MinPeers configured": s.MinPeers,
|
zap.Int("configured", s.MinPeers),
|
||||||
"MinPeers actual": defaultMinPeers,
|
zap.Int("actual", defaultMinPeers))
|
||||||
}).Info("bad MinPeers configured, using the default value")
|
|
||||||
s.MinPeers = defaultMinPeers
|
s.MinPeers = defaultMinPeers
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.MaxPeers <= 0 {
|
if s.MaxPeers <= 0 {
|
||||||
log.WithFields(log.Fields{
|
s.log.Info("bad MaxPeers configured, using the default value",
|
||||||
"MaxPeers configured": s.MaxPeers,
|
zap.Int("configured", s.MaxPeers),
|
||||||
"MaxPeers actual": defaultMaxPeers,
|
zap.Int("actual", defaultMaxPeers))
|
||||||
}).Info("bad MaxPeers configured, using the default value")
|
|
||||||
s.MaxPeers = defaultMaxPeers
|
s.MaxPeers = defaultMaxPeers
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.AttemptConnPeers <= 0 {
|
if s.AttemptConnPeers <= 0 {
|
||||||
log.WithFields(log.Fields{
|
s.log.Info("bad AttemptConnPeers configured, using the default value",
|
||||||
"AttemptConnPeers configured": s.AttemptConnPeers,
|
zap.Int("configured", s.AttemptConnPeers),
|
||||||
"AttemptConnPeers actual": defaultAttemptConnPeers,
|
zap.Int("actual", defaultAttemptConnPeers))
|
||||||
}).Info("bad AttemptConnPeers configured, using the default value")
|
|
||||||
s.AttemptConnPeers = defaultAttemptConnPeers
|
s.AttemptConnPeers = defaultAttemptConnPeers
|
||||||
}
|
}
|
||||||
|
|
||||||
s.transport = NewTCPTransport(s, fmt.Sprintf("%s:%d", config.Address, config.Port))
|
s.transport = NewTCPTransport(s, fmt.Sprintf("%s:%d", config.Address, config.Port), s.log)
|
||||||
s.discovery = NewDefaultDiscovery(
|
s.discovery = NewDefaultDiscovery(
|
||||||
s.DialTimeout,
|
s.DialTimeout,
|
||||||
s.transport,
|
s.transport,
|
||||||
|
@ -147,10 +152,9 @@ func (s *Server) ID() uint32 {
|
||||||
|
|
||||||
// Start will start the server and its underlying transport.
|
// Start will start the server and its underlying transport.
|
||||||
func (s *Server) Start(errChan chan error) {
|
func (s *Server) Start(errChan chan error) {
|
||||||
log.WithFields(log.Fields{
|
s.log.Info("node started",
|
||||||
"blockHeight": s.chain.BlockHeight(),
|
zap.Uint32("blockHeight", s.chain.BlockHeight()),
|
||||||
"headerHeight": s.chain.HeaderHeight(),
|
zap.Uint32("headerHeight", s.chain.HeaderHeight()))
|
||||||
}).Info("node started")
|
|
||||||
|
|
||||||
s.discovery.BackFill(s.Seeds...)
|
s.discovery.BackFill(s.Seeds...)
|
||||||
|
|
||||||
|
@ -162,9 +166,7 @@ func (s *Server) Start(errChan chan error) {
|
||||||
|
|
||||||
// Shutdown disconnects all peers and stops listening.
|
// Shutdown disconnects all peers and stops listening.
|
||||||
func (s *Server) Shutdown() {
|
func (s *Server) Shutdown() {
|
||||||
log.WithFields(log.Fields{
|
s.log.Info("shutting down server", zap.Int("peers", s.PeerCount()))
|
||||||
"peers": s.PeerCount(),
|
|
||||||
}).Info("shutting down server")
|
|
||||||
s.bQueue.discard()
|
s.bQueue.discard()
|
||||||
close(s.quit)
|
close(s.quit)
|
||||||
}
|
}
|
||||||
|
@ -205,9 +207,7 @@ func (s *Server) run() {
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
s.peers[p] = true
|
s.peers[p] = true
|
||||||
s.lock.Unlock()
|
s.lock.Unlock()
|
||||||
log.WithFields(log.Fields{
|
s.log.Info("new peer connected", zap.Stringer("addr", p.RemoteAddr()))
|
||||||
"addr": p.RemoteAddr(),
|
|
||||||
}).Info("new peer connected")
|
|
||||||
peerCount := s.PeerCount()
|
peerCount := s.PeerCount()
|
||||||
if peerCount > s.MaxPeers {
|
if peerCount > s.MaxPeers {
|
||||||
s.lock.RLock()
|
s.lock.RLock()
|
||||||
|
@ -225,11 +225,10 @@ func (s *Server) run() {
|
||||||
if s.peers[drop.peer] {
|
if s.peers[drop.peer] {
|
||||||
delete(s.peers, drop.peer)
|
delete(s.peers, drop.peer)
|
||||||
s.lock.Unlock()
|
s.lock.Unlock()
|
||||||
log.WithFields(log.Fields{
|
s.log.Warn("peer disconnected",
|
||||||
"addr": drop.peer.RemoteAddr(),
|
zap.Stringer("addr", drop.peer.RemoteAddr()),
|
||||||
"reason": drop.reason,
|
zap.String("reason", drop.reason.Error()),
|
||||||
"peerCount": s.PeerCount(),
|
zap.Int("peerCount", s.PeerCount()))
|
||||||
}).Warn("peer disconnected")
|
|
||||||
addr := drop.peer.PeerAddr().String()
|
addr := drop.peer.PeerAddr().String()
|
||||||
if drop.reason == errIdenticalID {
|
if drop.reason == errIdenticalID {
|
||||||
s.discovery.RegisterBadAddr(addr)
|
s.discovery.RegisterBadAddr(addr)
|
||||||
|
@ -254,7 +253,7 @@ func (s *Server) tryStartConsensus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.HandshakedPeersCount() >= s.MinPeers {
|
if s.HandshakedPeersCount() >= s.MinPeers {
|
||||||
log.Info("minimum amount of peers were connected to")
|
s.log.Info("minimum amount of peers were connected to")
|
||||||
if s.connected.CAS(false, true) {
|
if s.connected.CAS(false, true) {
|
||||||
s.consensus.Start()
|
s.consensus.Start()
|
||||||
}
|
}
|
||||||
|
@ -304,12 +303,11 @@ func (s *Server) HandshakedPeersCount() int {
|
||||||
func (s *Server) startProtocol(p Peer) {
|
func (s *Server) startProtocol(p Peer) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
s.log.Info("started protocol",
|
||||||
"addr": p.RemoteAddr(),
|
zap.Stringer("addr", p.RemoteAddr()),
|
||||||
"userAgent": string(p.Version().UserAgent),
|
zap.ByteString("userAgent", p.Version().UserAgent),
|
||||||
"startHeight": p.Version().StartHeight,
|
zap.Uint32("startHeight", p.Version().StartHeight),
|
||||||
"id": p.Version().Nonce,
|
zap.Uint32("id", p.Version().Nonce))
|
||||||
}).Info("started protocol")
|
|
||||||
|
|
||||||
s.discovery.RegisterGoodAddr(p.PeerAddr().String())
|
s.discovery.RegisterGoodAddr(p.PeerAddr().String())
|
||||||
if s.chain.HeaderHeight() < p.Version().StartHeight {
|
if s.chain.HeaderHeight() < p.Version().StartHeight {
|
||||||
|
@ -386,7 +384,7 @@ func (s *Server) handleVersionCmd(p Peer, version *payload.Version) error {
|
||||||
// This method could best be called in a separate routine.
|
// This method could best be called in a separate routine.
|
||||||
func (s *Server) handleHeadersCmd(p Peer, headers *payload.Headers) {
|
func (s *Server) handleHeadersCmd(p Peer, headers *payload.Headers) {
|
||||||
if err := s.chain.AddHeaders(headers.Hdrs...); err != nil {
|
if err := s.chain.AddHeaders(headers.Hdrs...); err != nil {
|
||||||
log.Warnf("failed processing headers: %s", err)
|
s.log.Warn("failed processing headers", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// The peer will respond with a maximum of 2000 headers in one batch.
|
// The peer will respond with a maximum of 2000 headers in one batch.
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/config"
|
"github.com/CityOfZion/neo-go/config"
|
||||||
log "github.com/sirupsen/logrus"
|
"go.uber.org/zap/zapcore"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -53,7 +53,7 @@ type (
|
||||||
ProtoTickInterval time.Duration
|
ProtoTickInterval time.Duration
|
||||||
|
|
||||||
// Level of the internal logger.
|
// Level of the internal logger.
|
||||||
LogLevel log.Level
|
LogLevel zapcore.Level
|
||||||
|
|
||||||
// Wallet is a wallet configuration.
|
// Wallet is a wallet configuration.
|
||||||
Wallet *config.WalletConfig
|
Wallet *config.WalletConfig
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
func TestSendVersion(t *testing.T) {
|
func TestSendVersion(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
s = newTestServer()
|
s = newTestServer(t)
|
||||||
p = newLocalPeer(t)
|
p = newLocalPeer(t)
|
||||||
)
|
)
|
||||||
s.Port = 3000
|
s.Port = 3000
|
||||||
|
@ -37,7 +37,7 @@ func TestSendVersion(t *testing.T) {
|
||||||
// Server should reply with a verack after receiving a valid version.
|
// Server should reply with a verack after receiving a valid version.
|
||||||
func TestVerackAfterHandleVersionCmd(t *testing.T) {
|
func TestVerackAfterHandleVersionCmd(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
s = newTestServer()
|
s = newTestServer(t)
|
||||||
p = newLocalPeer(t)
|
p = newLocalPeer(t)
|
||||||
)
|
)
|
||||||
na, _ := net.ResolveTCPAddr("tcp", "0.0.0.0:3000")
|
na, _ := net.ResolveTCPAddr("tcp", "0.0.0.0:3000")
|
||||||
|
@ -58,7 +58,7 @@ func TestVerackAfterHandleVersionCmd(t *testing.T) {
|
||||||
// invalid version and disconnects the peer.
|
// invalid version and disconnects the peer.
|
||||||
func TestServerNotSendsVerack(t *testing.T) {
|
func TestServerNotSendsVerack(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
s = newTestServer()
|
s = newTestServer(t)
|
||||||
p = newLocalPeer(t)
|
p = newLocalPeer(t)
|
||||||
p2 = newLocalPeer(t)
|
p2 = newLocalPeer(t)
|
||||||
)
|
)
|
||||||
|
@ -91,7 +91,7 @@ func TestServerNotSendsVerack(t *testing.T) {
|
||||||
|
|
||||||
func TestRequestHeaders(t *testing.T) {
|
func TestRequestHeaders(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
s = newTestServer()
|
s = newTestServer(t)
|
||||||
p = newLocalPeer(t)
|
p = newLocalPeer(t)
|
||||||
)
|
)
|
||||||
p.messageHandler = func(t *testing.T, msg *Message) {
|
p.messageHandler = func(t *testing.T, msg *Message) {
|
||||||
|
|
|
@ -6,11 +6,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/io"
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
log "github.com/sirupsen/logrus"
|
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TCPTransport allows network communication over TCP.
|
// TCPTransport allows network communication over TCP.
|
||||||
type TCPTransport struct {
|
type TCPTransport struct {
|
||||||
|
log *zap.Logger
|
||||||
server *Server
|
server *Server
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
bindAddr string
|
bindAddr string
|
||||||
|
@ -20,8 +22,9 @@ var reClosedNetwork = regexp.MustCompile(".* use of closed network connection")
|
||||||
|
|
||||||
// NewTCPTransport returns a new TCPTransport that will listen for
|
// NewTCPTransport returns a new TCPTransport that will listen for
|
||||||
// new incoming peer connections.
|
// new incoming peer connections.
|
||||||
func NewTCPTransport(s *Server, bindAddr string) *TCPTransport {
|
func NewTCPTransport(s *Server, bindAddr string, log *zap.Logger) *TCPTransport {
|
||||||
return &TCPTransport{
|
return &TCPTransport{
|
||||||
|
log: log,
|
||||||
server: s,
|
server: s,
|
||||||
bindAddr: bindAddr,
|
bindAddr: bindAddr,
|
||||||
}
|
}
|
||||||
|
@ -41,7 +44,7 @@ func (t *TCPTransport) Dial(addr string, timeout time.Duration) error {
|
||||||
func (t *TCPTransport) Accept() {
|
func (t *TCPTransport) Accept() {
|
||||||
l, err := net.Listen("tcp", t.bindAddr)
|
l, err := net.Listen("tcp", t.bindAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("TCP listen error %s", err)
|
t.log.Panic("TCP listen error", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +53,7 @@ func (t *TCPTransport) Accept() {
|
||||||
for {
|
for {
|
||||||
conn, err := l.Accept()
|
conn, err := l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("TCP accept error: %s", err)
|
t.log.Warn("TCP accept error", zap.Error(err))
|
||||||
if t.isCloseError(err) {
|
if t.isCloseError(err) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -80,14 +83,17 @@ func (t *TCPTransport) handleConn(conn net.Conn) {
|
||||||
|
|
||||||
// When a new peer is connected we send out our version immediately.
|
// When a new peer is connected we send out our version immediately.
|
||||||
if err := t.server.sendVersion(p); err != nil {
|
if err := t.server.sendVersion(p); err != nil {
|
||||||
log.WithFields(log.Fields{
|
t.log.Error("error on sendVersion", zap.Stringer("addr", p.RemoteAddr()), zap.Error(err))
|
||||||
"addr": p.RemoteAddr(),
|
|
||||||
}).Error(err)
|
|
||||||
}
|
}
|
||||||
r := io.NewBinReaderFromIO(p.conn)
|
r := io.NewBinReaderFromIO(p.conn)
|
||||||
for {
|
for {
|
||||||
msg := &Message{}
|
msg := &Message{}
|
||||||
if err = msg.Decode(r); err != nil {
|
err := msg.Decode(r)
|
||||||
|
|
||||||
|
if err == payload.ErrTooManyHeaders {
|
||||||
|
t.log.Warn("not all headers were processed")
|
||||||
|
r.Err = nil
|
||||||
|
} else if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err = t.server.handleMessage(p, msg); err != nil {
|
if err = t.server.handleMessage(p, msg); err != nil {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -21,7 +21,6 @@ type (
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
RawParams json.RawMessage `json:"params,omitempty"`
|
RawParams json.RawMessage `json:"params,omitempty"`
|
||||||
RawID json.RawMessage `json:"id,omitempty"`
|
RawID json.RawMessage `json:"id,omitempty"`
|
||||||
enableCORSWorkaround bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response represents a standard JSON-RPC 2.0
|
// Response represents a standard JSON-RPC 2.0
|
||||||
|
@ -35,10 +34,9 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewRequest creates a new Request struct.
|
// NewRequest creates a new Request struct.
|
||||||
func NewRequest(corsWorkaround bool) *Request {
|
func NewRequest() *Request {
|
||||||
return &Request{
|
return &Request{
|
||||||
JSONRPC: jsonRPCVersion,
|
JSONRPC: jsonRPCVersion,
|
||||||
enableCORSWorkaround: corsWorkaround,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +71,7 @@ func (r *Request) Params() (*Params, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteErrorResponse writes an error response to the ResponseWriter.
|
// WriteErrorResponse writes an error response to the ResponseWriter.
|
||||||
func (r Request) WriteErrorResponse(w http.ResponseWriter, err error) {
|
func (s *Server) WriteErrorResponse(r *Request, w http.ResponseWriter, err error) {
|
||||||
jsonErr, ok := err.(*Error)
|
jsonErr, ok := err.(*Error)
|
||||||
if !ok {
|
if !ok {
|
||||||
jsonErr = NewInternalServerError("Internal server error", err)
|
jsonErr = NewInternalServerError("Internal server error", err)
|
||||||
|
@ -85,34 +83,36 @@ func (r Request) WriteErrorResponse(w http.ResponseWriter, err error) {
|
||||||
ID: r.RawID,
|
ID: r.RawID,
|
||||||
}
|
}
|
||||||
|
|
||||||
logFields := log.Fields{
|
logFields := []zap.Field{
|
||||||
"err": jsonErr.Cause,
|
zap.Error(jsonErr.Cause),
|
||||||
"method": r.Method,
|
zap.String("method", r.Method),
|
||||||
}
|
|
||||||
params, err := r.Params()
|
|
||||||
if err == nil {
|
|
||||||
logFields["params"] = *params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.WithFields(logFields).Error("Error encountered with rpc request")
|
params, err := r.Params()
|
||||||
|
if err == nil {
|
||||||
|
logFields = append(logFields, zap.Any("params", params))
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Error("Error encountered with rpc request", logFields...)
|
||||||
|
|
||||||
w.WriteHeader(jsonErr.HTTPCode)
|
w.WriteHeader(jsonErr.HTTPCode)
|
||||||
r.writeServerResponse(w, response)
|
s.writeServerResponse(r, w, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteResponse encodes the response and writes it to the ResponseWriter.
|
// WriteResponse encodes the response and writes it to the ResponseWriter.
|
||||||
func (r Request) WriteResponse(w http.ResponseWriter, result interface{}) {
|
func (s *Server) WriteResponse(r *Request, w http.ResponseWriter, result interface{}) {
|
||||||
response := Response{
|
response := Response{
|
||||||
JSONRPC: r.JSONRPC,
|
JSONRPC: r.JSONRPC,
|
||||||
Result: result,
|
Result: result,
|
||||||
ID: r.RawID,
|
ID: r.RawID,
|
||||||
}
|
}
|
||||||
|
|
||||||
r.writeServerResponse(w, response)
|
s.writeServerResponse(r, w, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Request) writeServerResponse(w http.ResponseWriter, response Response) {
|
func (s *Server) writeServerResponse(r *Request, w http.ResponseWriter, response Response) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
if r.enableCORSWorkaround {
|
if s.config.EnableCORSWorkaround {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With")
|
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With")
|
||||||
}
|
}
|
||||||
|
@ -120,12 +120,9 @@ func (r Request) writeServerResponse(w http.ResponseWriter, response Response) {
|
||||||
encoder := json.NewEncoder(w)
|
encoder := json.NewEncoder(w)
|
||||||
err := encoder.Encode(response)
|
err := encoder.Encode(response)
|
||||||
|
|
||||||
logFields := log.Fields{
|
|
||||||
"err": err,
|
|
||||||
"method": r.Method,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(logFields).Error("Error encountered while encoding response")
|
s.log.Error("Error encountered while encoding response",
|
||||||
|
zap.String("err", err.Error()),
|
||||||
|
zap.String("method", r.Method))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/rpc/wrappers"
|
"github.com/CityOfZion/neo-go/pkg/rpc/wrappers"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -26,6 +26,7 @@ type (
|
||||||
chain core.Blockchainer
|
chain core.Blockchainer
|
||||||
config config.RPCConfig
|
config config.RPCConfig
|
||||||
coreServer *network.Server
|
coreServer *network.Server
|
||||||
|
log *zap.Logger
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ var invalidBlockHeightError = func(index int, height int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new Server struct.
|
// NewServer creates a new Server struct.
|
||||||
func NewServer(chain core.Blockchainer, conf config.RPCConfig, coreServer *network.Server) Server {
|
func NewServer(chain core.Blockchainer, conf config.RPCConfig, coreServer *network.Server, log *zap.Logger) Server {
|
||||||
httpServer := &http.Server{
|
httpServer := &http.Server{
|
||||||
Addr: conf.Address + ":" + strconv.FormatUint(uint64(conf.Port), 10),
|
Addr: conf.Address + ":" + strconv.FormatUint(uint64(conf.Port), 10),
|
||||||
}
|
}
|
||||||
|
@ -44,6 +45,7 @@ func NewServer(chain core.Blockchainer, conf config.RPCConfig, coreServer *netwo
|
||||||
chain: chain,
|
chain: chain,
|
||||||
config: conf,
|
config: conf,
|
||||||
coreServer: coreServer,
|
coreServer: coreServer,
|
||||||
|
log: log,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,13 +53,11 @@ func NewServer(chain core.Blockchainer, conf config.RPCConfig, coreServer *netwo
|
||||||
// listening on the configured port.
|
// listening on the configured port.
|
||||||
func (s *Server) Start(errChan chan error) {
|
func (s *Server) Start(errChan chan error) {
|
||||||
if !s.config.Enabled {
|
if !s.config.Enabled {
|
||||||
log.Info("RPC server is not enabled")
|
s.log.Info("RPC server is not enabled")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.Handler = http.HandlerFunc(s.requestHandler)
|
s.Handler = http.HandlerFunc(s.requestHandler)
|
||||||
log.WithFields(log.Fields{
|
s.log.Info("starting rpc-server", zap.String("endpoint", s.Addr))
|
||||||
"endpoint": s.Addr,
|
|
||||||
}).Info("starting rpc-server")
|
|
||||||
|
|
||||||
errChan <- s.ListenAndServe()
|
errChan <- s.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
@ -65,17 +65,16 @@ func (s *Server) Start(errChan chan error) {
|
||||||
// Shutdown overrides the http.Server Shutdown
|
// Shutdown overrides the http.Server Shutdown
|
||||||
// method.
|
// method.
|
||||||
func (s *Server) Shutdown() error {
|
func (s *Server) Shutdown() error {
|
||||||
log.WithFields(log.Fields{
|
s.log.Info("shutting down rpc-server", zap.String("endpoint", s.Addr))
|
||||||
"endpoint": s.Addr,
|
|
||||||
}).Info("shutting down rpc-server")
|
|
||||||
return s.Server.Shutdown(context.Background())
|
return s.Server.Shutdown(context.Background())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request) {
|
func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request) {
|
||||||
req := NewRequest(s.config.EnableCORSWorkaround)
|
req := NewRequest()
|
||||||
|
|
||||||
if httpRequest.Method != "POST" {
|
if httpRequest.Method != "POST" {
|
||||||
req.WriteErrorResponse(
|
s.WriteErrorResponse(
|
||||||
|
req,
|
||||||
w,
|
w,
|
||||||
NewInvalidParamsError(
|
NewInvalidParamsError(
|
||||||
fmt.Sprintf("Invalid method '%s', please retry with 'POST'", httpRequest.Method), nil,
|
fmt.Sprintf("Invalid method '%s', please retry with 'POST'", httpRequest.Method), nil,
|
||||||
|
@ -86,13 +85,13 @@ func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request
|
||||||
|
|
||||||
err := req.DecodeData(httpRequest.Body)
|
err := req.DecodeData(httpRequest.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.WriteErrorResponse(w, NewParseError("Problem parsing JSON-RPC request body", err))
|
s.WriteErrorResponse(req, w, NewParseError("Problem parsing JSON-RPC request body", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reqParams, err := req.Params()
|
reqParams, err := req.Params()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.WriteErrorResponse(w, NewInvalidParamsError("Problem parsing request parameters", err))
|
s.WriteErrorResponse(req, w, NewInvalidParamsError("Problem parsing request parameters", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,10 +99,9 @@ func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) methodHandler(w http.ResponseWriter, req *Request, reqParams Params) {
|
func (s *Server) methodHandler(w http.ResponseWriter, req *Request, reqParams Params) {
|
||||||
log.WithFields(log.Fields{
|
s.log.Info("processing rpc request",
|
||||||
"method": req.Method,
|
zap.String("method", req.Method),
|
||||||
"params": fmt.Sprintf("%v", reqParams),
|
zap.String("params", fmt.Sprintf("%v", reqParams)))
|
||||||
}).Info("processing rpc request")
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
results interface{}
|
results interface{}
|
||||||
|
@ -268,11 +266,11 @@ Methods:
|
||||||
}
|
}
|
||||||
|
|
||||||
if resultsErr != nil {
|
if resultsErr != nil {
|
||||||
req.WriteErrorResponse(w, resultsErr)
|
s.WriteErrorResponse(req, w, resultsErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.WriteResponse(w, results)
|
s.WriteResponse(req, w, results)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
|
func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/rpc/result"
|
"github.com/CityOfZion/neo-go/pkg/rpc/result"
|
||||||
"github.com/CityOfZion/neo-go/pkg/rpc/wrappers"
|
"github.com/CityOfZion/neo-go/pkg/rpc/wrappers"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.uber.org/zap/zaptest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrorResponse struct represents JSON-RPC error.
|
// ErrorResponse struct represents JSON-RPC error.
|
||||||
|
@ -180,7 +181,8 @@ func initServerWithInMemoryChain(t *testing.T) (*core.Blockchain, http.HandlerFu
|
||||||
require.NoError(t, err, "could not load config")
|
require.NoError(t, err, "could not load config")
|
||||||
|
|
||||||
memoryStore := storage.NewMemoryStore()
|
memoryStore := storage.NewMemoryStore()
|
||||||
chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration)
|
logger := zaptest.NewLogger(t)
|
||||||
|
chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration, logger)
|
||||||
require.NoError(t, err, "could not create chain")
|
require.NoError(t, err, "could not create chain")
|
||||||
|
|
||||||
go chain.Run()
|
go chain.Run()
|
||||||
|
@ -198,8 +200,8 @@ func initServerWithInMemoryChain(t *testing.T) (*core.Blockchain, http.HandlerFu
|
||||||
}
|
}
|
||||||
|
|
||||||
serverConfig := network.NewServerConfig(cfg)
|
serverConfig := network.NewServerConfig(cfg)
|
||||||
server := network.NewServer(serverConfig, chain)
|
server := network.NewServer(serverConfig, chain, logger)
|
||||||
rpcServer := NewServer(chain, cfg.ApplicationConfiguration.RPC, server)
|
rpcServer := NewServer(chain, cfg.ApplicationConfiguration.RPC, server, logger)
|
||||||
handler := http.HandlerFunc(rpcServer.requestHandler)
|
handler := http.HandlerFunc(rpcServer.requestHandler)
|
||||||
|
|
||||||
return chain, handler
|
return chain, handler
|
||||||
|
|
|
@ -417,6 +417,8 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
func TestRPC(t *testing.T) {
|
func TestRPC(t *testing.T) {
|
||||||
chain, handler := initServerWithInMemoryChain(t)
|
chain, handler := initServerWithInMemoryChain(t)
|
||||||
|
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
e := &executor{chain: chain, handler: handler}
|
e := &executor{chain: chain, handler: handler}
|
||||||
for method, cases := range rpcTestCases {
|
for method, cases := range rpcTestCases {
|
||||||
t.Run(method, func(t *testing.T) {
|
t.Run(method, func(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue