github.com/igggame/nebulas-go@v2.1.0+incompatible/nf/nbre/nbre.go (about)

     1  // Copyright (C) 2018 go-nebulas authors
     2  //
     3  // This file is part of the go-nebulas library.
     4  //
     5  // the go-nebulas library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // the go-nebulas library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with the go-nebulas library.  If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  
    19  package nbre
    20  
    21  /*
    22  #cgo LDFLAGS: -L${SRCDIR}/native -lnbre_rt
    23  
    24  #include <stdlib.h>
    25  #include <native/nipc_interface.h>
    26  
    27  void NbreVersionFunc_cgo(int isc, void *holder, uint32_t major, uint32_t minor,uint32_t patch);
    28  void NbreIrListFunc_cgo(int isc, void *holder, const char *ir_name_list);
    29  void NbreIrVersionsFunc_cgo(int isc, void *holder, const char *ir_versions);
    30  void NbreNrHandleFunc_cgo(int isc, void *holder, const char *nr_handle);
    31  void NbreNrResultByhandleFunc_cgo(int isc, void *holder, const char *nr_result);
    32  void NbreNrResultByHeightFunc_cgo(int isc, void *holder, const char *nr_result);
    33  void NbreNrSumFunc_cgo(int isc, void *holder, const char *nr_sum);
    34  void NbreDipRewardFunc_cgo(int isc, void *holder, const char *dip_reward);
    35  */
    36  import "C"
    37  import (
    38  	"sync"
    39  	"time"
    40  
    41  	"github.com/gogo/protobuf/proto"
    42  
    43  	"unsafe"
    44  
    45  	"path/filepath"
    46  
    47  	"github.com/nebulasio/go-nebulas/core"
    48  	"github.com/nebulasio/go-nebulas/util"
    49  	"github.com/nebulasio/go-nebulas/util/logging"
    50  	"github.com/sirupsen/logrus"
    51  )
    52  
    53  const (
    54  	// ExecutionTimeoutSeconds max nbre execution timeout.
    55  	ExecutionTimeoutSeconds = 15
    56  )
    57  
    58  // default config path
    59  const (
    60  	defaultRootDir     = "nbre"
    61  	defaultLogDir      = "nbre/logs"
    62  	defaultNbreDataDir = "nbre/nbre.db"
    63  	defaultNbrePath    = "nbre/nbre"
    64  )
    65  
    66  // nbre private data
    67  var (
    68  	nbreOnce     = sync.Once{}
    69  	handlerIdx   = uint64(0)
    70  	nbreHandlers = make(map[uint64]*handler, 1024)
    71  	nbreLock     = sync.RWMutex{}
    72  )
    73  
    74  type handler struct {
    75  	id     uint64
    76  	result interface{}
    77  	err    error
    78  	done   chan bool
    79  }
    80  
    81  // Nbre type of Nbre
    82  type Nbre struct {
    83  	neb Neblet
    84  
    85  	libHeight uint64
    86  
    87  	quitCh chan int
    88  }
    89  
    90  // NewNbre create new Nbre
    91  func NewNbre(neb Neblet) core.Nbre {
    92  	nbreOnce.Do(func() {
    93  		InitializeNbre()
    94  	})
    95  	return &Nbre{
    96  		neb:       neb,
    97  		libHeight: 2, //lib start from 2
    98  		quitCh:    make(chan int, 1),
    99  	}
   100  }
   101  
   102  //func getCurrPath() string {
   103  //	file, _ := exec.LookPath(os.Args[0])
   104  //	path, _ := filepath.Abs(file)
   105  //	index := strings.LastIndex(path, string(os.PathSeparator))
   106  //	ret := path[:index]
   107  //	return ret
   108  //}
   109  
   110  // Start launch the nbre
   111  func (n *Nbre) Start() error {
   112  	if n.neb.Config().Nbre == nil {
   113  		return ErrConfigNotFound
   114  	}
   115  
   116  	if n.neb.BlockChain().LIB() != nil {
   117  		n.libHeight = n.neb.BlockChain().LIB().Height()
   118  	}
   119  
   120  	var (
   121  		rootDir     = defaultRootDir
   122  		logDir      = defaultLogDir
   123  		nbreDataDir = defaultNbreDataDir
   124  		nbrePath    = defaultNbrePath
   125  		err         error
   126  	)
   127  	conf := n.neb.Config().Nbre
   128  	if len(conf.RootDir) > 0 {
   129  		rootDir = conf.RootDir
   130  	}
   131  	if rootDir, err = filepath.Abs(rootDir); err != nil {
   132  		return err
   133  	}
   134  	if err := util.CreateDirIfNotExist(rootDir); err != nil {
   135  		return err
   136  	}
   137  
   138  	if len(conf.LogDir) > 0 {
   139  		logDir = conf.LogDir
   140  	}
   141  	if logDir, err = filepath.Abs(logDir); err != nil {
   142  		return err
   143  	}
   144  
   145  	if err := util.CreateDirIfNotExist(logDir); err != nil {
   146  		return err
   147  	}
   148  
   149  	if len(conf.DataDir) > 0 {
   150  		nbreDataDir = conf.DataDir
   151  	}
   152  	if nbreDataDir, err = filepath.Abs(nbreDataDir); err != nil {
   153  		return err
   154  	}
   155  
   156  	if len(conf.NbrePath) > 0 {
   157  		nbrePath = conf.NbrePath
   158  	}
   159  	if nbrePath, err = filepath.Abs(nbrePath); err != nil {
   160  		return err
   161  	}
   162  
   163  	dataDir, err := filepath.Abs(n.neb.Config().Chain.Datadir)
   164  	if err != nil {
   165  		return err
   166  	}
   167  
   168  	cRootDir := C.CString(rootDir)
   169  	defer C.free(unsafe.Pointer(cRootDir))
   170  	cLogDir := C.CString(logDir)
   171  	defer C.free(unsafe.Pointer(cLogDir))
   172  	cNbreDataDir := C.CString(nbreDataDir)
   173  	defer C.free(unsafe.Pointer(cNbreDataDir))
   174  	cNbrePath := C.CString(nbrePath)
   175  	defer C.free(unsafe.Pointer(cNbrePath))
   176  	cDataDir := C.CString(dataDir)
   177  	defer C.free(unsafe.Pointer(cDataDir))
   178  
   179  	cAdminAddr := C.CString(n.neb.Config().Nbre.AdminAddress)
   180  	defer C.free(unsafe.Pointer(cAdminAddr))
   181  
   182  	cIpcListen := C.CString(n.neb.Config().Nbre.IpcListen)
   183  	defer C.free(unsafe.Pointer(cIpcListen))
   184  
   185  	p := C.nbre_params_t{
   186  		m_nbre_root_dir:     cRootDir,
   187  		m_nbre_exe_name:     cNbrePath,
   188  		m_neb_db_dir:        cDataDir,
   189  		m_nbre_db_dir:       cNbreDataDir,
   190  		m_nbre_log_dir:      cLogDir,
   191  		m_admin_pub_addr:    cAdminAddr,
   192  		m_nbre_start_height: C.uint64_t(n.neb.Config().Nbre.StartHeight),
   193  		m_nipc_listen:       cIpcListen,
   194  		m_nipc_port:         C.uint16_t(n.neb.Config().Nbre.IpcPort),
   195  	}
   196  
   197  	cResult := C.start_nbre_ipc(p)
   198  	if int(cResult) != 0 {
   199  		logging.VLog().WithFields(logrus.Fields{
   200  			"data":   nbreDataDir,
   201  			"nbre":   nbrePath,
   202  			"result": int(cResult),
   203  		}).Error("Failed to start nbre.")
   204  		return ErrNbreStartFailed
   205  	}
   206  
   207  	logging.CLog().WithFields(logrus.Fields{
   208  		"data":  nbreDataDir,
   209  		"nbre":  nbrePath,
   210  		"admin": n.neb.Config().Nbre.AdminAddress,
   211  	}).Info("Started nbre.")
   212  
   213  	go n.loop()
   214  	return nil
   215  }
   216  
   217  func (n *Nbre) loop() {
   218  
   219  	logging.CLog().Info("started nbre loop.")
   220  
   221  	timerChan := time.NewTicker(time.Second * 15).C
   222  	for {
   223  		select {
   224  		case <-n.quitCh:
   225  			logging.CLog().Info("Stopped nbre ir loop.")
   226  			return
   227  		case <-timerChan:
   228  			n.checkIRUpdate()
   229  		}
   230  	}
   231  }
   232  
   233  // checkIRUpdate check lib block for ir transactions packaged.
   234  // If ir transactions are missed, nbre looks for database completion
   235  func (n *Nbre) checkIRUpdate() {
   236  	lib := n.neb.BlockChain().LIB()
   237  	if lib == nil || lib.Height() < n.neb.Config().Nbre.StartHeight {
   238  		return
   239  	}
   240  
   241  	for lib.Height() >= n.libHeight {
   242  		block := n.neb.BlockChain().GetBlockOnCanonicalChainByHeight(n.libHeight)
   243  		txs := []*core.Transaction{}
   244  		for _, tx := range block.Transactions() {
   245  			if tx.Type() == core.TxPayloadProtocolType {
   246  				txs = append(txs, tx)
   247  			}
   248  		}
   249  
   250  		handle := unsafe.Pointer(uintptr(block.Height()))
   251  		//prepare for tx send
   252  		C.ipc_nbre_ir_transactions_create(handle, C.uint64_t(block.Height()))
   253  
   254  		if len(txs) > 0 {
   255  			//append tx data
   256  			for _, tx := range txs {
   257  				pbTx, err := tx.ToProto()
   258  				if err != nil {
   259  					logging.VLog().WithFields(logrus.Fields{
   260  						"tx":  tx,
   261  						"err": err,
   262  					}).Error("Failed to convert the ir tx to proto data.")
   263  					return
   264  				}
   265  				bytes, err := proto.Marshal(pbTx)
   266  				if err != nil {
   267  					logging.VLog().WithFields(logrus.Fields{
   268  						"tx":  tx,
   269  						"err": err,
   270  					}).Error("Failed to marshal the ir tx.")
   271  					return
   272  				}
   273  				cBytes := (*C.char)(unsafe.Pointer(&bytes[0]))
   274  				C.ipc_nbre_ir_transactions_append(handle, C.uint64_t(block.Height()), cBytes, C.int32_t(len(bytes)))
   275  			}
   276  		}
   277  
   278  		// commit tx send
   279  		C.ipc_nbre_ir_transactions_send(handle, C.uint64_t(block.Height()))
   280  
   281  		logging.VLog().WithFields(logrus.Fields{
   282  			"height":   block.Height(),
   283  			"ir count": len(txs),
   284  		}).Debug("Update ir block.")
   285  
   286  		n.libHeight++
   287  	}
   288  }
   289  
   290  // InitializeNbre initialize nbre
   291  func InitializeNbre() {
   292  	C.set_recv_nbre_version_callback((C.nbre_version_callback_t)(unsafe.Pointer(C.NbreVersionFunc_cgo)))
   293  	C.set_recv_nbre_ir_list_callback((C.nbre_ir_list_callback_t)(unsafe.Pointer(C.NbreIrListFunc_cgo)))
   294  	C.set_recv_nbre_ir_versions_callback((C.nbre_ir_versions_callback_t)(unsafe.Pointer(C.NbreIrVersionsFunc_cgo)))
   295  	C.set_recv_nbre_nr_handle_callback((C.nbre_nr_handle_callback_t)(unsafe.Pointer(C.NbreNrHandleFunc_cgo)))
   296  	C.set_recv_nbre_nr_result_by_handle_callback((C.nbre_nr_result_by_handle_callback_t)(unsafe.Pointer(C.NbreNrResultByhandleFunc_cgo)))
   297  	C.set_recv_nbre_nr_result_by_height_callback((C.nbre_nr_result_by_height_callback_t)(unsafe.Pointer(C.NbreNrResultByHeightFunc_cgo)))
   298  	C.set_recv_nbre_nr_sum_callback((C.nbre_nr_sum_callback_t)(unsafe.Pointer(C.NbreNrSumFunc_cgo)))
   299  	C.set_recv_nbre_dip_reward_callback((C.nbre_dip_reward_callback_t)(unsafe.Pointer(C.NbreDipRewardFunc_cgo)))
   300  }
   301  
   302  // Execute execute command
   303  func (n *Nbre) Execute(command string, args ...interface{}) (interface{}, error) {
   304  	handlerIdx++
   305  	handler := &handler{
   306  		id:     handlerIdx,
   307  		done:   make(chan bool, 1),
   308  		err:    nil,
   309  		result: nil,
   310  	}
   311  
   312  	//(func() {
   313  	nbreLock.Lock()
   314  	nbreHandlers[handler.id] = handler
   315  	nbreLock.Unlock()
   316  	//})()
   317  
   318  	logging.VLog().WithFields(logrus.Fields{
   319  		"id":      handler.id,
   320  		"command": command,
   321  		"args":    args,
   322  	}).Debug("run nbre command")
   323  
   324  	go func() {
   325  		// handle nbre command
   326  		n.handleNbreCommand(handler, command, args...)
   327  	}()
   328  
   329  	select {
   330  	case <-handler.done:
   331  		// wait for C func returns.
   332  		deleteHandler(handler)
   333  
   334  	case <-time.After(ExecutionTimeoutSeconds * time.Second):
   335  		handler.err = ErrNebCallbackTimeout
   336  		deleteHandler(handler)
   337  	}
   338  
   339  	logging.VLog().WithFields(logrus.Fields{
   340  		"id":      handler.id,
   341  		"command": command,
   342  		"params":  args,
   343  		"result":  handler.result,
   344  		"error":   handler.err,
   345  	}).Debug("nbre command response")
   346  	return handler.result, handler.err
   347  }
   348  
   349  func deleteHandler(handler *handler) {
   350  	nbreLock.Lock()
   351  	defer nbreLock.Unlock()
   352  	handler.done = nil
   353  	delete(nbreHandlers, handler.id)
   354  }
   355  
   356  func (n *Nbre) handleNbreCommand(handler *handler, command string, args ...interface{}) {
   357  	handlerId := uint64(0)
   358  	if handler != nil {
   359  		handlerId = handler.id
   360  	}
   361  
   362  	switch command {
   363  	case CommandVersion:
   364  		height := args[0].(uint64)
   365  		C.ipc_nbre_version(unsafe.Pointer(uintptr(handlerId)), C.uint64_t(height))
   366  	case CommandIRList:
   367  		C.ipc_nbre_ir_list(unsafe.Pointer(uintptr(handlerId)))
   368  	case CommandIRVersions:
   369  		irName := args[0].(string)
   370  		cIrName := C.CString(irName)
   371  		defer C.free(unsafe.Pointer(cIrName))
   372  		C.ipc_nbre_ir_versions(unsafe.Pointer(uintptr(handlerId)), cIrName)
   373  	case CommandNRHandler:
   374  		start := args[0].(uint64)
   375  		end := args[1].(uint64)
   376  		version := args[2].(uint64)
   377  		C.ipc_nbre_nr_handle(unsafe.Pointer(uintptr(handlerId)), C.uint64_t(start), C.uint64_t(end), C.uint64_t(version))
   378  	case CommandNRListByHandle:
   379  		handle := args[0].(string)
   380  		cHandle := C.CString(handle)
   381  		defer C.free(unsafe.Pointer(cHandle))
   382  		C.ipc_nbre_nr_result_by_handle(unsafe.Pointer(uintptr(handlerId)), cHandle)
   383  	case CommandNRListByHeight:
   384  		height := args[0].(uint64)
   385  		C.ipc_nbre_nr_result_by_height(unsafe.Pointer(uintptr(handlerId)), C.uint64_t(height))
   386  	case CommandNRSum:
   387  		height := args[0].(uint64)
   388  		C.ipc_nbre_nr_sum(unsafe.Pointer(uintptr(handlerId)), C.uint64_t(height))
   389  	case CommandDIPList:
   390  		height := args[0].(uint64)
   391  		version := args[1].(uint64)
   392  		C.ipc_nbre_dip_reward(unsafe.Pointer(uintptr(handlerId)), C.uint64_t(height), C.uint64_t(version))
   393  	default:
   394  		if handler != nil {
   395  			handler.result = nil
   396  			handler.err = ErrCommandNotFound
   397  			if handler.done != nil {
   398  				handler.done <- true
   399  			}
   400  		}
   401  	}
   402  }
   403  
   404  func getNbreHandler(id uint64) (*handler, error) {
   405  	nbreLock.Lock()
   406  	defer nbreLock.Unlock()
   407  
   408  	if handler, ok := nbreHandlers[id]; ok {
   409  		return handler, nil
   410  	} else {
   411  		return nil, ErrHandlerNotFound
   412  	}
   413  }
   414  
   415  func nbreHandled(code C.int, holder unsafe.Pointer, result interface{}, handleErr error) {
   416  	handlerId := uint64(uintptr(holder))
   417  	handler, err := getNbreHandler(handlerId)
   418  	if err != nil {
   419  		logging.VLog().WithFields(logrus.Fields{
   420  			"handlerId": handlerId,
   421  			"err":       err,
   422  		}).Error("Failed to handle nbre callback")
   423  		return
   424  	}
   425  
   426  	switch code {
   427  	case C.ipc_status_succ:
   428  		err = nil
   429  	case C.ipc_status_fail:
   430  		err = ErrNbreCallbackFailed
   431  	case C.ipc_status_timeout:
   432  		err = ErrExecutionTimeout
   433  	case C.ipc_status_exception:
   434  		err = ErrNbreCallbackException
   435  	case C.ipc_status_nbre_not_ready:
   436  		err = ErrNbreCallbackNotReady
   437  	default:
   438  		err = ErrNbreCallbackCodeErr
   439  	}
   440  
   441  	if err == nil {
   442  		handler.result = result
   443  		handler.err = handleErr
   444  	} else {
   445  		handler.err = err
   446  	}
   447  	go func() {
   448  		if handler.done != nil {
   449  			handler.done <- true
   450  		}
   451  	}()
   452  }
   453  
   454  // Stop stop nbre
   455  func (n *Nbre) Stop() {
   456  	logging.CLog().Info("Stopping Nbre.")
   457  
   458  	// stop ir check loop
   459  	n.quitCh <- 1
   460  
   461  	select {
   462  	case <-n.shutdown():
   463  		return
   464  	}
   465  }
   466  
   467  // Shutdown shutdown nbre
   468  func (n *Nbre) shutdown() chan bool {
   469  	quitCh := make(chan bool, 1)
   470  
   471  	go func() {
   472  		C.nbre_ipc_shutdown()
   473  		logging.CLog().Info("Stopped Nbre.")
   474  
   475  		quitCh <- true
   476  		return
   477  	}()
   478  
   479  	return quitCh
   480  }