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 }