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