code.vegaprotocol.io/vega@v0.79.0/core/api/core.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package api 17 18 import ( 19 "context" 20 "encoding/hex" 21 "fmt" 22 "math" 23 "reflect" 24 "strings" 25 "sync" 26 "sync/atomic" 27 "time" 28 29 "code.vegaprotocol.io/vega/core/events" 30 "code.vegaprotocol.io/vega/core/evtforward" 31 "code.vegaprotocol.io/vega/core/metrics" 32 "code.vegaprotocol.io/vega/core/stats" 33 "code.vegaprotocol.io/vega/core/vegatime" 34 "code.vegaprotocol.io/vega/libs/proto" 35 "code.vegaprotocol.io/vega/libs/subscribers" 36 "code.vegaprotocol.io/vega/logging" 37 ptypes "code.vegaprotocol.io/vega/protos/vega" 38 protoapi "code.vegaprotocol.io/vega/protos/vega/api/v1" 39 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 40 eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" 41 "code.vegaprotocol.io/vega/wallet/crypto" 42 43 "github.com/cometbft/cometbft/libs/bytes" 44 tmctypes "github.com/cometbft/cometbft/rpc/core/types" 45 "github.com/pkg/errors" 46 "google.golang.org/grpc/codes" 47 ) 48 49 var ( 50 ErrInvalidSignature = errors.New("invalid signature") 51 ErrSubmitTxCommitDisabled = errors.New("broadcast_tx_commit is disabled") 52 ErrUnknownSubmitTxRequestType = errors.New("invalid broadcast_tx type") 53 ) 54 55 type coreService struct { 56 protoapi.UnimplementedCoreServiceServer 57 log *logging.Logger 58 conf Config 59 60 blockchain Blockchain 61 stats *stats.Stats 62 63 svcMu sync.RWMutex 64 evtForwarder EvtForwarder 65 timesvc TimeService 66 eventService EventService 67 subCancels []func() 68 powParams ProofOfWorkParams 69 spamEngine SpamEngine 70 powEngine PowEngine 71 72 chainID string 73 genesisTime time.Time 74 hasGenesisTimeAndChainID atomic.Bool 75 mu sync.Mutex 76 77 netInfo *tmctypes.ResultNetInfo 78 netInfoMu sync.RWMutex 79 } 80 81 func (s *coreService) UpdateProtocolServices( 82 evtforwarder EvtForwarder, 83 timesvc TimeService, 84 evtsvc EventService, 85 powParams ProofOfWorkParams, 86 ) { 87 s.svcMu.Lock() 88 defer s.svcMu.Unlock() 89 // first cancel all subscriptions 90 for _, f := range s.subCancels { 91 f() 92 } 93 s.evtForwarder = evtforwarder 94 s.eventService = evtsvc 95 s.timesvc = timesvc 96 s.powParams = powParams 97 } 98 99 // no need for a mutex - we only access the config through a value receiver. 100 func (s *coreService) updateConfig(conf Config) { 101 s.conf = conf 102 } 103 104 func (s *coreService) LastBlockHeight( 105 ctx context.Context, 106 _ *protoapi.LastBlockHeightRequest, 107 ) (*protoapi.LastBlockHeightResponse, error) { 108 defer metrics.StartAPIRequestAndTimeGRPC("LastBlockHeight")() 109 110 if !s.hasGenesisTimeAndChainID.Load() { 111 if err := s.getGenesisTimeAndChainID(ctx); err != nil { 112 return nil, fmt.Errorf("failed to intialise chainID: %w", err) 113 } 114 } 115 116 blockHeight, blockHash := s.powParams.BlockData() 117 if s.log.IsDebug() { 118 s.log.Debug("block height requested, returning", logging.Uint64("block-height", blockHeight), logging.String("block hash", blockHash), logging.String("chaindID", s.chainID)) 119 } 120 121 if !s.powParams.IsReady() { 122 return nil, errors.New("Failed to get last block height server is initialising") 123 } 124 125 return &protoapi.LastBlockHeightResponse{ 126 Height: blockHeight, 127 Hash: blockHash, 128 SpamPowDifficulty: s.powParams.SpamPoWDifficulty(), 129 SpamPowHashFunction: s.powParams.SpamPoWHashFunction(), 130 SpamPowNumberOfPastBlocks: s.powParams.SpamPoWNumberOfPastBlocks(), 131 SpamPowNumberOfTxPerBlock: s.powParams.SpamPoWNumberOfTxPerBlock(), 132 SpamPowIncreasingDifficulty: s.powParams.SpamPoWIncreasingDifficulty(), 133 ChainId: s.chainID, 134 }, nil 135 } 136 137 // GetVegaTime returns the latest blockchain header timestamp, in UnixNano format. 138 // Example: "1568025900111222333" corresponds to 2019-09-09T10:45:00.111222333Z. 139 func (s *coreService) GetVegaTime(ctx context.Context, _ *protoapi.GetVegaTimeRequest) (*protoapi.GetVegaTimeResponse, error) { 140 defer metrics.StartAPIRequestAndTimeGRPC("GetVegaTime")() 141 s.svcMu.RLock() 142 defer s.svcMu.RUnlock() 143 144 return &protoapi.GetVegaTimeResponse{ 145 Timestamp: s.timesvc.GetTimeNow().UnixNano(), 146 }, nil 147 } 148 149 func (s *coreService) SubmitTransaction(ctx context.Context, req *protoapi.SubmitTransactionRequest) (*protoapi.SubmitTransactionResponse, error) { 150 startTime := time.Now() 151 defer metrics.APIRequestAndTimeGRPC("SubmitTransaction", startTime) 152 153 if req == nil { 154 return nil, apiError(codes.InvalidArgument, ErrMalformedRequest) 155 } 156 157 txResult, err := s.blockchain.SubmitTransactionSync(ctx, req.Tx) 158 if err != nil { 159 return nil, apiError(codes.Internal, err) 160 } 161 162 return &protoapi.SubmitTransactionResponse{ 163 Success: txResult.Code == 0, 164 Code: txResult.Code, 165 Data: string(txResult.Data.Bytes()), 166 Log: txResult.Log, 167 Height: 0, 168 TxHash: txResult.Hash.String(), 169 }, nil 170 } 171 172 func (s *coreService) CheckTransaction(ctx context.Context, req *protoapi.CheckTransactionRequest) (*protoapi.CheckTransactionResponse, error) { 173 startTime := time.Now() 174 defer metrics.APIRequestAndTimeGRPC("CheckTransaction", startTime) 175 176 if req == nil { 177 return nil, apiError(codes.InvalidArgument, ErrMalformedRequest) 178 } 179 180 checkResult, err := s.blockchain.CheckTransaction(ctx, req.Tx) 181 if err != nil { 182 return nil, apiError(codes.Internal, err) 183 } 184 185 return &protoapi.CheckTransactionResponse{ 186 Code: checkResult.Code, 187 Data: string(checkResult.Data), 188 Info: checkResult.Info, 189 Log: checkResult.Log, 190 Success: checkResult.IsOK(), 191 GasWanted: checkResult.GasWanted, 192 GasUsed: checkResult.GasUsed, 193 }, nil 194 } 195 196 func (s *coreService) CheckRawTransaction(ctx context.Context, req *protoapi.CheckRawTransactionRequest) (*protoapi.CheckRawTransactionResponse, error) { 197 startTime := time.Now() 198 defer metrics.APIRequestAndTimeGRPC("CheckRawTransaction", startTime) 199 200 if req == nil { 201 return nil, apiError(codes.InvalidArgument, ErrMalformedRequest) 202 } 203 204 checkResult, err := s.blockchain.CheckRawTransaction(ctx, req.Tx) 205 if err != nil { 206 return nil, apiError(codes.Internal, err) 207 } 208 209 return &protoapi.CheckRawTransactionResponse{ 210 Code: checkResult.Code, 211 Data: string(checkResult.Data), 212 Info: checkResult.Info, 213 Log: checkResult.Log, 214 Success: checkResult.IsOK(), 215 GasWanted: checkResult.GasWanted, 216 GasUsed: checkResult.GasUsed, 217 }, nil 218 } 219 220 func (s *coreService) PropagateChainEvent(ctx context.Context, req *protoapi.PropagateChainEventRequest) (*protoapi.PropagateChainEventResponse, error) { 221 if req.Event == nil { 222 return nil, apiError(codes.InvalidArgument, ErrMalformedRequest) 223 } 224 225 // verify the signature then 226 err := verifySignature(s.log, req.Event, req.Signature, req.PubKey) 227 if err != nil { 228 return nil, apiError(codes.InvalidArgument, fmt.Errorf("not a valid signature: %w", err)) 229 } 230 231 evt := commandspb.ChainEvent{} 232 err = proto.Unmarshal(req.Event, &evt) 233 if err != nil { 234 return nil, apiError(codes.InvalidArgument, fmt.Errorf("not a valid chain event: %w", err)) 235 } 236 237 ok := true 238 s.svcMu.RLock() 239 defer s.svcMu.RUnlock() 240 err = s.evtForwarder.Forward(ctx, &evt, req.PubKey) 241 if err != nil && err != evtforward.ErrEvtAlreadyExist { 242 s.log.Error("unable to forward chain event", 243 logging.String("pubkey", req.PubKey), 244 logging.Error(err)) 245 if err == evtforward.ErrPubKeyNotAllowlisted { 246 return nil, apiError(codes.PermissionDenied, err) 247 } 248 return nil, apiError(codes.Internal, err) 249 } 250 251 return &protoapi.PropagateChainEventResponse{ 252 Success: ok, 253 }, nil 254 } 255 256 func verifySignature( 257 log *logging.Logger, 258 message []byte, 259 sig []byte, 260 pubKey string, 261 ) error { 262 validator, err := crypto.NewSignatureAlgorithm(crypto.Ed25519, 1) 263 if err != nil { 264 if log != nil { 265 log.Error("unable to instantiate new algorithm", logging.Error(err)) 266 } 267 return err 268 } 269 270 pubKeyBytes, err := hex.DecodeString(pubKey) 271 if err != nil { 272 if log != nil { 273 log.Error("unable to decode hexencoded ubkey", logging.Error(err)) 274 } 275 return err 276 } 277 ok, err := validator.Verify(pubKeyBytes, message, sig) 278 if err != nil { 279 if log != nil { 280 log.Error("unable to verify bundle", logging.Error(err)) 281 } 282 return err 283 } 284 if !ok { 285 return ErrInvalidSignature 286 } 287 return nil 288 } 289 290 // Statistics provides various blockchain and Vega statistics, including: 291 // Blockchain height, backlog length, current time, orders and trades per block, tendermint version 292 // Vega counts for parties, markets, order actions (amend, cancel, submit), Vega version. 293 func (s *coreService) Statistics(ctx context.Context, _ *protoapi.StatisticsRequest) (*protoapi.StatisticsResponse, error) { 294 defer metrics.StartAPIRequestAndTimeGRPC("Statistics")() 295 // Call tendermint and related services to get information for statistics 296 // We load read-only internal statistics through each package level statistics structs 297 s.svcMu.RLock() 298 epochTime := s.timesvc.GetTimeNow() 299 s.svcMu.RUnlock() 300 301 // Call tendermint via rpc client 302 var ( 303 backlogLength, numPeers int 304 gt *time.Time 305 chainID string 306 ) 307 308 backlogLength, numPeers, gt, chainID, err := s.getTendermintStats(ctx) 309 if err != nil { 310 // do not return an error, let just eventually log it 311 s.log.Debug("could not load tendermint stats", logging.Error(err)) 312 } 313 314 // If the chain is replaying then genesis time can be nil 315 genesisTime := "" 316 if gt != nil { 317 genesisTime = vegatime.Format(*gt) 318 } 319 320 stats := &protoapi.Statistics{ 321 BlockHeight: s.stats.Blockchain.Height(), 322 BlockHash: s.stats.Blockchain.Hash(), 323 BacklogLength: uint64(backlogLength), 324 TotalPeers: uint64(numPeers), 325 GenesisTime: genesisTime, 326 CurrentTime: vegatime.Format(time.Now()), 327 VegaTime: vegatime.Format(epochTime), 328 Uptime: vegatime.Format(s.stats.GetUptime()), 329 TxPerBlock: s.stats.Blockchain.TotalTxLastBatch(), 330 AverageTxBytes: s.stats.Blockchain.AverageTxSizeBytes(), 331 AverageOrdersPerBlock: s.stats.Blockchain.AverageOrdersPerBatch(), 332 TradesPerSecond: s.stats.Blockchain.TradesPerSecond(), 333 OrdersPerSecond: s.stats.Blockchain.OrdersPerSecond(), 334 Status: ptypes.ChainStatus_CHAIN_STATUS_CONNECTED, 335 AppVersionHash: s.stats.GetVersionHash(), 336 AppVersion: s.stats.GetVersion(), 337 ChainVersion: s.stats.GetChainVersion(), 338 TotalAmendOrder: s.stats.Blockchain.TotalAmendOrder(), 339 TotalCancelOrder: s.stats.Blockchain.TotalCancelOrder(), 340 TotalCreateOrder: s.stats.Blockchain.TotalCreateOrder(), 341 TotalOrders: s.stats.Blockchain.TotalOrders(), 342 TotalTrades: s.stats.Blockchain.TotalTrades(), 343 BlockDuration: s.stats.Blockchain.BlockDuration(), 344 EventCount: s.stats.Blockchain.TotalEventsLastBatch(), 345 EventsPerSecond: s.stats.Blockchain.EventsPerSecond(), 346 EpochSeq: s.stats.GetEpochSeq(), 347 EpochStartTime: vegatime.Format(s.stats.GetEpochStartTime()), 348 EpochExpiryTime: vegatime.Format(s.stats.GetEpochExpireTime()), 349 ChainId: chainID, 350 } 351 return &protoapi.StatisticsResponse{ 352 Statistics: stats, 353 }, nil 354 } 355 356 func (s *coreService) getTendermintStats( 357 ctx context.Context, 358 ) ( 359 backlogLength, numPeers int, 360 genesis *time.Time, 361 chainID string, 362 err error, 363 ) { 364 if s.stats == nil || s.stats.Blockchain == nil { 365 return 0, 0, nil, "", apiError(codes.Internal, ErrChainNotConnected) 366 } 367 368 const refused = "connection refused" 369 370 // Unconfirmed TX count == current transaction backlog length 371 backlogLength, err = s.blockchain.GetUnconfirmedTxCount(ctx) 372 if err != nil { 373 if strings.Contains(err.Error(), refused) { 374 return 0, 0, nil, "", nil 375 } 376 return 0, 0, nil, "", apiError(codes.Internal, ErrBlockchainBacklogLength, err) 377 } 378 379 if !s.hasGenesisTimeAndChainID.Load() { 380 if err = s.getGenesisTimeAndChainID(ctx); err != nil { 381 return 0, 0, nil, "", err 382 } 383 } 384 385 // Net info provides peer stats etc (block chain network info) == number of peers 386 netInfo, err := s.getTMNetInfo(ctx) 387 if err != nil { 388 return backlogLength, 0, &s.genesisTime, s.chainID, nil // nolint 389 } 390 391 return backlogLength, netInfo.NPeers, &s.genesisTime, s.chainID, nil 392 } 393 394 func (s *coreService) getTMNetInfo(_ context.Context) (tmctypes.ResultNetInfo, error) { 395 s.netInfoMu.RLock() 396 defer s.netInfoMu.RUnlock() 397 398 if s.netInfo == nil { 399 return tmctypes.ResultNetInfo{}, apiError(codes.Internal, ErrBlockchainNetworkInfo) 400 } 401 402 return *s.netInfo, nil 403 } 404 405 func (s *coreService) updateNetInfo(ctx context.Context) { 406 // update the net info every 1 minutes 407 ticker := time.NewTicker(1 * time.Minute) 408 defer ticker.Stop() 409 410 for { 411 select { 412 case <-ctx.Done(): 413 return 414 case <-ticker.C: 415 netInfo, err := s.blockchain.GetNetworkInfo(ctx) 416 if err != nil { 417 continue 418 } 419 s.netInfoMu.Lock() 420 s.netInfo = netInfo 421 s.netInfoMu.Unlock() 422 } 423 } 424 } 425 426 func (s *coreService) getGenesisTimeAndChainID(ctx context.Context) error { 427 const refused = "connection refused" 428 // just lock in here, ideally we'll come here only once, so not a big issue to lock 429 s.mu.Lock() 430 defer s.mu.Unlock() 431 432 var err error 433 // Genesis retrieves the current genesis date/time for the blockchain 434 s.genesisTime, err = s.blockchain.GetGenesisTime(ctx) 435 if err != nil { 436 if strings.Contains(err.Error(), refused) { 437 return nil 438 } 439 return apiError(codes.Internal, ErrBlockchainGenesisTime, err) 440 } 441 442 s.chainID, err = s.blockchain.GetChainID(ctx) 443 if err != nil { 444 return apiError(codes.Internal, ErrBlockchainChainID, err) 445 } 446 447 s.hasGenesisTimeAndChainID.Store(true) 448 return nil 449 } 450 451 func (s *coreService) ObserveEventBus( 452 stream protoapi.CoreService_ObserveEventBusServer, 453 ) error { 454 defer metrics.StartAPIRequestAndTimeGRPC("ObserveEventBus")() 455 456 ctx, cfunc := context.WithCancel(stream.Context()) 457 defer cfunc() 458 459 // now we start listening for a few seconds in order to get at least the very first message 460 // this will be blocking until the connection by the client is closed 461 // and we will not start processing any events until we receive the original request 462 // indicating filters and batch size. 463 req, err := s.recvEventRequest(stream) 464 if err != nil { 465 // client exited, nothing to do 466 return nil // nolint 467 } 468 469 // now we will aggregate filter out of the initial request 470 types, err := events.ProtoToInternal(req.Type...) 471 if err != nil { 472 return apiError(codes.InvalidArgument, ErrMalformedRequest, err) 473 } 474 filters := []subscribers.EventFilter{} 475 if len(req.MarketId) > 0 && len(req.PartyId) > 0 { 476 filters = append(filters, events.GetPartyAndMarketFilter(req.MarketId, req.PartyId)) 477 } else { 478 if len(req.MarketId) > 0 { 479 filters = append(filters, events.GetMarketIDFilter(req.MarketId)) 480 } 481 if len(req.PartyId) > 0 { 482 filters = append(filters, events.GetPartyIDFilter(req.PartyId)) 483 } 484 } 485 486 // here we add the cancel to the list of observer 487 // so if a protocol upgrade happen we can stop processing those 488 // and nicely do the upgrade 489 s.svcMu.Lock() 490 s.subCancels = append(s.subCancels, cfunc) 491 s.svcMu.Unlock() 492 493 // number of retries to -1 to have pretty much unlimited retries 494 ch, bCh := s.eventService.ObserveEvents(ctx, s.conf.StreamRetries, types, int(req.BatchSize), filters...) 495 defer close(bCh) 496 497 if req.BatchSize > 0 { 498 err := s.observeEventsWithAck(ctx, stream, req.BatchSize, ch, bCh) 499 return err 500 } 501 err = s.observeEvents(ctx, stream, ch) 502 return err 503 } 504 505 func (s *coreService) observeEvents( 506 ctx context.Context, 507 stream protoapi.CoreService_ObserveEventBusServer, 508 ch <-chan []*eventspb.BusEvent, 509 ) error { 510 for { 511 select { 512 case data, ok := <-ch: 513 if !ok { 514 return nil 515 } 516 resp := &protoapi.ObserveEventBusResponse{ 517 Events: data, 518 } 519 if err := stream.Send(resp); err != nil { 520 s.log.Error("Error sending event on stream", logging.Error(err)) 521 return apiError(codes.Internal, ErrStreamInternal, err) 522 } 523 case <-ctx.Done(): 524 return apiError(codes.Internal, ErrStreamInternal, ctx.Err()) 525 } 526 } 527 } 528 529 func (s *coreService) recvEventRequest( 530 stream protoapi.CoreService_ObserveEventBusServer, 531 ) (*protoapi.ObserveEventBusRequest, error) { 532 readCtx, cfunc := context.WithTimeout(stream.Context(), 5*time.Second) 533 oebCh := make(chan protoapi.ObserveEventBusRequest) 534 var err error 535 go func() { 536 defer close(oebCh) 537 nb := protoapi.ObserveEventBusRequest{} 538 if err = stream.RecvMsg(&nb); err != nil { 539 cfunc() 540 return 541 } 542 oebCh <- nb 543 }() 544 select { 545 case <-readCtx.Done(): 546 if err != nil { 547 // this means the client disconnected 548 return nil, err 549 } 550 // this mean we timedout 551 return nil, readCtx.Err() 552 case nb := <-oebCh: 553 return &nb, nil 554 } 555 } 556 557 func (s *coreService) observeEventsWithAck( 558 ctx context.Context, 559 stream protoapi.CoreService_ObserveEventBusServer, 560 batchSize int64, 561 ch <-chan []*eventspb.BusEvent, 562 bCh chan<- int, 563 ) error { 564 for { 565 select { 566 case data, ok := <-ch: 567 if !ok { 568 return nil 569 } 570 resp := &protoapi.ObserveEventBusResponse{ 571 Events: data, 572 } 573 if err := stream.Send(resp); err != nil { 574 s.log.Error("Error sending event on stream", logging.Error(err)) 575 return apiError(codes.Internal, ErrStreamInternal, err) 576 } 577 case <-ctx.Done(): 578 return apiError(codes.Internal, ErrStreamInternal, ctx.Err()) 579 } 580 581 // now we try to read again the new size / ack 582 req, err := s.recvEventRequest(stream) 583 if err != nil { 584 return err 585 } 586 587 if req.BatchSize != batchSize { 588 batchSize = req.BatchSize 589 bCh <- int(batchSize) 590 } 591 } 592 } 593 594 func (s *coreService) handleSubmitRawTxTMError(err error) error { 595 // This is Tendermint's specific error signature 596 if _, ok := err.(interface { 597 Code() uint32 598 Details() string 599 Error() string 600 }); ok { 601 s.log.Debug("unable to submit raw transaction", logging.Error(err)) 602 return apiError(codes.InvalidArgument, err) 603 } 604 s.log.Debug("unable to submit raw transaction", logging.Error(err)) 605 606 return apiError(codes.Internal, err) 607 } 608 609 func setResponseBasisContent(response *protoapi.SubmitRawTransactionResponse, code uint32, log string, data, hash bytes.HexBytes) { 610 response.TxHash = hash.String() 611 response.Code = code 612 response.Data = data.String() 613 response.Log = log 614 } 615 616 func (s *coreService) SubmitRawTransaction(ctx context.Context, req *protoapi.SubmitRawTransactionRequest) (*protoapi.SubmitRawTransactionResponse, error) { 617 startTime := time.Now() 618 defer metrics.APIRequestAndTimeGRPC("SubmitTransaction", startTime) 619 620 if req == nil { 621 return nil, apiError(codes.InvalidArgument, ErrMalformedRequest) 622 } 623 624 successResponse := &protoapi.SubmitRawTransactionResponse{Success: true} 625 switch req.Type { 626 case protoapi.SubmitRawTransactionRequest_TYPE_ASYNC: 627 txResult, err := s.blockchain.SubmitRawTransactionAsync(ctx, req.Tx) 628 if err != nil { 629 if txResult != nil { 630 return &protoapi.SubmitRawTransactionResponse{ 631 Success: false, 632 Code: txResult.Code, 633 Data: txResult.Data.String(), 634 Log: txResult.Log, 635 }, s.handleSubmitRawTxTMError(err) 636 } 637 return nil, s.handleSubmitRawTxTMError(err) 638 } 639 setResponseBasisContent(successResponse, txResult.Code, txResult.Log, txResult.Data, txResult.Hash) 640 641 case protoapi.SubmitRawTransactionRequest_TYPE_SYNC: 642 txResult, err := s.blockchain.SubmitRawTransactionSync(ctx, req.Tx) 643 if err != nil { 644 if txResult != nil { 645 return &protoapi.SubmitRawTransactionResponse{ 646 Success: false, 647 Code: txResult.Code, 648 Data: txResult.Data.String(), 649 Log: txResult.Log, 650 }, s.handleSubmitRawTxTMError(err) 651 } 652 return nil, s.handleSubmitRawTxTMError(err) 653 } 654 setResponseBasisContent(successResponse, txResult.Code, txResult.Log, txResult.Data, txResult.Hash) 655 656 case protoapi.SubmitRawTransactionRequest_TYPE_COMMIT: 657 txResult, err := s.blockchain.SubmitRawTransactionCommit(ctx, req.Tx) 658 if err != nil { 659 if txResult != nil { 660 return &protoapi.SubmitRawTransactionResponse{ 661 Success: false, 662 Code: txResult.TxResult.Code, 663 Data: string(txResult.TxResult.Data), 664 Log: txResult.TxResult.Log, 665 }, s.handleSubmitRawTxTMError(err) 666 } 667 return nil, s.handleSubmitRawTxTMError(err) 668 } 669 setResponseBasisContent(successResponse, txResult.TxResult.Code, txResult.TxResult.Log, txResult.TxResult.Data, txResult.Hash) 670 successResponse.Height = txResult.Height 671 672 default: 673 return nil, apiError(codes.InvalidArgument, errors.New("Invalid TX Type")) 674 } 675 676 return successResponse, nil 677 } 678 679 func (s *coreService) GetSpamStatistics(ctx context.Context, req *protoapi.GetSpamStatisticsRequest) (*protoapi.GetSpamStatisticsResponse, error) { 680 if req.PartyId == "" { 681 return nil, apiError(codes.InvalidArgument, ErrEmptyMissingPartyID) 682 } 683 684 if !s.hasGenesisTimeAndChainID.Load() { 685 if err := s.getGenesisTimeAndChainID(ctx); err != nil { 686 return nil, fmt.Errorf("failed to intialise chainID: %w", err) 687 } 688 } 689 690 spamStats := &protoapi.SpamStatistics{} 691 // Spam engine is not set when NullBlockChain is used 692 if s.spamEngine != nil && !reflect.ValueOf(s.spamEngine).IsNil() { 693 spamStats = s.spamEngine.GetSpamStatistics(req.PartyId) 694 } else { 695 defaultStats := &protoapi.SpamStatistic{ 696 MaxForEpoch: math.MaxUint64, 697 MinTokensRequired: "0", 698 } 699 spamStats.Delegations = defaultStats 700 spamStats.NodeAnnouncements = defaultStats 701 spamStats.Proposals = defaultStats 702 spamStats.IssueSignatures = defaultStats 703 spamStats.Transfers = defaultStats 704 spamStats.CreateReferralSet = defaultStats 705 spamStats.UpdateReferralSet = defaultStats 706 spamStats.ApplyReferralCode = defaultStats 707 spamStats.Votes = &protoapi.VoteSpamStatistics{ 708 Statistics: []*protoapi.VoteSpamStatistic{}, 709 MaxForEpoch: math.MaxUint64, 710 } 711 } 712 713 // Noop PoW Engine is used for NullBlockChain so this should be safe 714 spamStats.Pow = s.powEngine.GetSpamStatistics(req.PartyId) 715 spamStats.EpochSeq = s.stats.GetEpochSeq() 716 717 resp := &protoapi.GetSpamStatisticsResponse{ 718 ChainId: s.chainID, 719 Statistics: spamStats, 720 } 721 722 return resp, nil 723 }