Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/syscoin_merge_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//go:build unittest

// SYSCOIN: Regression coverage for asset filtering and summary pagination across upstream merges.
package api

import (
Expand Down
2 changes: 2 additions & 0 deletions api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ type ContractInfoResult struct {

// Token contains info about tokens held by an address
type Token struct {
// SYSCOIN: Both standard fields include the emitted SPTAllocated value.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve Go deprecation markers

Because this new sentence is directly adjacent to Deprecated:, both lines now form one paragraph beginning with SYSCOIN:. Go deprecation tooling only recognizes paragraphs that begin with Deprecated:, so Token.Type—and the identical addition on TokenTransfer.Type—will no longer be reported as deprecated to Go consumers. Add a blank // separator before Deprecated: or place the deprecation paragraph first.

Useful? React with 👍 / 👎.

// Deprecated: Use Standard instead.
Type bchain.TokenStandardName `json:"type" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155' | 'TRC20' | 'TRC721' | 'TRC1155' | 'SPTAllocated'" ts_doc:"@deprecated: Use standard instead."`
Standard bchain.TokenStandardName `json:"standard" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155' | 'TRC20' | 'TRC721' | 'TRC1155' | 'SPTAllocated'"`
Expand Down Expand Up @@ -328,6 +329,7 @@ func (a Tokens) Less(i, j int) bool {

// TokenTransfer contains info about a token transfer done in a transaction
type TokenTransfer struct {
// SYSCOIN: Both standard fields include the emitted SPT value.
// Deprecated: Use Standard instead.
Type bchain.TokenStandardName `json:"type" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155' | 'TRC20' | 'TRC721' | 'TRC1155' | 'SPT'" ts_doc:"@deprecated: Use standard instead."`
Standard bchain.TokenStandardName `json:"standard" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155' | 'TRC20' | 'TRC721' | 'TRC1155' | 'SPT'"`
Expand Down
2 changes: 2 additions & 0 deletions api/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,7 @@ var errFilteredScanLimit = NewAPIError("Filtered history is too large to scan, n
func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool, filter *AddressFilter, maxResults int) ([]string, error) {
var err error
txids := make([]string, 0, 4)
// SYSCOIN: Filter transaction versions against the requested asset mask.
txMatchesAssetMask := func(txid string) (bool, error) {
if filter.AssetsMask == bchain.AllMask {
return true, nil
Expand All @@ -1148,6 +1149,7 @@ func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool
}
return uint32(filter.AssetsMask)&uint32(mask) == uint32(mask), nil
}
// SYSCOIN: Asset-mask rejections share the upstream bound for filtered scans.
nonMatching := 0
skipNonMatching := func() error {
nonMatching++
Expand Down
12 changes: 10 additions & 2 deletions api/xpub.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ func (w *Worker) xpubGetAddressTxids(addrDesc bchain.AddressDescriptor, mempool
return txs, complete, nil
}

// SYSCOIN: An asset mask also prevents use of the unfiltered transaction cache.
func isUnfilteredXpubTxidFilter(filter *AddressFilter) bool {
return filter == nil || filter.FromHeight == 0 && filter.ToHeight == 0 && filter.Vout == AddressFilterVoutOff && filter.AssetsMask == bchain.AllMask
}
Expand Down Expand Up @@ -728,6 +729,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
page = 0
}
if filter == nil {
// SYSCOIN: An omitted filter includes every asset transaction type.
filter = &AddressFilter{Vout: AddressFilterVoutOff, AssetsMask: bchain.AllMask}
}
type mempoolMap struct {
Expand Down Expand Up @@ -760,7 +762,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
accountAddrDescs[string(da[i].addrDesc)] = struct{}{}
}
}
// setup filtering of txids
// SYSCOIN: Asset masks participate in confirmed transaction filtering.
var txidFilter func(txid *xpubTxid, ad *xpubAddress) bool
if !(filter.FromHeight == 0 && filter.ToHeight == 0 && filter.Vout == AddressFilterVoutOff && filter.AssetsMask == bchain.AllMask) {
toHeight := maxUint32
Expand All @@ -777,6 +779,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
return false
}
}
// SYSCOIN: Match asset transaction types from the indexed version.
if filter.AssetsMask != bchain.AllMask {
ta, err := w.db.GetTxAddresses(txid.txid)
if err != nil || ta == nil {
Expand All @@ -792,6 +795,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
}
filtered = true
}
// SYSCOIN: Apply mempool filters independently of confirmed transaction metadata.
xpubMempoolTxidFilter := func(txid *xpubTxid) bool {
if filter.FromHeight != 0 {
return false
Expand All @@ -809,6 +813,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
// process mempool, only if ToHeight is not specified
if filter.ToHeight == 0 && !filter.OnlyConfirmed {
txmMap = make(map[string]*Tx)
// SYSCOIN: Count transactions once while retaining per-address balance updates.
countedMempoolTxs := make(map[string]struct{})
addedMempoolEntries := make(map[string]struct{})
mempoolEntries = make(bchain.MempoolTxidEntries, 0)
Expand All @@ -822,6 +827,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
for _, txid := range newTxids {
// the same tx can have multiple addresses from the same xpub, get it from backend it only once
tx, foundTx := txmMap[txid.txid]
// SYSCOIN: Asset filtering needs the transaction version even for basic detail.
needTx := option != AccountDetailsBasic || filter.AssetsMask != bchain.AllMask
if option == AccountDetailsBasic && !needTx {
// Basic detail: skip per-tx loading. Count unique mempool txids
Expand All @@ -845,6 +851,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
}
// skip already confirmed txs, mempool may be out of sync
if tx.Confirmations == 0 {
// SYSCOIN: Exclude mempool transactions outside the requested asset mask.
if filter.AssetsMask != bchain.AllMask {
mask := assetMaskFromVersion(w.chainParser, tx.Version)
if mask != bchain.AllMask && uint32(filter.AssetsMask)&uint32(mask) != uint32(mask) {
Expand All @@ -857,6 +864,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
}
uBalSat.Add(&uBalSat, tx.getAddrVoutValue(ad.addrDesc))
uBalSat.Sub(&uBalSat, tx.getAddrVinValue(ad.addrDesc))
// SYSCOIN: Accumulate SPT mempool balances for each derived address.
if option > AccountDetailsBasic {
addrKey := string(ad.addrDesc)
assetMempool := xpubAssetMempool[addrKey]
Expand All @@ -867,7 +875,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
tx.addAddrVoutAssetMempool(ad.addrDesc, assetMempool)
tx.addAddrVinAssetMempool(ad.addrDesc, assetMempool)
}
// Mempool txs enter the paged sequence once, uniquely and filtered.
// SYSCOIN: Mempool txs enter the paged sequence once, uniquely and filtered.
if xpubMempoolTxidFilter(&txid) {
if _, added := addedMempoolEntries[txid.txid]; added {
continue
Expand Down
1 change: 1 addition & 0 deletions bchain/coins/blockchain_syscoin_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//go:build unittest

// SYSCOIN: Regression coverage for broadcast options and their metrics wrapper.
package coins

import (
Expand Down
2 changes: 2 additions & 0 deletions build/tools/typescriptify/typescriptify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ func TestMarkNullableIsScopedToInterface(t *testing.T) {
}
}

// SYSCOIN: Keep generated memo types consistent with the base64 JSON wire format.
func TestTransactionMemoMatchesJSONWireType(t *testing.T) {
memo := []byte{0, 1, 2, 0xff, 0x0a}
encoded, err := json.Marshal(api.Tx{Memo: memo})
Expand All @@ -164,6 +165,7 @@ func TestTransactionMemoMatchesJSONWireType(t *testing.T) {
}
}

// SYSCOIN: Preserve the emitted SPT standards in generated TypeScript unions.
func TestSyscoinTokenStandardsMatchGeneratedTypes(t *testing.T) {
for _, tc := range []struct {
name string
Expand Down
8 changes: 6 additions & 2 deletions db/rocksdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ func (d *RocksDB) GetAddrDescTransactions(addrDesc bchain.AddressDescriptor, low
if glog.V(2) {
glog.Warningf("rocksdb: addrDesc %s - mixed with %s", addrDesc, hex.EncodeToString(key))
}
// Longer indexed scripts can share even a complete descriptor as a prefix. Bound the scan, but report exhaustion so callers cannot mistake partial history for a complete result.
// SYSCOIN: Longer indexed scripts can share even a complete descriptor as a prefix. Report scan exhaustion so callers cannot mistake partial history for a complete result.
nonMatching++
if nonMatching > maxAddrDescScanNonMatching {
return errors.Errorf("address transaction scan limit exceeded for descriptor %s after %d non-matching keys", hex.EncodeToString(addrDesc), nonMatching)
Expand Down Expand Up @@ -1457,6 +1457,7 @@ func (d *RocksDB) GetTxAddressesOutput(txid string, vout uint32) (*TxOutput, err
return nil, nil
}
var l int
// SYSCOIN: Our transaction-address records include a version before the height.
if supportsSyscoinAssets(d.chainParser) {
_, l = unpackVaruint(buf) // version
}
Expand Down Expand Up @@ -1850,6 +1851,7 @@ func (d *RocksDB) packedTxInputLen(buf []byte) int {
_, n := unpackVaruint(buf[pos:]) // prev vout
pos += n
}
// SYSCOIN: Include the optional asset suffix when skipping an extended input.
if supportsSyscoinAssets(d.chainParser) {
pos += packedAssetInfoLen(buf[pos:])
}
Expand All @@ -1858,6 +1860,7 @@ func (d *RocksDB) packedTxInputLen(buf []byte) int {
al, pos := unpackVaruint(buf)
pos += int(al) // addrDesc
pos += int(buf[pos]) + 1 // value
// SYSCOIN: Basic inputs also carry the optional asset suffix.
if supportsSyscoinAssets(d.chainParser) {
pos += packedAssetInfoLen(buf[pos:])
}
Expand All @@ -1881,6 +1884,7 @@ func (d *RocksDB) packedTxOutputLen(buf []byte) int {
_, n = unpackVaruint(buf[pos:]) // spent height
pos += n
}
// SYSCOIN: Skip the asset suffix after the output's optional spending metadata.
if supportsSyscoinAssets(d.chainParser) {
pos += packedAssetInfoLen(buf[pos:])
}
Expand Down Expand Up @@ -3197,7 +3201,7 @@ func unpackAssetInfo(buf []byte) (*bchain.AssetInfo, int) {
return &bchain.AssetInfo{AssetGuid: assetGuid, ValueSat: &valueSat}, l
}

// packedAssetInfoLen skips optional Syscoin metadata without allocating its value.
// SYSCOIN: packedAssetInfoLen skips optional asset metadata without allocating its value.
// Keep in sync with appendAssetInfo/unpackAssetInfo.
func packedAssetInfoLen(buf []byte) int {
flag, pos := unpackVaruint(buf)
Expand Down
1 change: 1 addition & 0 deletions db/rocksdb_syscoin_output_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//go:build unittest

// SYSCOIN: Regression coverage for indexed output decoding, bulk indexing, and reorg restoration.
package db

import (
Expand Down
2 changes: 2 additions & 0 deletions db/rocksdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2077,10 +2077,12 @@ func TestGetAddrDescTransactions_ScanBound(t *testing.T) {
matches++
return nil
})
// SYSCOIN: Exhaustion must be reported instead of returning a successful partial history.
require.ErrorContains(t, err, "address transaction scan limit exceeded")
require.Equal(t, 0, matches, "scan must abort before reaching the trailing matching key")
}

// SYSCOIN: A complete, indexable descriptor can still prefix unrelated scripts.
func TestGetAddrDescTransactions_CompleteDescriptorScanLimit(t *testing.T) {
d := setupRocksDB(t, &testBitcoinParser{BitcoinParser: bitcoinTestnetParser()})
defer closeAndDestroyRocksDB(t, d)
Expand Down
1 change: 1 addition & 0 deletions server/ws_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type resultError struct {
}

// WsAccountInfoReq carries parameters for the 'getAccountInfo' method.
// SYSCOIN: Details also accepts the lightweight txsummary response.
type WsAccountInfoReq struct {
Descriptor string `json:"descriptor" ts_doc:"Address or XPUB descriptor to query."`
Details string `json:"details,omitempty" ts_type:"'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txsummary' | 'txslight' | 'txs'" ts_doc:"Level of detail to retrieve about the account."`
Expand Down
Loading