github.com/cryptohub-digital/blockbook-fork@v0.0.0-20230713133354-673c927af7f1/api/types.go (about) 1 package api 2 3 import ( 4 "encoding/json" 5 "errors" 6 "math/big" 7 "sort" 8 "time" 9 10 "github.com/cryptohub-digital/blockbook-fork/bchain" 11 "github.com/cryptohub-digital/blockbook-fork/bchain/coins/eth" 12 "github.com/cryptohub-digital/blockbook-fork/bchain/coins/xcb" 13 "github.com/cryptohub-digital/blockbook-fork/common" 14 "github.com/cryptohub-digital/blockbook-fork/db" 15 ) 16 17 const maxUint32 = ^uint32(0) 18 const maxInt = int(^uint(0) >> 1) 19 const maxInt64 = int64(^uint64(0) >> 1) 20 21 // AccountDetails specifies what data returns GetAddress and GetXpub calls 22 type AccountDetails int 23 24 const ( 25 // AccountDetailsBasic - only that address is indexed and some basic info 26 AccountDetailsBasic AccountDetails = iota 27 // AccountDetailsTokens - basic info + tokens 28 AccountDetailsTokens 29 // AccountDetailsTokenBalances - basic info + token with balance 30 AccountDetailsTokenBalances 31 // AccountDetailsTxidHistory - basic + token balances + txids, subject to paging 32 AccountDetailsTxidHistory 33 // AccountDetailsTxHistoryLight - basic + tokens + easily obtained tx data (not requiring requests to backend), subject to paging 34 AccountDetailsTxHistoryLight 35 // AccountDetailsTxHistory - basic + tokens + full tx data, subject to paging 36 AccountDetailsTxHistory 37 ) 38 39 // ErrUnsupportedXpub is returned when coin type does not support xpub address derivation or provided string is not an xpub 40 var ErrUnsupportedXpub = errors.New("XPUB not supported") 41 42 // APIError extends error by information if the error details should be returned to the end user 43 type APIError struct { 44 Text string 45 Public bool 46 } 47 48 func (e *APIError) Error() string { 49 return e.Text 50 } 51 52 // NewAPIError creates ApiError 53 func NewAPIError(s string, public bool) error { 54 return &APIError{ 55 Text: s, 56 Public: public, 57 } 58 } 59 60 // Amount is datatype holding amounts 61 type Amount big.Int 62 63 // IsZeroBigInt if big int has zero value 64 func IsZeroBigInt(b *big.Int) bool { 65 return len(b.Bits()) == 0 66 } 67 68 // Compare returns an integer comparing two Amounts. The result will be 0 if a == b, -1 if a < b, and +1 if a > b. 69 // Nil Amount is always less then non nil amount, two nil Amounts are equal 70 func (a *Amount) Compare(b *Amount) int { 71 if b == nil { 72 if a == nil { 73 return 0 74 } 75 return 1 76 } 77 if a == nil { 78 return -1 79 } 80 return (*big.Int)(a).Cmp((*big.Int)(b)) 81 } 82 83 // MarshalJSON Amount serialization 84 func (a *Amount) MarshalJSON() (out []byte, err error) { 85 if a == nil { 86 return []byte(`"0"`), nil 87 } 88 return []byte(`"` + (*big.Int)(a).String() + `"`), nil 89 } 90 91 func (a *Amount) String() string { 92 if a == nil { 93 return "" 94 } 95 return (*big.Int)(a).String() 96 } 97 98 // DecimalString returns amount with decimal point placed according to parameter d 99 func (a *Amount) DecimalString(d int) string { 100 return bchain.AmountToDecimalString((*big.Int)(a), d) 101 } 102 103 // AsBigInt returns big.Int type for the Amount (empty if Amount is nil) 104 func (a *Amount) AsBigInt() big.Int { 105 if a == nil { 106 return *new(big.Int) 107 } 108 return big.Int(*a) 109 } 110 111 // AsInt64 returns Amount as int64 (0 if Amount is nil). 112 // It is used only for legacy interfaces (socket.io) 113 // and generally not recommended to use for possible loss of precision. 114 func (a *Amount) AsInt64() int64 { 115 if a == nil { 116 return 0 117 } 118 return (*big.Int)(a).Int64() 119 } 120 121 // Vin contains information about single transaction input 122 type Vin struct { 123 Txid string `json:"txid,omitempty"` 124 Vout uint32 `json:"vout,omitempty"` 125 Sequence int64 `json:"sequence,omitempty"` 126 N int `json:"n"` 127 AddrDesc bchain.AddressDescriptor `json:"-"` 128 Addresses []string `json:"addresses,omitempty"` 129 IsAddress bool `json:"isAddress"` 130 IsOwn bool `json:"isOwn,omitempty"` 131 ValueSat *Amount `json:"value,omitempty"` 132 Hex string `json:"hex,omitempty"` 133 Asm string `json:"asm,omitempty"` 134 Coinbase string `json:"coinbase,omitempty"` 135 } 136 137 // Vout contains information about single transaction output 138 type Vout struct { 139 ValueSat *Amount `json:"value,omitempty"` 140 N int `json:"n"` 141 Spent bool `json:"spent,omitempty"` 142 SpentTxID string `json:"spentTxId,omitempty"` 143 SpentIndex int `json:"spentIndex,omitempty"` 144 SpentHeight int `json:"spentHeight,omitempty"` 145 Hex string `json:"hex,omitempty"` 146 Asm string `json:"asm,omitempty"` 147 AddrDesc bchain.AddressDescriptor `json:"-"` 148 Addresses []string `json:"addresses"` 149 IsAddress bool `json:"isAddress"` 150 IsOwn bool `json:"isOwn,omitempty"` 151 Type string `json:"type,omitempty"` 152 } 153 154 // MultiTokenValue contains values for contract with id and value (like ERC1155) 155 type MultiTokenValue struct { 156 Id *Amount `json:"id,omitempty"` 157 Value *Amount `json:"value,omitempty"` 158 } 159 160 // Token contains info about tokens held by an address 161 type Token struct { 162 Type bchain.TokenTypeName `json:"type" ts_type:"'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155'"` 163 Name string `json:"name"` 164 Path string `json:"path,omitempty"` 165 Contract string `json:"contract,omitempty"` 166 Transfers int `json:"transfers"` 167 Symbol string `json:"symbol,omitempty"` 168 Decimals int `json:"decimals,omitempty"` 169 BalanceSat *Amount `json:"balance,omitempty"` 170 BaseValue float64 `json:"baseValue,omitempty"` // value in the base currency (ETH for Ethereum) 171 SecondaryValue float64 `json:"secondaryValue,omitempty"` // value in secondary (fiat) currency, if specified 172 Ids []Amount `json:"ids,omitempty"` // multiple ERC721 tokens 173 MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` // multiple ERC1155 tokens 174 TotalReceivedSat *Amount `json:"totalReceived,omitempty"` 175 TotalSentSat *Amount `json:"totalSent,omitempty"` 176 ContractIndex string `json:"-"` 177 } 178 179 // Tokens is array of Token 180 type Tokens []Token 181 182 func (a Tokens) Len() int { return len(a) } 183 func (a Tokens) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 184 func (a Tokens) Less(i, j int) bool { 185 ti := &a[i] 186 tj := &a[j] 187 // sort by BaseValue descending and then Name and then by Contract 188 if ti.BaseValue < tj.BaseValue { 189 return false 190 } else if ti.BaseValue > tj.BaseValue { 191 return true 192 } 193 if ti.Name == "" { 194 if tj.Name != "" { 195 return false 196 } 197 } else { 198 if tj.Name == "" { 199 return true 200 } 201 return ti.Name < tj.Name 202 } 203 return ti.Contract < tj.Contract 204 } 205 206 // TokenTransfer contains info about a token transfer done in a transaction 207 type TokenTransfer struct { 208 Type bchain.TokenTypeName `json:"type"` 209 From string `json:"from"` 210 To string `json:"to"` 211 Contract string `json:"contract"` 212 Name string `json:"name"` 213 Symbol string `json:"symbol"` 214 Decimals int `json:"decimals"` 215 Value *Amount `json:"value,omitempty"` 216 MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` 217 } 218 219 type EthereumInternalTransfer struct { 220 Type bchain.EthereumInternalTransactionType `json:"type"` 221 From string `json:"from"` 222 To string `json:"to"` 223 Value *Amount `json:"value"` 224 } 225 226 // EthereumSpecific contains ethereum specific transaction data 227 type EthereumSpecific struct { 228 Type bchain.EthereumInternalTransactionType `json:"type,omitempty"` 229 CreatedContract string `json:"createdContract,omitempty"` 230 Status eth.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending 231 Error string `json:"error,omitempty"` 232 Nonce uint64 `json:"nonce"` 233 GasLimit *big.Int `json:"gasLimit"` 234 GasUsed *big.Int `json:"gasUsed,omitempty"` 235 GasPrice *Amount `json:"gasPrice"` 236 Data string `json:"data,omitempty"` 237 ParsedData *bchain.EthereumParsedInputData `json:"parsedData,omitempty"` 238 InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"` 239 } 240 241 // CoreCoinSpecific contains core coin specific transaction data 242 type CoreCoinSpecific struct { 243 Status xcb.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending 244 Nonce uint64 `json:"nonce"` 245 EnergyLimit *big.Int `json:"energyLimit"` 246 EnergyUsed *big.Int `json:"energyUsed,omitempty"` 247 EnergyPrice *Amount `json:"energyPrice"` 248 Data string `json:"data,omitempty"` 249 } 250 251 type AddressAlias struct { 252 Type string 253 Alias string 254 } 255 type AddressAliasesMap map[string]AddressAlias 256 257 // Tx holds information about a transaction 258 type Tx struct { 259 Txid string `json:"txid"` 260 Version int32 `json:"version,omitempty"` 261 Locktime uint32 `json:"lockTime,omitempty"` 262 Vin []Vin `json:"vin"` 263 Vout []Vout `json:"vout"` 264 Blockhash string `json:"blockHash,omitempty"` 265 Blockheight int `json:"blockHeight"` 266 Confirmations uint32 `json:"confirmations"` 267 ConfirmationETABlocks uint32 `json:"confirmationETABlocks,omitempty"` 268 ConfirmationETASeconds int64 `json:"confirmationETASeconds,omitempty"` 269 Blocktime int64 `json:"blockTime"` 270 Size int `json:"size,omitempty"` 271 VSize int `json:"vsize,omitempty"` 272 ValueOutSat *Amount `json:"value"` 273 ValueInSat *Amount `json:"valueIn,omitempty"` 274 FeesSat *Amount `json:"fees,omitempty"` 275 Hex string `json:"hex,omitempty"` 276 Rbf bool `json:"rbf,omitempty"` 277 CoinSpecificData json.RawMessage `json:"coinSpecificData,omitempty" ts_type:"any"` 278 TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty"` 279 EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty"` 280 CoreCoinSpecific *CoreCoinSpecific `json:"corecoinSpecific,omitempty"` 281 AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` 282 } 283 284 // FeeStats contains detailed block fee statistics 285 type FeeStats struct { 286 TxCount int `json:"txCount"` 287 TotalFeesSat *Amount `json:"totalFeesSat"` 288 AverageFeePerKb int64 `json:"averageFeePerKb"` 289 DecilesFeePerKb [11]int64 `json:"decilesFeePerKb"` 290 } 291 292 // Paging contains information about paging for address, blocks and block 293 type Paging struct { 294 Page int `json:"page,omitempty"` 295 TotalPages int `json:"totalPages,omitempty"` 296 ItemsOnPage int `json:"itemsOnPage,omitempty"` 297 } 298 299 // TokensToReturn specifies what tokens are returned by GetAddress and GetXpubAddress 300 type TokensToReturn int 301 302 const ( 303 // AddressFilterVoutOff disables filtering of transactions by vout 304 AddressFilterVoutOff = -1 305 // AddressFilterVoutInputs specifies that only txs where the address is as input are returned 306 AddressFilterVoutInputs = -2 307 // AddressFilterVoutOutputs specifies that only txs where the address is as output are returned 308 AddressFilterVoutOutputs = -3 309 // AddressFilterVoutQueryNotNecessary signals that query for transactions is not necessary as there are no transactions for specified contract filter 310 AddressFilterVoutQueryNotNecessary = -4 311 312 // TokensToReturnNonzeroBalance - return only tokens with nonzero balance 313 TokensToReturnNonzeroBalance TokensToReturn = 0 314 // TokensToReturnUsed - return tokens with some transfers (even if they have zero balance now) 315 TokensToReturnUsed TokensToReturn = 1 316 // TokensToReturnDerived - return all derived tokens 317 TokensToReturnDerived TokensToReturn = 2 318 ) 319 320 // AddressFilter is used to filter data returned from GetAddress api method 321 type AddressFilter struct { 322 Vout int 323 Contract string 324 FromHeight uint32 325 ToHeight uint32 326 TokensToReturn TokensToReturn 327 // OnlyConfirmed set to true will ignore mempool transactions; mempool is also ignored if FromHeight/ToHeight filter is specified 328 OnlyConfirmed bool 329 } 330 331 // Address holds information about address and its transactions 332 type Address struct { 333 Paging 334 AddrStr string `json:"address"` 335 BalanceSat *Amount `json:"balance"` 336 TotalReceivedSat *Amount `json:"totalReceived,omitempty"` 337 TotalSentSat *Amount `json:"totalSent,omitempty"` 338 UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance"` 339 UnconfirmedTxs int `json:"unconfirmedTxs"` 340 Txs int `json:"txs"` 341 AddrTxCount int `json:"addrTxCount,omitempty"` 342 NonTokenTxs int `json:"nonTokenTxs,omitempty"` 343 InternalTxs int `json:"internalTxs,omitempty"` 344 Transactions []*Tx `json:"transactions,omitempty"` 345 Txids []string `json:"txids,omitempty"` 346 Nonce string `json:"nonce,omitempty"` 347 UsedTokens int `json:"usedTokens,omitempty"` 348 Tokens Tokens `json:"tokens,omitempty"` 349 SecondaryValue float64 `json:"secondaryValue,omitempty"` // address value in secondary currency 350 TokensBaseValue float64 `json:"tokensBaseValue,omitempty"` 351 TokensSecondaryValue float64 `json:"tokensSecondaryValue,omitempty"` 352 TotalBaseValue float64 `json:"totalBaseValue,omitempty"` // value including tokens in base currency 353 TotalSecondaryValue float64 `json:"totalSecondaryValue,omitempty"` // value including tokens in secondary currency 354 ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"` 355 Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty"` // deprecated 356 AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` 357 // helpers for explorer 358 Filter string `json:"-"` 359 XPubAddresses map[string]struct{} `json:"-"` 360 } 361 362 // Utxo is one unspent transaction output 363 type Utxo struct { 364 Txid string `json:"txid"` 365 Vout int32 `json:"vout"` 366 AmountSat *Amount `json:"value"` 367 Height int `json:"height,omitempty"` 368 Confirmations int `json:"confirmations"` 369 Address string `json:"address,omitempty"` 370 Path string `json:"path,omitempty"` 371 Locktime uint32 `json:"lockTime,omitempty"` 372 Coinbase bool `json:"coinbase,omitempty"` 373 } 374 375 // Utxos is array of Utxo 376 type Utxos []Utxo 377 378 func (a Utxos) Len() int { return len(a) } 379 func (a Utxos) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 380 func (a Utxos) Less(i, j int) bool { 381 // sort in reverse order, unconfirmed (height==0) utxos on top 382 hi := a[i].Height 383 hj := a[j].Height 384 if hi == 0 { 385 hi = maxInt 386 } 387 if hj == 0 { 388 hj = maxInt 389 } 390 return hi >= hj 391 } 392 393 // BalanceHistory contains info about one point in time of balance history 394 type BalanceHistory struct { 395 Time uint32 `json:"time"` 396 Txs uint32 `json:"txs"` 397 ReceivedSat *Amount `json:"received"` 398 SentSat *Amount `json:"sent"` 399 SentToSelfSat *Amount `json:"sentToSelf"` 400 FiatRates map[string]float32 `json:"rates,omitempty"` 401 Txid string `json:"txid,omitempty"` 402 } 403 404 // BalanceHistories is array of BalanceHistory 405 type BalanceHistories []BalanceHistory 406 407 func (a BalanceHistories) Len() int { return len(a) } 408 func (a BalanceHistories) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 409 func (a BalanceHistories) Less(i, j int) bool { 410 ti := a[i].Time 411 tj := a[j].Time 412 if ti == tj { 413 return a[i].Txid < a[j].Txid 414 } 415 return ti < tj 416 } 417 418 // SortAndAggregate sums BalanceHistories to groups defined by parameter groupByTime 419 func (a BalanceHistories) SortAndAggregate(groupByTime uint32) BalanceHistories { 420 bhs := make(BalanceHistories, 0) 421 if len(a) > 0 { 422 bha := BalanceHistory{ 423 ReceivedSat: &Amount{}, 424 SentSat: &Amount{}, 425 SentToSelfSat: &Amount{}, 426 } 427 sort.Sort(a) 428 for i := range a { 429 bh := &a[i] 430 time := bh.Time - bh.Time%groupByTime 431 if bha.Time != time { 432 if bha.Time != 0 { 433 // in aggregate, do not return txid as it could multiple of them 434 bha.Txid = "" 435 bhs = append(bhs, bha) 436 } 437 bha = BalanceHistory{ 438 Time: time, 439 ReceivedSat: &Amount{}, 440 SentSat: &Amount{}, 441 SentToSelfSat: &Amount{}, 442 } 443 } 444 if bha.Txid != bh.Txid { 445 bha.Txs += bh.Txs 446 bha.Txid = bh.Txid 447 } 448 (*big.Int)(bha.ReceivedSat).Add((*big.Int)(bha.ReceivedSat), (*big.Int)(bh.ReceivedSat)) 449 (*big.Int)(bha.SentSat).Add((*big.Int)(bha.SentSat), (*big.Int)(bh.SentSat)) 450 (*big.Int)(bha.SentToSelfSat).Add((*big.Int)(bha.SentToSelfSat), (*big.Int)(bh.SentToSelfSat)) 451 } 452 if bha.Txs > 0 { 453 bha.Txid = "" 454 bhs = append(bhs, bha) 455 } 456 } 457 return bhs 458 } 459 460 // Blocks is list of blocks with paging information 461 type Blocks struct { 462 Paging 463 Blocks []db.BlockInfo `json:"blocks"` 464 } 465 466 // BlockInfo contains extended block header data and a list of block txids 467 type BlockInfo struct { 468 Hash string `json:"hash"` 469 Prev string `json:"previousBlockHash,omitempty"` 470 Next string `json:"nextBlockHash,omitempty"` 471 Height uint32 `json:"height"` 472 Confirmations int `json:"confirmations"` 473 Size int `json:"size"` 474 Time int64 `json:"time,omitempty"` 475 Version common.JSONNumber `json:"version"` 476 MerkleRoot string `json:"merkleRoot"` 477 Nonce string `json:"nonce"` 478 Bits string `json:"bits"` 479 Difficulty string `json:"difficulty"` 480 Txids []string `json:"tx,omitempty"` 481 } 482 483 // Block contains information about block 484 type Block struct { 485 Paging 486 BlockInfo 487 TxCount int `json:"txCount"` 488 Transactions []*Tx `json:"txs,omitempty"` 489 AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` 490 } 491 492 // BlockRaw contains raw block in hex 493 type BlockRaw struct { 494 Hex string `json:"hex"` 495 } 496 497 // BlockbookInfo contains information about the running blockbook instance 498 type BlockbookInfo struct { 499 Coin string `json:"coin"` 500 Host string `json:"host"` 501 Version string `json:"version"` 502 GitCommit string `json:"gitCommit"` 503 BuildTime string `json:"buildTime"` 504 SyncMode bool `json:"syncMode"` 505 InitialSync bool `json:"initialSync"` 506 InSync bool `json:"inSync"` 507 BestHeight uint32 `json:"bestHeight"` 508 LastBlockTime time.Time `json:"lastBlockTime"` 509 InSyncMempool bool `json:"inSyncMempool"` 510 LastMempoolTime time.Time `json:"lastMempoolTime"` 511 MempoolSize int `json:"mempoolSize"` 512 Decimals int `json:"decimals"` 513 DbSize int64 `json:"dbSize"` 514 HasFiatRates bool `json:"hasFiatRates,omitempty"` 515 HasTokenFiatRates bool `json:"hasTokenFiatRates,omitempty"` 516 CurrentFiatRatesTime *time.Time `json:"currentFiatRatesTime,omitempty"` 517 HistoricalFiatRatesTime *time.Time `json:"historicalFiatRatesTime,omitempty"` 518 HistoricalTokenFiatRatesTime *time.Time `json:"historicalTokenFiatRatesTime,omitempty"` 519 DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty"` 520 DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty"` 521 About string `json:"about"` 522 } 523 524 // SystemInfo contains information about the running blockbook and backend instance 525 type SystemInfo struct { 526 Blockbook *BlockbookInfo `json:"blockbook"` 527 Backend *common.BackendInfo `json:"backend"` 528 } 529 530 // MempoolTxid contains information about a transaction in mempool 531 type MempoolTxid struct { 532 Time int64 `json:"time"` 533 Txid string `json:"txid"` 534 } 535 536 // MempoolTxids contains a list of mempool txids with paging information 537 type MempoolTxids struct { 538 Paging 539 Mempool []MempoolTxid `json:"mempool"` 540 MempoolSize int `json:"mempoolSize"` 541 } 542 543 // FiatTicker contains formatted CurrencyRatesTicker data 544 type FiatTicker struct { 545 Timestamp int64 `json:"ts,omitempty"` 546 Rates map[string]float32 `json:"rates"` 547 Error string `json:"error,omitempty"` 548 } 549 550 // FiatTickers contains a formatted CurrencyRatesTicker list 551 type FiatTickers struct { 552 Tickers []FiatTicker `json:"tickers"` 553 } 554 555 // AvailableVsCurrencies contains formatted data about available versus currencies for exchange rates 556 type AvailableVsCurrencies struct { 557 Timestamp int64 `json:"ts,omitempty"` 558 Tickers []string `json:"available_currencies"` 559 Error string `json:"error,omitempty"` 560 }