github.com/cryptohub-digital/blockbook-fork@v0.0.0-20230713133354-673c927af7f1/bchain/coins/xcb/xcbrpc.go (about) 1 package xcb 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "math/big" 8 "strconv" 9 "sync" 10 "time" 11 12 "github.com/core-coin/go-core/v2" 13 xcbcommon "github.com/core-coin/go-core/v2/common" 14 "github.com/core-coin/go-core/v2/common/hexutil" 15 "github.com/core-coin/go-core/v2/core/types" 16 "github.com/core-coin/go-core/v2/rpc" 17 "github.com/core-coin/go-core/v2/xcbclient" 18 "github.com/cryptohub-digital/blockbook-fork/bchain" 19 "github.com/cryptohub-digital/blockbook-fork/common" 20 "github.com/golang/glog" 21 "github.com/juju/errors" 22 ) 23 24 // Network type specifies the type of core-coin network 25 type Network uint32 26 27 const ( 28 // MainNet is production network 29 MainNet Network = 1 30 // TestNet is Devin test network 31 TestNet Network = 3 32 ) 33 34 // Configuration represents json config file 35 type Configuration struct { 36 CoinName string `json:"coin_name"` 37 CoinShortcut string `json:"coin_shortcut"` 38 RPCURL string `json:"rpc_url"` 39 RPCTimeout int `json:"rpc_timeout"` 40 BlockAddressesToKeep int `json:"block_addresses_to_keep"` 41 AddressAliases bool `json:"address_aliases,omitempty"` 42 MempoolTxTimeoutHours int `json:"mempoolTxTimeoutHours"` 43 QueryBackendOnMempoolResync bool `json:"queryBackendOnMempoolResync"` 44 ProcessInternalTransactions bool `json:"processInternalTransactions"` 45 ProcessZeroInternalTransactions bool `json:"processZeroInternalTransactions"` 46 ConsensusNodeVersionURL string `json:"consensusNodeVersion"` 47 } 48 49 // CoreblockchainRPC is an interface to JSON-RPC xcb service. 50 type CoreblockchainRPC struct { 51 *bchain.BaseChain 52 Client CVMClient 53 RPC CVMRPCClient 54 MainNetChainID Network 55 Timeout time.Duration 56 Parser *CoreCoinParser 57 PushHandler func(bchain.NotificationType) 58 OpenRPC func(string) (CVMRPCClient, CVMClient, error) 59 Mempool *bchain.MempoolCoreCoinType 60 mempoolInitialized bool 61 bestHeaderLock sync.Mutex 62 bestHeader CVMHeader 63 bestHeaderTime time.Time 64 NewBlock CVMNewBlockSubscriber 65 newBlockSubscription CVMClientSubscription 66 NewTx CVMNewTxSubscriber 67 newTxSubscription CVMClientSubscription 68 ChainConfig *Configuration 69 } 70 71 // ProcessInternalTransactions specifies if internal transactions are processed 72 var ProcessInternalTransactions bool 73 74 // NewCoreblockchainRPC returns new XcbRPC instance. 75 func NewCoreblockchainRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { 76 var err error 77 var c Configuration 78 err = json.Unmarshal(config, &c) 79 if err != nil { 80 return nil, errors.Annotatef(err, "Invalid configuration file") 81 } 82 // keep at least 100 mappings block->addresses to allow rollback 83 if c.BlockAddressesToKeep < 100 { 84 c.BlockAddressesToKeep = 100 85 } 86 87 s := &CoreblockchainRPC{ 88 BaseChain: &bchain.BaseChain{}, 89 ChainConfig: &c, 90 } 91 92 ProcessInternalTransactions = c.ProcessInternalTransactions 93 94 // overwrite TokenTypeMap with crc specific token type names 95 bchain.TokenTypeMap = []bchain.TokenTypeName{CRC20TokenType, CRC721TokenType, bchain.ERC1155TokenType} 96 97 // always create parser 98 s.Parser = NewCoreCoinParser(c.BlockAddressesToKeep) 99 s.Timeout = time.Duration(c.RPCTimeout) * time.Second 100 s.PushHandler = pushHandler 101 102 return s, nil 103 } 104 105 // Initialize initializes core coin rpc interface 106 func (b *CoreblockchainRPC) Initialize() error { 107 b.OpenRPC = func(url string) (CVMRPCClient, CVMClient, error) { 108 r, err := rpc.Dial(url) 109 if err != nil { 110 return nil, nil, err 111 } 112 rc := &CoreCoinRPCClient{Client: r} 113 ec := &CoreblockchainClient{Client: xcbclient.NewClient(r)} 114 return rc, ec, nil 115 } 116 117 rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL) 118 if err != nil { 119 return err 120 } 121 122 // set chain specific 123 b.Client = ec 124 b.RPC = rc 125 b.MainNetChainID = MainNet 126 b.NewBlock = &CoreCoinNewBlock{channel: make(chan *types.Header)} 127 b.NewTx = &CoreCoinNewTx{channel: make(chan xcbcommon.Hash)} 128 129 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 130 defer cancel() 131 132 id, err := b.Client.NetworkID(ctx) 133 if err != nil { 134 return err 135 } 136 xcbcommon.DefaultNetworkID = xcbcommon.NetworkID(id.Int64()) 137 // parameters for getInfo request 138 switch Network(id.Uint64()) { 139 case MainNet: 140 b.Testnet = false 141 b.Network = "mainnet" 142 case TestNet: 143 b.Testnet = true 144 b.Network = "devin" 145 default: 146 return errors.Errorf("Unknown network id %v", id) 147 } 148 glog.Info("rpc: block chain ", b.Network) 149 150 return nil 151 } 152 153 // CreateMempool creates mempool if not already created, however does not initialize it 154 func (b *CoreblockchainRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) { 155 if b.Mempool == nil { 156 b.Mempool = bchain.NewMempoolCoreCoinType(chain, b.ChainConfig.MempoolTxTimeoutHours, b.ChainConfig.QueryBackendOnMempoolResync) 157 glog.Info("mempool created, MempoolTxTimeoutHours=", b.ChainConfig.MempoolTxTimeoutHours, ", QueryBackendOnMempoolResync=", b.ChainConfig.QueryBackendOnMempoolResync) 158 } 159 return b.Mempool, nil 160 } 161 162 // InitializeMempool creates subscriptions to newHeads and newPendingTransactions 163 func (b *CoreblockchainRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc, onNewTx bchain.OnNewTxFunc) error { 164 if b.Mempool == nil { 165 return errors.New("Mempool not created") 166 } 167 168 // get initial mempool transactions 169 txs, err := b.GetMempoolTransactions() 170 if err != nil { 171 return err 172 } 173 for _, txid := range txs { 174 b.Mempool.AddTransactionToMempool(txid) 175 } 176 177 b.Mempool.OnNewTxAddr = onNewTxAddr 178 b.Mempool.OnNewTx = onNewTx 179 180 if err = b.subscribeEvents(); err != nil { 181 return err 182 } 183 184 b.mempoolInitialized = true 185 186 return nil 187 } 188 189 func (b *CoreblockchainRPC) subscribeEvents() error { 190 // new block notifications handling 191 go func() { 192 for { 193 h, ok := b.NewBlock.Read() 194 if !ok { 195 break 196 } 197 b.UpdateBestHeader(h) 198 // notify blockbook 199 b.PushHandler(bchain.NotificationNewBlock) 200 } 201 }() 202 203 // new block subscription 204 if err := b.subscribe(func() (CVMClientSubscription, error) { 205 // invalidate the previous subscription - it is either the first one or there was an error 206 b.newBlockSubscription = nil 207 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 208 defer cancel() 209 sub, err := b.RPC.XcbSubscribe(ctx, b.NewBlock.Channel(), "newHeads") 210 if err != nil { 211 return nil, errors.Annotatef(err, "XcbSubscribe newHeads") 212 } 213 b.newBlockSubscription = sub 214 glog.Info("Subscribed to newHeads") 215 return sub, nil 216 }); err != nil { 217 return err 218 } 219 220 // new mempool transaction notifications handling 221 go func() { 222 for { 223 t, ok := b.NewTx.Read() 224 if !ok { 225 break 226 } 227 hex := t.Hex() 228 if glog.V(2) { 229 glog.Info("rpc: new tx ", hex) 230 } 231 b.Mempool.AddTransactionToMempool(hex) 232 b.PushHandler(bchain.NotificationNewTx) 233 } 234 }() 235 236 // new mempool transaction subscription 237 if err := b.subscribe(func() (CVMClientSubscription, error) { 238 // invalidate the previous subscription - it is either the first one or there was an error 239 b.newTxSubscription = nil 240 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 241 defer cancel() 242 sub, err := b.RPC.XcbSubscribe(ctx, b.NewTx.Channel(), "newPendingTransactions") 243 if err != nil { 244 return nil, errors.Annotatef(err, "XcbSubscribe newPendingTransactions") 245 } 246 b.newTxSubscription = sub 247 glog.Info("Subscribed to newPendingTransactions") 248 return sub, nil 249 }); err != nil { 250 return err 251 } 252 253 return nil 254 } 255 256 // subscribe subscribes notification and tries to resubscribe in case of error 257 func (b *CoreblockchainRPC) subscribe(f func() (CVMClientSubscription, error)) error { 258 s, err := f() 259 if err != nil { 260 return err 261 } 262 go func() { 263 Loop: 264 for { 265 // wait for error in subscription 266 e := <-s.Err() 267 // nil error means sub.Unsubscribe called, exit goroutine 268 if e == nil { 269 return 270 } 271 glog.Error("Subscription error ", e) 272 timer := time.NewTimer(time.Second * 2) 273 // try in 2 second interval to resubscribe 274 for { 275 select { 276 case e = <-s.Err(): 277 if e == nil { 278 return 279 } 280 case <-timer.C: 281 ns, err := f() 282 if err == nil { 283 // subscription successful, restart wait for next error 284 s = ns 285 continue Loop 286 } 287 glog.Error("Resubscribe error ", err) 288 timer.Reset(time.Second * 2) 289 } 290 } 291 } 292 }() 293 return nil 294 } 295 296 func (b *CoreblockchainRPC) closeRPC() { 297 if b.newBlockSubscription != nil { 298 b.newBlockSubscription.Unsubscribe() 299 } 300 if b.newTxSubscription != nil { 301 b.newTxSubscription.Unsubscribe() 302 } 303 if b.RPC != nil { 304 b.RPC.Close() 305 } 306 } 307 308 func (b *CoreblockchainRPC) reconnectRPC() error { 309 glog.Info("Reconnecting RPC") 310 b.closeRPC() 311 rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL) 312 if err != nil { 313 return err 314 } 315 b.RPC = rc 316 b.Client = ec 317 return b.subscribeEvents() 318 } 319 320 // Shutdown cleans up rpc interface to xcb 321 func (b *CoreblockchainRPC) Shutdown(ctx context.Context) error { 322 b.closeRPC() 323 b.NewBlock.Close() 324 b.NewTx.Close() 325 glog.Info("rpc: shutdown") 326 return nil 327 } 328 329 // GetCoinName returns coin name 330 func (b *CoreblockchainRPC) GetCoinName() string { 331 return b.ChainConfig.CoinName 332 } 333 334 // GetSubversion returns empty string, core coin does not have subversion 335 func (b *CoreblockchainRPC) GetSubversion() string { 336 return "" 337 } 338 339 // GetChainInfo returns information about the connected backend 340 func (b *CoreblockchainRPC) GetChainInfo() (*bchain.ChainInfo, error) { 341 h, err := b.getBestHeader() 342 if err != nil { 343 return nil, err 344 } 345 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 346 defer cancel() 347 id, err := b.Client.NetworkID(ctx) 348 if err != nil { 349 return nil, err 350 } 351 var ver string 352 if err := b.RPC.CallContext(ctx, &ver, "web3_clientVersion"); err != nil { 353 return nil, err 354 } 355 rv := &bchain.ChainInfo{ 356 Blocks: int(h.Number().Int64()), 357 Bestblockhash: h.Hash(), 358 Difficulty: h.Difficulty().String(), 359 Version: ver, 360 } 361 idi := int(id.Uint64()) 362 if idi == int(b.MainNetChainID) { 363 rv.Chain = "mainnet" 364 } else { 365 rv.Chain = "testnet " + strconv.Itoa(idi) 366 } 367 return rv, nil 368 } 369 370 func (b *CoreblockchainRPC) getBestHeader() (CVMHeader, error) { 371 b.bestHeaderLock.Lock() 372 defer b.bestHeaderLock.Unlock() 373 // if the best header was not updated for 15 minutes, there could be a subscription problem, reconnect RPC 374 // do it only in case of normal operation, not initial synchronization 375 if b.bestHeaderTime.Add(15*time.Minute).Before(time.Now()) && !b.bestHeaderTime.IsZero() && b.mempoolInitialized { 376 err := b.reconnectRPC() 377 if err != nil { 378 return nil, err 379 } 380 b.bestHeader = nil 381 } 382 if b.bestHeader == nil { 383 var err error 384 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 385 defer cancel() 386 b.bestHeader, err = b.Client.HeaderByNumber(ctx, nil) 387 if err != nil { 388 b.bestHeader = nil 389 return nil, err 390 } 391 b.bestHeaderTime = time.Now() 392 } 393 return b.bestHeader, nil 394 } 395 396 // UpdateBestHeader keeps track of the latest block header confirmed on chain 397 func (b *CoreblockchainRPC) UpdateBestHeader(h CVMHeader) { 398 glog.V(2).Info("rpc: new block header ", h.Number()) 399 b.bestHeaderLock.Lock() 400 b.bestHeader = h 401 b.bestHeaderTime = time.Now() 402 b.bestHeaderLock.Unlock() 403 } 404 405 // GetBestBlockHash returns hash of the tip of the best-block-chain 406 func (b *CoreblockchainRPC) GetBestBlockHash() (string, error) { 407 h, err := b.getBestHeader() 408 if err != nil { 409 return "", err 410 } 411 return h.Hash(), nil 412 } 413 414 // GetBestBlockHeight returns height of the tip of the best-block-chain 415 func (b *CoreblockchainRPC) GetBestBlockHeight() (uint32, error) { 416 h, err := b.getBestHeader() 417 if err != nil { 418 return 0, err 419 } 420 return uint32(h.Number().Uint64()), nil 421 } 422 423 // GetBlockHash returns hash of block in best-block-chain at given height 424 func (b *CoreblockchainRPC) GetBlockHash(height uint32) (string, error) { 425 var n big.Int 426 n.SetUint64(uint64(height)) 427 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 428 defer cancel() 429 h, err := b.Client.HeaderByNumber(ctx, &n) 430 if err != nil { 431 if err == core.NotFound { 432 return "", bchain.ErrBlockNotFound 433 } 434 return "", errors.Annotatef(err, "height %v", height) 435 } 436 return h.Hash(), nil 437 } 438 439 func (b *CoreblockchainRPC) xcbHeaderToBlockHeader(h *rpcHeader) (*bchain.BlockHeader, error) { 440 height, err := xcbNumber(h.Number) 441 if err != nil { 442 return nil, err 443 } 444 c, err := b.computeConfirmations(uint64(height)) 445 if err != nil { 446 return nil, err 447 } 448 time, err := xcbNumber(h.Time) 449 if err != nil { 450 return nil, err 451 } 452 size, err := xcbNumber(h.Size) 453 if err != nil { 454 return nil, err 455 } 456 return &bchain.BlockHeader{ 457 Hash: h.Hash, 458 Prev: h.ParentHash, 459 Height: uint32(height), 460 Confirmations: int(c), 461 Time: time, 462 Size: int(size), 463 }, nil 464 } 465 466 // GetBlockHeader returns header of block with given hash 467 func (b *CoreblockchainRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) { 468 raw, err := b.getBlockRaw(hash, 0, false) 469 if err != nil { 470 return nil, err 471 } 472 var h rpcHeader 473 if err := json.Unmarshal(raw, &h); err != nil { 474 return nil, errors.Annotatef(err, "hash %v", hash) 475 } 476 return b.xcbHeaderToBlockHeader(&h) 477 } 478 479 func (b *CoreblockchainRPC) computeConfirmations(n uint64) (uint32, error) { 480 bh, err := b.getBestHeader() 481 if err != nil { 482 return 0, err 483 } 484 bn := bh.Number().Uint64() 485 // transaction in the best block has 1 confirmation 486 return uint32(bn - n + 1), nil 487 } 488 489 func (b *CoreblockchainRPC) getBlockRaw(hash string, height uint32, fullTxs bool) (json.RawMessage, error) { 490 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 491 defer cancel() 492 var raw json.RawMessage 493 var err error 494 if hash != "" { 495 if hash == "pending" { 496 err = b.RPC.CallContext(ctx, &raw, "xcb_getBlockByNumber", hash, fullTxs) 497 } else { 498 err = b.RPC.CallContext(ctx, &raw, "xcb_getBlockByHash", xcbcommon.HexToHash(hash), fullTxs) 499 } 500 } else { 501 err = b.RPC.CallContext(ctx, &raw, "xcb_getBlockByNumber", fmt.Sprintf("%#x", height), fullTxs) 502 } 503 if err != nil { 504 return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) 505 } else if len(raw) == 0 || (len(raw) == 4 && string(raw) == "null") { 506 return nil, bchain.ErrBlockNotFound 507 } 508 return raw, nil 509 } 510 511 func (b *CoreblockchainRPC) getTokenEventsForBlock(blockNumber string) (map[string][]*RpcLog, error) { 512 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 513 defer cancel() 514 var logs []rpcLogWithTxHash 515 err := b.RPC.CallContext(ctx, &logs, "xcb_getLogs", map[string]interface{}{ 516 "fromBlock": blockNumber, 517 "toBlock": blockNumber, 518 "topics": []string{tokenTransferEventSignature}, 519 }) 520 if err != nil { 521 return nil, errors.Annotatef(err, "xcb_getLogs blockNumber %v", blockNumber) 522 } 523 r := make(map[string][]*RpcLog) 524 for i := range logs { 525 l := &logs[i] 526 r[l.Hash] = append(r[l.Hash], &l.RpcLog) 527 } 528 return r, nil 529 } 530 531 // GetBlock returns block with given hash or height, hash has precedence if both passed 532 func (b *CoreblockchainRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { 533 raw, err := b.getBlockRaw(hash, height, true) 534 if err != nil { 535 return nil, err 536 } 537 var head rpcHeader 538 if err := json.Unmarshal(raw, &head); err != nil { 539 return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) 540 } 541 var body rpcBlockTransactions 542 if err := json.Unmarshal(raw, &body); err != nil { 543 return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) 544 } 545 bbh, err := b.xcbHeaderToBlockHeader(&head) 546 if err != nil { 547 return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) 548 } 549 // get token events 550 logs, err := b.getTokenEventsForBlock(head.Number) 551 if err != nil { 552 return nil, err 553 } 554 btxs := make([]bchain.Tx, len(body.Transactions)) 555 for i := range body.Transactions { 556 tx := &body.Transactions[i] 557 btx, err := b.Parser.xcbTxToTx(tx, &RpcReceipt{Logs: logs[tx.Hash]}, bbh.Time, uint32(bbh.Confirmations)) 558 if err != nil { 559 return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash) 560 } 561 btxs[i] = *btx 562 if b.mempoolInitialized { 563 b.Mempool.RemoveTransactionFromMempool(tx.Hash) 564 } 565 } 566 bbk := bchain.Block{ 567 BlockHeader: *bbh, 568 Txs: btxs, 569 } 570 return &bbk, nil 571 } 572 573 // GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids 574 func (b *CoreblockchainRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) { 575 raw, err := b.getBlockRaw(hash, 0, false) 576 if err != nil { 577 return nil, err 578 } 579 var head rpcHeader 580 var txs rpcBlockTxids 581 if err := json.Unmarshal(raw, &head); err != nil { 582 return nil, errors.Annotatef(err, "hash %v", hash) 583 } 584 if err = json.Unmarshal(raw, &txs); err != nil { 585 return nil, err 586 } 587 bch, err := b.xcbHeaderToBlockHeader(&head) 588 if err != nil { 589 return nil, err 590 } 591 return &bchain.BlockInfo{ 592 BlockHeader: *bch, 593 Difficulty: common.JSONNumber(head.Difficulty), 594 Nonce: common.JSONNumber(head.Nonce), 595 Txids: txs.Transactions, 596 }, nil 597 } 598 599 // GetTransactionForMempool returns a transaction by the transaction ID. 600 // It could be optimized for mempool, i.e. without block time and confirmations 601 func (b *CoreblockchainRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) { 602 return b.GetTransaction(txid) 603 } 604 605 // GetTransaction returns a transaction by the transaction ID. 606 func (b *CoreblockchainRPC) GetTransaction(txid string) (*bchain.Tx, error) { 607 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 608 defer cancel() 609 var tx *RpcTransaction 610 hash := xcbcommon.HexToHash(txid) 611 err := b.RPC.CallContext(ctx, &tx, "xcb_getTransactionByHash", hash) 612 if err != nil { 613 return nil, err 614 } else if tx == nil { 615 if b.mempoolInitialized { 616 b.Mempool.RemoveTransactionFromMempool(txid) 617 } 618 return nil, bchain.ErrTxNotFound 619 } 620 var btx *bchain.Tx 621 if tx.BlockNumber == "" { 622 // mempool tx 623 btx, err = b.Parser.xcbTxToTx(tx, nil, 0, 0) 624 if err != nil { 625 return nil, errors.Annotatef(err, "txid %v", txid) 626 } 627 } else { 628 // non mempool tx - read the block header to get the block time 629 raw, err := b.getBlockRaw(tx.BlockHash, 0, false) 630 if err != nil { 631 return nil, err 632 } 633 var ht struct { 634 Time string `json:"timestamp"` 635 } 636 if err := json.Unmarshal(raw, &ht); err != nil { 637 return nil, errors.Annotatef(err, "hash %v", hash) 638 } 639 var time int64 640 if time, err = xcbNumber(ht.Time); err != nil { 641 return nil, errors.Annotatef(err, "txid %v", txid) 642 } 643 var receipt RpcReceipt 644 err = b.RPC.CallContext(ctx, &receipt, "xcb_getTransactionReceipt", hash) 645 if err != nil { 646 return nil, errors.Annotatef(err, "txid %v", txid) 647 } 648 n, err := xcbNumber(tx.BlockNumber) 649 if err != nil { 650 return nil, errors.Annotatef(err, "txid %v", txid) 651 } 652 confirmations, err := b.computeConfirmations(uint64(n)) 653 if err != nil { 654 return nil, errors.Annotatef(err, "txid %v", txid) 655 } 656 btx, err = b.Parser.xcbTxToTx(tx, &receipt, time, confirmations) 657 if err != nil { 658 return nil, errors.Annotatef(err, "txid %v", txid) 659 } 660 // remove tx from mempool if it is there 661 if b.mempoolInitialized { 662 b.Mempool.RemoveTransactionFromMempool(txid) 663 } 664 } 665 return btx, nil 666 } 667 668 // GetTransactionSpecific returns json as returned by backend, with all coin specific data 669 func (b *CoreblockchainRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) { 670 csd, ok := tx.CoinSpecificData.(CoreCoinSpecificData) 671 if !ok { 672 ntx, err := b.GetTransaction(tx.Txid) 673 if err != nil { 674 return nil, err 675 } 676 csd, ok = ntx.CoinSpecificData.(CoreCoinSpecificData) 677 if !ok { 678 return nil, errors.New("Cannot get CoinSpecificData") 679 } 680 } 681 m, err := json.Marshal(&csd) 682 return json.RawMessage(m), err 683 } 684 685 // GetMempoolTransactions returns transactions in mempool 686 func (b *CoreblockchainRPC) GetMempoolTransactions() ([]string, error) { 687 raw, err := b.getBlockRaw("pending", 0, false) 688 if err != nil { 689 return nil, err 690 } 691 var body rpcBlockTxids 692 if len(raw) > 0 { 693 if err := json.Unmarshal(raw, &body); err != nil { 694 return nil, err 695 } 696 } 697 return body.Transactions, nil 698 } 699 700 // EstimateFee returns fee estimation 701 func (b *CoreblockchainRPC) EstimateFee(blocks int) (big.Int, error) { 702 return b.EstimateSmartFee(blocks, true) 703 } 704 705 // EstimateSmartFee returns fee estimation 706 func (b *CoreblockchainRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) { 707 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 708 defer cancel() 709 var r big.Int 710 gp, err := b.Client.SuggestEnergyPrice(ctx) 711 if err == nil && b != nil { 712 r = *gp 713 } 714 return r, err 715 } 716 717 // GetStringFromMap attempts to return the value for a specific key in a map as a string if valid, 718 // otherwise returns an empty string with false indicating there was no key found, or the value was not a string 719 func GetStringFromMap(p string, params map[string]interface{}) (string, bool) { 720 v, ok := params[p] 721 if ok { 722 s, ok := v.(string) 723 return s, ok 724 } 725 return "", false 726 } 727 728 // CoreCoinTypeEstimateEnergy returns estimation of energy consumption for given transaction parameters 729 func (b *CoreblockchainRPC) CoreCoinTypeEstimateEnergy(params map[string]interface{}) (uint64, error) { 730 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 731 defer cancel() 732 msg := core.CallMsg{} 733 if s, ok := GetStringFromMap("from", params); ok && len(s) > 0 { 734 addr, err := xcbcommon.HexToAddress(s) 735 if err != nil { 736 return 0, err 737 } 738 msg.From = addr 739 } 740 if s, ok := GetStringFromMap("to", params); ok && len(s) > 0 { 741 a, err := xcbcommon.HexToAddress(s) 742 if err != nil { 743 return 0, err 744 } 745 msg.To = &a 746 } 747 if s, ok := GetStringFromMap("data", params); ok && len(s) > 0 { 748 msg.Data = xcbcommon.FromHex(s) 749 } 750 if s, ok := GetStringFromMap("value", params); ok && len(s) > 0 { 751 msg.Value, _ = hexutil.DecodeBig(s) 752 } 753 if s, ok := GetStringFromMap("energy", params); ok && len(s) > 0 { 754 msg.Energy, _ = hexutil.DecodeUint64(s) 755 } 756 if s, ok := GetStringFromMap("energyPrice", params); ok && len(s) > 0 { 757 msg.EnergyPrice, _ = hexutil.DecodeBig(s) 758 } 759 return b.Client.EstimateEnergy(ctx, msg) 760 } 761 762 // SendRawTransaction sends raw transaction 763 func (b *CoreblockchainRPC) SendRawTransaction(hex string) (string, error) { 764 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 765 defer cancel() 766 var raw json.RawMessage 767 err := b.RPC.CallContext(ctx, &raw, "xcb_sendRawTransaction", hex) 768 if err != nil { 769 return "", err 770 } else if len(raw) == 0 { 771 return "", errors.New("SendRawTransaction: failed") 772 } 773 var result string 774 if err := json.Unmarshal(raw, &result); err != nil { 775 return "", errors.Annotatef(err, "raw result %v", raw) 776 } 777 if result == "" { 778 return "", errors.New("SendRawTransaction: failed, empty result") 779 } 780 return result, nil 781 } 782 783 // CoreCoinTypeGetBalance returns current balance of an address 784 func (b *CoreblockchainRPC) CoreCoinTypeGetBalance(addrDesc bchain.AddressDescriptor) (*big.Int, error) { 785 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 786 defer cancel() 787 return b.Client.BalanceAt(ctx, addrDesc, nil) 788 } 789 790 // CoreCoinTypeGetNonce returns current balance of an address 791 func (b *CoreblockchainRPC) CoreCoinTypeGetNonce(addrDesc bchain.AddressDescriptor) (uint64, error) { 792 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 793 defer cancel() 794 return b.Client.NonceAt(ctx, addrDesc, nil) 795 } 796 797 // GetChainParser returns core coin BlockChainParser 798 func (b *CoreblockchainRPC) GetChainParser() bchain.BlockChainParser { 799 return b.Parser 800 }