diff --git a/api/openapi.json b/api/openapi.json index 5756ffcc..ec757345 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -5225,7 +5225,7 @@ }, "sponsored": { "default": false, - "description": "true — the Battery relay pays gas for this transaction; submit it via /v2/gasless/send. false — self-paid; sign and broadcast via /v2/blockchain/message as usual. The final TON sweep is always self-paid, whatever gas_payer says: the relay does not sponsor a TON-only batch, and gasless has no jetton balance left to bill a commission against.\n", + "description": "true — the Battery relay pays gas for this transaction; submit it via /v2/gasless/send. false — self-paid; sign and broadcast via /v2/blockchain/message as usual (e.g. the final TON sweep).\n", "type": "boolean" }, "state_init": { @@ -5787,10 +5787,6 @@ "trust": { "$ref": "#/components/schemas/TrustType" }, - "trust_v2": { - "$ref": "#/components/schemas/TrustType", - "description": "Corrected trust classification: an item that is neither whitelisted, graylisted, nor blacklisted is TrustNone here (trust still reports TrustBlacklist for that case, for backward compatibility with older clients)." - }, "verified": { "description": "Collection master contract confirmed that this item is part of collection", "example": true, diff --git a/api/openapi.yml b/api/openapi.yml index 104820db..f73593ac 100644 --- a/api/openapi.yml +++ b/api/openapi.yml @@ -6628,12 +6628,6 @@ components: example: false trust: $ref: '#/components/schemas/TrustType' - trust_v2: - $ref: '#/components/schemas/TrustType' - description: >- - Corrected trust classification: an item that is neither whitelisted, graylisted, nor - blacklisted is TrustNone here (trust still reports TrustBlacklist for that case, for - backward compatibility with older clients). code_hash: type: string description: Hash of the NFT item account code cell (hex) diff --git a/pkg/api/handler.go b/pkg/api/handler.go index d5152bc9..27ab8105 100644 --- a/pkg/api/handler.go +++ b/pkg/api/handler.go @@ -60,6 +60,8 @@ type Handler struct { // parallelTraceProcessing enables parallel trace-to-action conversion. parallelTraceProcessing bool + // nftTrustNoneEnabled reports an unreviewed NFT item as TrustNone instead of TrustBlacklist. + nftTrustNoneEnabled bool // mempoolEmulate contains results of emulation of messages that are in the mempool. mempoolEmulate mempoolEmulate // ctxToDetails converts a request context to a details instance. @@ -106,6 +108,7 @@ type Options struct { defiAssets defiAssetsSource score scoreSource parallelTraceProcessing bool + nftTrustNoneEnabled bool archiveLiteServers []config.LiteServer archiveClient rewards.LiteClient publicAPIURL string @@ -205,6 +208,12 @@ func WithParallelTraceProcessing(enabled bool) Option { } } +func WithNftTrustNoneEnabled(enabled bool) Option { + return func(o *Options) { + o.nftTrustNoneEnabled = enabled + } +} + func WithArchiveLiteServers(s []config.LiteServer) Option { return func(o *Options) { o.archiveLiteServers = s @@ -336,6 +345,7 @@ func NewHandler(logger *zap.Logger, opts ...Option) (*Handler, error) { tongo.MustParseAddress("0:0000000000000000000000000000000000000000000000000000000000000000").ID: {}, }, parallelTraceProcessing: options.parallelTraceProcessing, + nftTrustNoneEnabled: options.nftTrustNoneEnabled, tongoVersion: tongoVersion, blacklistedBocCache: cache.NewLRUCache[[32]byte, struct{}](100000, "blacklisted_boc_cache"), getMethodsCache: cache.NewLRUCache[string, *oas.MethodExecutionResult](100000, "get_methods_cache"), diff --git a/pkg/api/nft_converters.go b/pkg/api/nft_converters.go index 84c528a5..5406eee3 100644 --- a/pkg/api/nft_converters.go +++ b/pkg/api/nft_converters.go @@ -86,22 +86,19 @@ func (h *Handler) convertNFT(ctx context.Context, item core.NftItem, book addres switch { case len(nftItem.ApprovedBy) > 0 && nftItem.Verified: nftItem.Trust = oas.TrustType(core.TrustWhitelist) - nftItem.TrustV2.SetTo(oas.TrustType(core.TrustWhitelist)) case trustType == core.TrustWhitelist || trustType == core.TrustGraylist: // The item has been reviewed and cleared (support graylisted it, for instance). That // verdict wins over whatever the spam filter's heuristics would otherwise return. nftItem.Trust = oas.TrustType(trustType) - nftItem.TrustV2.SetTo(oas.TrustType(trustType)) default: nftTrust := h.spamFilter.NftTrust(item.Address, item.CollectionAddress, item.OwnerAddress, collectionTrust, name, description, image) if nftTrust == core.TrustNone && trustType != "" { nftTrust = trustType } - nftItem.TrustV2.SetTo(oas.TrustType(nftTrust)) - // trust keeps the historical contract for clients that can't update instantly: an item - // nothing vouches for is blacklisted so they keep blurring it. trust_v2 carries the - // corrected TrustNone value above. - if nftTrust == core.TrustNone { + if nftTrust == core.TrustNone && !h.nftTrustNoneEnabled { + // Deployments that can't update their client instantly (e.g. mobile) keep the old + // contract: an item nothing vouches for is blacklisted so they keep blurring it. + // Flip NFT_TRUST_NONE_ENABLED once the client handles TrustNone correctly. nftTrust = core.TrustBlacklist } nftItem.Trust = oas.TrustType(nftTrust) diff --git a/pkg/api/nft_converters_test.go b/pkg/api/nft_converters_test.go index 4f188f81..9d624232 100644 --- a/pkg/api/nft_converters_test.go +++ b/pkg/api/nft_converters_test.go @@ -26,10 +26,10 @@ func newTestMetaCache(collections map[ton.AccountID]collectionMeta) metadataCach } // TestConvertNFTTrust locks in how the NFT trust sources are combined: an item nothing vouches -// for is blacklisted in the legacy trust field (so older clients keep blurring it) but TrustNone -// in trust_v2 (the corrected value, for clients that can read it); an item inherits the trust of -// its collection; and a review — support graylisting the item, or the address book approving it — -// keeps it visible in both fields. +// for resolves to TrustBlacklist by default (nftTrustNoneEnabled off, e.g. mobile) or TrustNone +// once nftTrustNoneEnabled is on (e.g. web); an item inherits the trust of its collection; and a +// review — support graylisting the item, or the address book approving it — keeps it visible +// regardless of the flag. func TestConvertNFTTrust(t *testing.T) { nftID := ton.MustParseAccountID("EQCNmNR28mDfkwn4bwAlwJ1uhEFnjSQTZ3REz9d7IGZXU9EZ") collectionID := ton.MustParseAccountID("EQDaaxtmY6Dk0YzIV0zNnbUpbjZ92TJHBvO72esc0srwv8K2") @@ -39,75 +39,78 @@ func TestConvertNFTTrust(t *testing.T) { }) tests := []struct { - name string - collection *ton.AccountID - collectionTrust core.TrustType - trustType core.TrustType - expectedTrust oas.TrustType - expectedTrustV2 oas.TrustType + name string + collection *ton.AccountID + collectionTrust core.TrustType + trustType core.TrustType + nftTrustNoneEnabled bool + expectedTrust oas.TrustType }{ { - name: "NFT without a collection is unreviewed", - collection: nil, - expectedTrust: oas.TrustType(core.TrustBlacklist), - expectedTrustV2: oas.TrustType(core.TrustNone), + name: "NFT without a collection is unreviewed", + collection: nil, + expectedTrust: oas.TrustType(core.TrustBlacklist), }, { - name: "NFT in an unreviewed collection is unreviewed", - collection: &collectionID, - expectedTrust: oas.TrustType(core.TrustBlacklist), - expectedTrustV2: oas.TrustType(core.TrustNone), + name: "NFT in an unreviewed collection is unreviewed", + collection: &collectionID, + expectedTrust: oas.TrustType(core.TrustBlacklist), + }, + { + name: "NFT without a collection is TrustNone once nftTrustNoneEnabled is on", + collection: nil, + nftTrustNoneEnabled: true, + expectedTrust: oas.TrustType(core.TrustNone), + }, + { + name: "NFT in an unreviewed collection is TrustNone once nftTrustNoneEnabled is on", + collection: &collectionID, + nftTrustNoneEnabled: true, + expectedTrust: oas.TrustType(core.TrustNone), }, { name: "NFT inherits a blacklisted collection", collection: &collectionID, collectionTrust: core.TrustBlacklist, expectedTrust: oas.TrustType(core.TrustBlacklist), - expectedTrustV2: oas.TrustType(core.TrustBlacklist), }, { name: "NFT inherits a graylisted collection", collection: &collectionID, collectionTrust: core.TrustGraylist, expectedTrust: oas.TrustType(core.TrustGraylist), - expectedTrustV2: oas.TrustType(core.TrustGraylist), }, { - name: "a blacklisted item stays blacklisted", - collection: &collectionID, - trustType: core.TrustBlacklist, - expectedTrust: oas.TrustType(core.TrustBlacklist), - expectedTrustV2: oas.TrustType(core.TrustBlacklist), + name: "a blacklisted item stays blacklisted", + collection: &collectionID, + trustType: core.TrustBlacklist, + expectedTrust: oas.TrustType(core.TrustBlacklist), }, { - name: "a graylisted item stays visible even though nothing else vouches for it", - collection: &collectionID, - trustType: core.TrustGraylist, - expectedTrust: oas.TrustType(core.TrustGraylist), - expectedTrustV2: oas.TrustType(core.TrustGraylist), + name: "a graylisted item stays visible even though nothing else vouches for it", + collection: &collectionID, + trustType: core.TrustGraylist, + expectedTrust: oas.TrustType(core.TrustGraylist), }, { - name: "a whitelisted item stays visible even without a collection", - collection: nil, - trustType: core.TrustWhitelist, - expectedTrust: oas.TrustType(core.TrustWhitelist), - expectedTrustV2: oas.TrustType(core.TrustWhitelist), + name: "a whitelisted item stays visible even without a collection", + collection: nil, + trustType: core.TrustWhitelist, + expectedTrust: oas.TrustType(core.TrustWhitelist), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { h := &Handler{ - addressBook: mockAddressBook{}, - spamFilter: mockSpamFilter{collectionTrust: tt.collectionTrust}, - metaCache: metaCache, + addressBook: mockAddressBook{}, + spamFilter: mockSpamFilter{collectionTrust: tt.collectionTrust}, + metaCache: metaCache, + nftTrustNoneEnabled: tt.nftTrustNoneEnabled, } item := core.NftItem{Address: nftID, CollectionAddress: tt.collection} got := h.convertNFT(context.Background(), item, h.addressBook, h.metaCache, tt.trustType) assert.Equal(t, tt.expectedTrust, got.Trust) - gotTrustV2, ok := got.TrustV2.Get() - assert.True(t, ok, "trust_v2 should always be set") - assert.Equal(t, tt.expectedTrustV2, gotTrustV2) }) } } diff --git a/pkg/oas/oas_json_gen.go b/pkg/oas/oas_json_gen.go index 64e22366..7f3cd9a7 100644 --- a/pkg/oas/oas_json_gen.go +++ b/pkg/oas/oas_json_gen.go @@ -33772,12 +33772,6 @@ func (s *NftItem) encodeFields(e *jx.Encoder) { e.FieldStart("trust") s.Trust.Encode(e) } - { - if s.TrustV2.Set { - e.FieldStart("trust_v2") - s.TrustV2.Encode(e) - } - } { if s.CodeHash.Set { e.FieldStart("code_hash") @@ -33792,7 +33786,7 @@ func (s *NftItem) encodeFields(e *jx.Encoder) { } } -var jsonFieldsNameOfNftItem = [15]string{ +var jsonFieldsNameOfNftItem = [14]string{ 0: "address", 1: "index", 2: "owner", @@ -33805,9 +33799,8 @@ var jsonFieldsNameOfNftItem = [15]string{ 9: "approved_by", 10: "include_cnft", 11: "trust", - 12: "trust_v2", - 13: "code_hash", - 14: "data_hash", + 12: "code_hash", + 13: "data_hash", } // Decode decodes NftItem from json. @@ -33952,16 +33945,6 @@ func (s *NftItem) Decode(d *jx.Decoder) error { }(); err != nil { return errors.Wrap(err, "decode field \"trust\"") } - case "trust_v2": - if err := func() error { - s.TrustV2.Reset() - if err := s.TrustV2.Decode(d); err != nil { - return err - } - return nil - }(); err != nil { - return errors.Wrap(err, "decode field \"trust_v2\"") - } case "code_hash": if err := func() error { s.CodeHash.Reset() @@ -39296,39 +39279,6 @@ func (s *OptTonTransferAction) UnmarshalJSON(data []byte) error { return s.Decode(d) } -// Encode encodes TrustType as json. -func (o OptTrustType) Encode(e *jx.Encoder) { - if !o.Set { - return - } - e.Str(string(o.Value)) -} - -// Decode decodes TrustType from json. -func (o *OptTrustType) Decode(d *jx.Decoder) error { - if o == nil { - return errors.New("invalid: unable to decode OptTrustType to nil") - } - o.Set = true - if err := o.Value.Decode(d); err != nil { - return err - } - return nil -} - -// MarshalJSON implements stdjson.Marshaler. -func (s OptTrustType) MarshalJSON() ([]byte, error) { - e := jx.Encoder{} - s.Encode(&e) - return e.Bytes(), nil -} - -// UnmarshalJSON implements stdjson.Unmarshaler. -func (s *OptTrustType) UnmarshalJSON(data []byte) error { - d := jx.DecodeBytes(data) - return s.Decode(d) -} - // Encode encodes uint32 as json. func (o OptUint32) Encode(e *jx.Encoder) { if !o.Set { diff --git a/pkg/oas/oas_schemas_gen.go b/pkg/oas/oas_schemas_gen.go index f060fbf7..c3f27b3c 100644 --- a/pkg/oas/oas_schemas_gen.go +++ b/pkg/oas/oas_schemas_gen.go @@ -10887,9 +10887,7 @@ type MigrationTransaction struct { // body — sign and wrap it for /v2/gasless/send as in the gasless flow. Boc string `json:"boc"` // True — the Battery relay pays gas for this transaction; submit it via /v2/gasless/send. false - // — self-paid; sign and broadcast via /v2/blockchain/message as usual. The final TON sweep is - // always self-paid, whatever gas_payer says: the relay does not sponsor a TON-only batch, and - // gasless has no jetton balance left to bill a commission against. + // — self-paid; sign and broadcast via /v2/blockchain/message as usual (e.g. the final TON sweep). Sponsored OptBool `json:"sponsored"` // Gasless only; the relay commission in indivisible gas-jetton units, embedded in the boc as a // jetton transfer to the relay. Exact for the first transaction; an estimate for later ones @@ -11849,10 +11847,6 @@ type NftItem struct { ApprovedBy NftApprovedBy `json:"approved_by"` IncludeCnft OptBool `json:"include_cnft"` Trust TrustType `json:"trust"` - // Corrected trust classification: an item that is neither whitelisted, graylisted, nor blacklisted - // is TrustNone here (trust still reports TrustBlacklist for that case, for backward compatibility - // with older clients). - TrustV2 OptTrustType `json:"trust_v2"` // Hash of the NFT item account code cell (hex). CodeHash OptString `json:"code_hash"` // Hash of the NFT item account data cell (hex). @@ -11919,11 +11913,6 @@ func (s *NftItem) GetTrust() TrustType { return s.Trust } -// GetTrustV2 returns the value of TrustV2. -func (s *NftItem) GetTrustV2() OptTrustType { - return s.TrustV2 -} - // GetCodeHash returns the value of CodeHash. func (s *NftItem) GetCodeHash() OptString { return s.CodeHash @@ -11994,11 +11983,6 @@ func (s *NftItem) SetTrust(val TrustType) { s.Trust = val } -// SetTrustV2 sets the value of TrustV2. -func (s *NftItem) SetTrustV2(val OptTrustType) { - s.TrustV2 = val -} - // SetCodeHash sets the value of CodeHash. func (s *NftItem) SetCodeHash(val OptString) { s.CodeHash = val @@ -18222,52 +18206,6 @@ func (o OptTonTransferAction) Or(d TonTransferAction) TonTransferAction { return d } -// NewOptTrustType returns new OptTrustType with value set to v. -func NewOptTrustType(v TrustType) OptTrustType { - return OptTrustType{ - Value: v, - Set: true, - } -} - -// OptTrustType is optional TrustType. -type OptTrustType struct { - Value TrustType - Set bool -} - -// IsSet returns true if OptTrustType was set. -func (o OptTrustType) IsSet() bool { return o.Set } - -// Reset unsets value. -func (o *OptTrustType) Reset() { - var v TrustType - o.Value = v - o.Set = false -} - -// SetTo sets value to v. -func (o *OptTrustType) SetTo(v TrustType) { - o.Set = true - o.Value = v -} - -// Get returns value and boolean that denotes whether value was set. -func (o OptTrustType) Get() (v TrustType, ok bool) { - if !o.Set { - return v, false - } - return o.Value, true -} - -// Or returns value if set, or given parameter if does not. -func (o OptTrustType) Or(d TrustType) TrustType { - if v, ok := o.Get(); ok { - return v - } - return d -} - // NewOptUint32 returns new OptUint32 with value set to v. func NewOptUint32(v uint32) OptUint32 { return OptUint32{ diff --git a/pkg/oas/oas_validators_gen.go b/pkg/oas/oas_validators_gen.go index ae2cfdf6..f55f1260 100644 --- a/pkg/oas/oas_validators_gen.go +++ b/pkg/oas/oas_validators_gen.go @@ -5173,24 +5173,6 @@ func (s *NftItem) Validate() error { Error: err, }) } - if err := func() error { - if value, ok := s.TrustV2.Get(); ok { - if err := func() error { - if err := value.Validate(); err != nil { - return err - } - return nil - }(); err != nil { - return err - } - } - return nil - }(); err != nil { - failures = append(failures, validate.FieldError{ - Name: "trust_v2", - Error: err, - }) - } if len(failures) > 0 { return &validate.Error{Fields: failures} }