Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

concatenate multiple text fields #332

Open
wants to merge 21 commits into
base: fix/refactor-wrapped-callback
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
89ad8ed
concatenate multiple text fields
mdtanrikulu Feb 20, 2024
2880879
update wildcard check without copying name, concatenate text in O(n)
mdtanrikulu Feb 20, 2024
2be5088
sync testing with feature/better-offchain-dns
mdtanrikulu Mar 26, 2024
e9894f6
update testhexutils
mdtanrikulu Mar 27, 2024
092b087
Merge branch 'fix/refactor-wrapped-callback' into experimental-multi-…
mdtanrikulu Jul 23, 2024
0758752
remove DummyDNSResolver2
mdtanrikulu Aug 22, 2024
c3a0836
update extendeddnsresolver deployments file
mdtanrikulu Aug 22, 2024
1445c3f
merge staging into
mdtanrikulu Aug 22, 2024
d7f5ee6
Merge branch 'fix/refactor-wrapped-callback' into experimental-multi-…
mdtanrikulu Aug 22, 2024
67e3145
fix wildcard check by comparing to it's hex representation
mdtanrikulu Aug 27, 2024
4f59136
Merge branch 'staging' into experimental-multi-field
mdtanrikulu Oct 24, 2024
92d01c6
revert wildcard support
mdtanrikulu Oct 24, 2024
c03968b
update deprecated experimental loader, bump hardhat
mdtanrikulu Oct 24, 2024
7ff6a56
update deprecated buffer use
mdtanrikulu Oct 24, 2024
6bc18af
revert redundant name offset
mdtanrikulu Oct 28, 2024
f98f323
update lock file
mdtanrikulu Oct 28, 2024
69476ed
Merge branch 'fix/refactor-wrapped-callback' into experimental-multi-…
mdtanrikulu Oct 28, 2024
db8a085
improve TXT concatenation testing
mdtanrikulu Oct 29, 2024
7654735
Update contracts/dnsregistrar/OffchainDNSResolver.sol
mdtanrikulu Nov 15, 2024
4630340
Update contracts/dnsregistrar/OffchainDNSResolver.sol
mdtanrikulu Nov 15, 2024
24fcb62
update for loops in readTXT method
mdtanrikulu Nov 15, 2024
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
Binary file modified bun.lockb
Binary file not shown.
23 changes: 19 additions & 4 deletions contracts/dnsregistrar/OffchainDNSResolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ contract OffchainDNSResolver is IExtendedResolver, IERC165 {
) {
// Ignore records with wrong name, type, or class
bytes memory rrname = RRUtils.readName(iter.data, iter.offset);

if (
!rrname.equals(name) ||
iter.class != CLASS_INET ||
Expand Down Expand Up @@ -199,10 +200,24 @@ contract OffchainDNSResolver is IExtendedResolver, IERC165 {
uint256 startIdx,
uint256 lastIdx
) internal pure returns (bytes memory) {
// TODO: Concatenate multiple text fields
uint256 fieldLength = data.readUint8(startIdx);
assert(startIdx + fieldLength < lastIdx);
return data.substring(startIdx + 1, fieldLength);
uint256 totalLength = 0;

for (uint256 i = startIdx; i < lastIdx; ) {
uint256 fieldLength = data.readUint8(i);
totalLength += fieldLength;
i += fieldLength + 1;
}

bytes memory result = new bytes(totalLength);
uint256 resultIdx = 0;

for (uint256 i = startIdx; i < lastIdx; ) {
uint256 fieldLength = data.readUint8(i);
result.strcpy(resultIdx, data, i + 1, fieldLength);
resultIdx += fieldLength;
i += fieldLength + 1;
}
return result;
}

function parseAndResolve(
Expand Down
21 changes: 21 additions & 0 deletions contracts/utils/BytesUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,27 @@ library BytesUtils {
}
}

function strcpy(
bytes memory self,
uint256 selfOffset,
bytes memory src,
uint256 srcOffset,
uint256 length
) internal pure {
require(selfOffset + length <= self.length);
require(srcOffset + length <= src.length);

uint256 selfPtr;
uint256 srcPtr;

assembly {
selfPtr := add(add(self, 32), selfOffset)
srcPtr := add(add(src, 32), srcOffset)
}

memcpy(selfPtr, srcPtr, length);
}

/*
* @dev Copies a substring into a new byte string.
* @param self The byte string to copy from.
Expand Down
6 changes: 3 additions & 3 deletions deploy/dnssec-oracle/10_deploy_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const realAnchors = [
keyTag: 19036,
algorithm: 8,
digestType: 2,
digest: new Buffer(
digest: Buffer.from(
'49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5',
'hex',
),
Expand All @@ -27,7 +27,7 @@ const realAnchors = [
keyTag: 20326,
algorithm: 8,
digestType: 2,
digest: new Buffer(
digest: Buffer.from(
'E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D',
'hex',
),
Expand All @@ -44,7 +44,7 @@ const dummyAnchor = {
keyTag: 1278, // Empty body, flags == 0x0101, algorithm = 253, body = 0x0000
algorithm: 253,
digestType: 253,
digest: new Buffer('', 'hex'),
digest: Buffer.from('', 'hex'),
},
}

Expand Down
4 changes: 4 additions & 0 deletions loader.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { register } from 'node:module'
import { pathToFileURL } from 'node:url'

register('ts-node/esm/transpile-only', pathToFileURL('./'))
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"description": "ENS contracts",
"type": "module",
"scripts": {
"compile": "NODE_OPTIONS=\"--experimental-loader ts-node/esm/transpile-only\" hardhat compile",
"test": "NODE_OPTIONS=\"--experimental-loader ts-node/esm/transpile-only\" hardhat test",
"test:parallel": "NODE_OPTIONS=\"--experimental-loader ts-node/esm/transpile-only\" hardhat test ./test/**/Test*.ts --parallel",
"test:local": "hardhat --network localhost test",
"compile": "NODE_OPTIONS=\"--import=./loader.mjs\" hardhat compile",
"test": "NODE_OPTIONS=\"--import=./loader.mjs\" hardhat test",
"test:parallel": "NODE_OPTIONS=\"--import=./loader.mjs\" hardhat test ./test/**/Test*.ts --parallel",
"test:local": "NODE_OPTIONS=\"--import=./loader.mjs\" hardhat --network localhost test",
"test:deploy": "bun ./scripts/deploy-test.ts",
"lint": "hardhat check",
"build": "rm -rf ./build/deploy ./build/hardhat.config.js && hardhat compile && tsc",
Expand Down Expand Up @@ -37,7 +37,7 @@
"abitype": "^1.0.2",
"chai": "^5.1.1",
"dotenv": "^16.4.5",
"hardhat": "^2.22.2",
"hardhat": "^2.22.9",
"hardhat-abi-exporter": "^2.9.0",
"hardhat-contract-sizer": "^2.6.1",
"hardhat-deploy": "^0.12.4",
Expand Down
2 changes: 1 addition & 1 deletion scripts/deploy-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ execSync('bun run hardhat --network localhost deploy', {
stdio: 'inherit',
env: {
...process.env,
NODE_OPTIONS: '--experimental-loader ts-node/esm/transpile-only',
NODE_OPTIONS: '--import=./loader.mjs',
BATCH_GATEWAY_URLS: '["https://example.com/"]',
},
})
Expand Down
165 changes: 164 additions & 1 deletion test/dnsregistrar/TestOffchainDNSResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
zeroAddress,
zeroHash,
type Hex,
encodeFunctionResult,
} from 'viem'
import {
expiration,
Expand Down Expand Up @@ -76,7 +77,7 @@ async function fixture() {
calldata,
}: {
name: string
texts: string[]
texts: (string | string[])[]
calldata: Hex
}) => {
const proof = [
Expand Down Expand Up @@ -590,4 +591,166 @@ describe('OffchainDNSResolver', () => {
)
.toBeRevertedWithCustomError('InvalidOperation')
})

it('should correctly concatenate multiple texts in the TXT record and resolve', async function () {
const { doDnsResolveCallback, publicResolverAbi } = await loadFixture(
fixture,
)
const resolver = await hre.viem.deployContract(
'DummyExtendedDNSSECResolver',
[],
)
const name = 'test.test'
const COIN_TYPE_ETH = 60
const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe'
const calldataAddr = encodeFunctionData({
abi: publicResolverAbi,
functionName: 'addr',
args: [namehash(name)],
})

const resultTextSingle = encodeFunctionResult({
abi: publicResolverAbi,
functionName: 'text',
result: `a[60]=${testAddress} t[smth]=smth.eth`,
})

// test with single string
await expect(
doDnsResolveCallback({
name,
texts: [
`ENS1 ${resolver.address} a[${COIN_TYPE_ETH}]=${testAddress} t[smth]=smth.eth`,
],
calldata: calldataAddr,
}),
).resolves.toEqual(resultTextSingle)

const resultTextSplit = encodeFunctionResult({
abi: publicResolverAbi,
functionName: 'text',
result: `a[${COIN_TYPE_ETH}]=${testAddress} t[smth]=smth.eth`,
})

// test with split strings
await expect(
doDnsResolveCallback({
name,
texts: [
[
`ENS1 ${resolver.address}`,
` a[${COIN_TYPE_ETH}]=${testAddress}`,
` t[smth]=smth.eth`,
],
],
calldata: calldataAddr,
}),
).resolves.toEqual(resultTextSplit)

// test with very long string
const longData = 'x'.repeat(300)

const resultTextLongData = encodeFunctionResult({
abi: publicResolverAbi,
functionName: 'text',
result: `a[${COIN_TYPE_ETH}]=${testAddress} ${longData}`,
})
await expect(
doDnsResolveCallback({
name,
texts: [
[
`ENS1 ${resolver.address} a[${COIN_TYPE_ETH}]=${testAddress}`,
' ',
longData.slice(0, 255),
longData.slice(255),
],
],
calldata: calldataAddr,
}),
).resolves.toEqual(resultTextLongData)
})

it('should correctly do text resolution regardless of order', async function () {
const name = 'test.test'
const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe'

const { doDnsResolveCallback, publicResolverAbi } = await loadFixture(
fixture,
)

const resolver = await hre.viem.deployContract(
'DummyExtendedDNSSECResolver',
[],
)

const callDataText = encodeFunctionData({
abi: publicResolverAbi,
functionName: 'text',
args: [namehash(name), 'smth'],
})

const resultText = encodeFunctionResult({
abi: publicResolverAbi,
functionName: 'text',
result: `t[smth]=smth.eth ${testAddress}`,
})

await expect(
doDnsResolveCallback({
name,
texts: [`ENS1 ${resolver.address} t[smth]=smth.eth ${testAddress}`],
calldata: callDataText,
}),
).resolves.toEqual(resultText)
})

it('should correctly do text resolution regardless of order', async function () {
const name = 'test.test'
const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe'

const { doDnsResolveCallback, publicResolverAbi } = await loadFixture(
fixture,
)

const resolver = await hre.viem.deployContract('ExtendedDNSResolver', [])

const callDataText = encodeFunctionData({
abi: publicResolverAbi,
functionName: 'text',
args: [namehash(name), 'smth'],
})

await expect(
doDnsResolveCallback({
name,
texts: [`ENS1 ${resolver.address} t[smth]=smth.eth ${testAddress}`],
calldata: callDataText,
}),
).resolves.toEqual(encodeAbiParameters([{ type: 'string' }], ['smth.eth']))
})

it('should correctly do text resolution regardless of key-value pair amount', async function () {
const name = 'test.test'

const { doDnsResolveCallback, publicResolverAbi } = await loadFixture(
fixture,
)

const resolver = await hre.viem.deployContract('ExtendedDNSResolver', [])

const callDataText = encodeFunctionData({
abi: publicResolverAbi,
functionName: 'text',
args: [namehash(name), 'bla'],
})

await expect(
doDnsResolveCallback({
name,
texts: [`ENS1 ${resolver.address} t[smth]=smth.eth t[bla]=bla.eth`],
calldata: callDataText,
}),
).resolves.toEqual(encodeAbiParameters([{ type: 'string' }], ['bla.eth']))
})
})
Loading