Skip to content

Commit

Permalink
feat: on-chain validations for wearables and emotes (#1731)
Browse files Browse the repository at this point in the history
  • Loading branch information
marianogoldman authored Mar 14, 2024
1 parent db6ad6b commit 63dd457
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 30 deletions.
4 changes: 2 additions & 2 deletions content/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@
"@dcl/catalyst-api-specs": "^3.2.5",
"@dcl/catalyst-contracts": "^4.3.1",
"@dcl/catalyst-storage": "^4.2.0",
"@dcl/content-validator": "^4.13.2",
"@dcl/content-validator": "^4.14.0",
"@dcl/crypto": "^3.4.5",
"@dcl/hashing": "^3.0.4",
"@dcl/schemas": "^10.3.0",
"@dcl/snapshots-fetcher": "^9.0.3",
"@dcl/urn-resolver": "^3.2.0",
"@dcl/urn-resolver": "^3.3.1",
"@well-known-components/env-config-provider": "^1.2.0",
"@well-known-components/fetch-component": "^2.0.2",
"@well-known-components/http-server": "^2.0.0",
Expand Down
2 changes: 0 additions & 2 deletions content/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,6 @@ export async function initComponentsWithEnv(env: Environment): Promise<AppCompon
{
env,
metrics,
fetcher,
config,
externalCalls,
logs
},
Expand Down
1 change: 0 additions & 1 deletion content/src/controller/handlers/filter-by-urn-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ async function isUrnPrefixValid(collectionUrn: string): Promise<string | false>
return `${collectionUrn}:`
}

console.log(parsedUrn)
if (
parsedUrn.type === 'blockchain-collection-third-party' ||
parsedUrn.type === 'blockchain-collection-v1' ||
Expand Down
102 changes: 98 additions & 4 deletions content/src/logic/checker.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { L1Checker, L2Checker } from '@dcl/content-validator'
import { inputCallFormatter, inputBlockNumberFormatter } from './formatters'
import { RequestManager, HTTPProvider, ContractFactory, toData } from 'eth-connect'
import { l1Contracts, l2Contracts, checkerAbi } from '@dcl/catalyst-contracts'
import { ItemChecker, L1Checker, L2Checker } from '@dcl/content-validator'
import { inputBlockNumberFormatter, inputCallFormatter } from './formatters'
import { ContractFactory, HTTPProvider, RequestManager, RPCSendableMessage, toBatchPayload, toData } from 'eth-connect'
import { checkerAbi, l1Contracts, l2Contracts } from '@dcl/catalyst-contracts'
import { code } from '@dcl/catalyst-contracts/dist/checkerByteCode'
import { parseUrn } from '@dcl/urn-resolver'
import { EthAddress } from '@dcl/schemas'
import { ILoggerComponent } from '@well-known-components/interfaces'

type CollectionItem = {
contract: string
nftId: string
item: string
}

export async function createL1Checker(provider: HTTPProvider, network: 'mainnet' | 'sepolia'): Promise<L1Checker> {
const contracts = l1Contracts[network]
Expand Down Expand Up @@ -124,3 +133,88 @@ export async function createL2Checker(provider: HTTPProvider, network: 'mumbai'
}
}
}

const itemCheckerAbi = [
{
inputs: [{ internalType: 'uint256', name: 'tokenId', type: 'uint256' }],
name: 'ownerOf',
outputs: [{ internalType: 'address', name: '', type: 'address' }],
stateMutability: 'view',
type: 'function'
}
]

function sendBatch(provider: HTTPProvider, batch: RPCSendableMessage[]) {
const payload = toBatchPayload(batch)
return new Promise<any>((resolve, reject) => {
provider.sendAsync(payload as any, (err: any, result: any) => {
if (err) {
reject(err)
return
}

resolve(result)
})
})
}

export async function createItemChecker(logs: ILoggerComponent, provider: HTTPProvider): Promise<ItemChecker> {
const logger = logs.getLogger('item-checker')
const requestManager = new RequestManager(provider)
const factory = new ContractFactory(requestManager, itemCheckerAbi)

async function getOwnerOf(items: CollectionItem[], block: number): Promise<(EthAddress | undefined)[]> {
const contracts = await Promise.all(items.map((item) => factory.at(item.contract) as any))
const batch: RPCSendableMessage[] = await Promise.all(
items.map((item, idx) => contracts[idx].ownerOf.toRPCMessage(item.nftId, block))
)
const result = await sendBatch(provider, batch)
return result.map((r: any, idx: number) => {
if (!r.result) {
return undefined
}
return contracts[idx].ownerOf.unpackOutput(toData(r.result))?.toLowerCase()
})
}

async function checkItems(ethAddress: string, items: string[], block: number): Promise<boolean[]> {
const uniqueItems = Array.from(new Set(items))
const urns = await Promise.all(uniqueItems.map((item) => parseUrn(item)))

const result = new Map<string, boolean>()
const filteredItems: CollectionItem[] = []

for (let i = 0; i < urns.length; ++i) {
const item = uniqueItems[i]
const urn = urns[i]
if (!urn) {
logger.warn(`Invalid urn ${item}`)
} else if (urn.type === 'blockchain-collection-v1-asset' || urn.type === 'blockchain-collection-v2-asset') {
logger.warn(`Found asset, let it pass: ${item}`)
result.set(item, true) // old deployment, let it pass
} else if (urn.type === 'blockchain-collection-v1-item' || urn.type === 'blockchain-collection-v2-item') {
if (!urn.contractAddress) {
logger.warn(`No contract address found for item: ${item}`)
} else if (!urn.tokenId) {
logger.warn(`No tokenId found for item: ${item})`)
result.set(item, true) // old deployment, let it pass
} else {
filteredItems.push({ contract: urn.contractAddress, nftId: urn.tokenId, item })
}
}
}

if (filteredItems.length > 0) {
const owners = await getOwnerOf(filteredItems, block)
owners.forEach((owner, idx) =>
result.set(filteredItems[idx].item, !!owner && owner.toLowerCase() === ethAddress.toLowerCase())
)
}

return items.map((item) => result.get(item) || false)
}

return {
checkItems
}
}
18 changes: 7 additions & 11 deletions content/src/service/validations/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { Authenticator } from '@dcl/crypto'
import { createSubgraphComponent } from '@well-known-components/thegraph-component'
import RequestManager, { HTTPProvider } from 'eth-connect'
import { EnvironmentConfig } from '../../Environment'
import { createL1Checker, createL2Checker } from '../../logic/checker'
import { createItemChecker, createL1Checker, createL2Checker } from '../../logic/checker'
import { AppComponents } from '../../types'

const createEthereumProvider = (httpProvider: HTTPProvider): EthereumProvider => {
Expand Down Expand Up @@ -75,16 +75,18 @@ export async function createIgnoreBlockchainValidator(
}

export async function createOnChainValidator(
components: Pick<AppComponents, 'env' | 'metrics' | 'config' | 'externalCalls' | 'logs' | 'fetcher'>,
components: Pick<AppComponents, 'env' | 'metrics' | 'externalCalls' | 'logs'>,
l1Provider: HTTPProvider,
l2Provider: HTTPProvider
): Promise<ValidateFn> {
const { env, metrics, logs, fetcher, config, externalCalls } = components
const { env, metrics, logs, externalCalls } = components
const l1Network: 'mainnet' | 'sepolia' = env.getConfig(EnvironmentConfig.ETH_NETWORK)
const l2Network = l1Network === 'mainnet' ? 'polygon' : 'mumbai'

const l1Checker = await createL1Checker(l1Provider, l1Network)
const l2Checker = await createL2Checker(l2Provider, l2Network)
const l1ItemChecker = await createItemChecker(logs, l1Provider)
const l2ItemChecker = await createItemChecker(logs, l2Provider)

const l1BlockSearch = createAvlBlockSearch({
blockRepository: createBlockRepository({
Expand Down Expand Up @@ -127,19 +129,13 @@ export async function createOnChainValidator(

const L1 = {
checker: l1Checker,
collections: await createSubgraphComponent(
{ config, fetch: fetcher, metrics, logs },
components.env.getConfig(EnvironmentConfig.COLLECTIONS_L1_SUBGRAPH_URL)
),
collections: l1ItemChecker,
blockSearch: l1BlockSearch
}

const L2 = {
checker: l2Checker,
collections: await createSubgraphComponent(
{ config, fetch: fetcher, metrics, logs },
components.env.getConfig(EnvironmentConfig.COLLECTIONS_L2_SUBGRAPH_URL)
),
collections: l2ItemChecker,
blockSearch: l2BlockSearch
}

Expand Down
2 changes: 1 addition & 1 deletion lambdas/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@dcl/catalyst-api-specs": "^3.2.5",
"@dcl/crypto": "^3.4.5",
"@dcl/schemas": "^10.3.0",
"@dcl/urn-resolver": "^3.2.0",
"@dcl/urn-resolver": "^3.3.1",
"@well-known-components/env-config-provider": "^1.2.0",
"@well-known-components/fetch-component": "^2.0.2",
"@well-known-components/http-server": "^2.0.0",
Expand Down
18 changes: 9 additions & 9 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -350,16 +350,16 @@
dependencies:
ethers "^5.6.8"

"@dcl/content-validator@^4.13.2":
version "4.13.2"
resolved "https://registry.yarnpkg.com/@dcl/content-validator/-/content-validator-4.13.2.tgz#9a26c81cbdd02960638fbf09d752fb3c6c193f87"
integrity sha512-GAQvmNOTKvLRF8LaW7jh3X832FVz418iSHWbxbYp9oHBCqCUEMEmVxRtaeoC+D3gafmEOS+vmkLyhbsdVwH4PA==
"@dcl/content-validator@^4.14.0":
version "4.14.0"
resolved "https://registry.yarnpkg.com/@dcl/content-validator/-/content-validator-4.14.0.tgz#f30d1713a5bad857419cbd882248a47d6b9306b8"
integrity sha512-KA8UIi1b5aKrTd6nPu/wxk2Wedxjkw52pVOGQ5I0r4haeHc2H1RhJHQaWvYBmwENTVDM/B3O9/8GGmleZtMETQ==
dependencies:
"@dcl/block-indexer" "^1.1.1"
"@dcl/content-hash-tree" "^1.1.4"
"@dcl/hashing" "^3.0.1"
"@dcl/schemas" "^10.3.0"
"@dcl/urn-resolver" "3.3.0"
"@dcl/urn-resolver" "^3.3.1"
"@well-known-components/interfaces" "^1.3.0"
"@well-known-components/thegraph-component" "^1.5.0"
ms "^2.1.3"
Expand Down Expand Up @@ -410,10 +410,10 @@
"@well-known-components/metrics" "^2.0.0"
p-queue "^6.6.2"

"@dcl/urn-resolver@3.3.0", "@dcl/urn-resolver@^3.2.0":
version "3.3.0"
resolved "https://registry.yarnpkg.com/@dcl/urn-resolver/-/urn-resolver-3.3.0.tgz#06c0b0b97839ba547d00c85485662c14cfdcf948"
integrity sha512-stDVUJk0hoagAKpy+P3QqLvg8Y1d9p2CN7JwqDtzwfcWxs9Ew6zyg+j/T7ADHQQk1mrvQ3lqoJd9KEWX6nHscQ==
"@dcl/urn-resolver@^3.3.1":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@dcl/urn-resolver/-/urn-resolver-3.3.1.tgz#440fa3e6427ea9e3a4473edcbcce463cfde0598a"
integrity sha512-mIar6XS+sYmKMHFcA1F9tuHqjvWTDRCE8zPK46Va8uftxFH/1KaqsfuutuBj9atj/61smxElmPb7O0qPguKVjA==

"@eslint/eslintrc@^1.3.0":
version "1.3.0"
Expand Down

0 comments on commit 63dd457

Please sign in to comment.