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 5 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
35 changes: 30 additions & 5 deletions contracts/dnsregistrar/OffchainDNSResolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,13 @@ contract OffchainDNSResolver is IExtendedResolver, IERC165 {
) {
// Ignore records with wrong name, type, or class
bytes memory rrname = RRUtils.readName(iter.data, iter.offset);
uint256 nameOffset = 0;
if (checkWildcard(name)) {
nameOffset = 2;
}

if (
!rrname.equals(name) ||
!name.equals(nameOffset, rrname, 0, name.length - nameOffset) ||
iter.class != CLASS_INET ||
iter.dnstype != TYPE_TXT
) {
Expand Down Expand Up @@ -168,6 +173,12 @@ contract OffchainDNSResolver is IExtendedResolver, IERC165 {
);
}

function checkWildcard(
bytes memory name
) public pure returns (bool isWildcard) {
return name.length > 4 && uint8(name[0]) == 1 && name[1] == "*";
mdtanrikulu marked this conversation as resolved.
Show resolved Hide resolved
}

function parseRR(
bytes memory data,
uint256 idx,
Expand Down Expand Up @@ -199,10 +210,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;
uint256 idx = startIdx;
while (idx < lastIdx) {
uint256 fieldLength = data.readUint8(idx);
totalLength += fieldLength;
idx += fieldLength + 1;
}
mdtanrikulu marked this conversation as resolved.
Show resolved Hide resolved

bytes memory result = new bytes(totalLength);
idx = startIdx;
uint256 resultIdx = 0;
while (idx < lastIdx) {
uint256 fieldLength = data.readUint8(idx);
result.strcpy(resultIdx, data, idx + 1, fieldLength);
resultIdx += fieldLength;
idx += fieldLength + 1;
}
mdtanrikulu marked this conversation as resolved.
Show resolved Hide resolved
return result;
}

function parseAndResolve(
Expand Down
221 changes: 221 additions & 0 deletions contracts/dnsregistrar/mocks/DummyExtendedDNSSECResolver2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "../../resolvers/profiles/IExtendedDNSResolver.sol";
import "../../resolvers/profiles/IAddressResolver.sol";
import "../../resolvers/profiles/IAddrResolver.sol";
import "../../resolvers/profiles/ITextResolver.sol";
import "../../utils/HexUtils.sol";
import "../../utils/BytesUtils.sol";

contract DummyExtendedDNSSECResolver2 is IExtendedDNSResolver, IERC165 {
using HexUtils for *;
using BytesUtils for *;
using Strings for *;

uint256 private constant COIN_TYPE_ETH = 60;

error NotImplemented();
error InvalidAddressFormat(bytes addr);

function supportsInterface(
bytes4 interfaceId
) external view virtual override returns (bool) {
return interfaceId == type(IExtendedDNSResolver).interfaceId;
}

function resolve(
bytes calldata /* name */,
bytes calldata data,
bytes calldata context
) external pure override returns (bytes memory) {
bytes4 selector = bytes4(data);
if (selector == IAddrResolver.addr.selector) {
return _resolveAddr(context);
} else if (selector == IAddressResolver.addr.selector) {
return _resolveAddress(data, context);
} else if (selector == ITextResolver.text.selector) {
return _resolveText(data, context);
}
revert NotImplemented();
}

function _resolveAddress(
bytes calldata data,
bytes calldata context
) internal pure returns (bytes memory) {
(, uint256 coinType) = abi.decode(data[4:], (bytes32, uint256));
bytes memory value;
// Per https://docs.ens.domains/ensip/11#specification
if (coinType & 0x80000000 != 0) {
value = _findValue(
context,
bytes.concat(
"a[e",
bytes((coinType & 0x7fffffff).toString()),
"]="
)
);
} else {
value = _findValue(
context,
bytes.concat("a[", bytes(coinType.toString()), "]=")
);
}
if (value.length == 0) {
return value;
}
(bytes memory record, bool valid) = value.hexToBytes(2, value.length);
if (!valid) revert InvalidAddressFormat(value);
return record;
}

function _resolveAddr(
bytes calldata context
) internal pure returns (bytes memory) {
bytes memory value = _findValue(context, "a[60]=");
if (value.length == 0) {
return value;
}
(bytes memory record, bool valid) = value.hexToBytes(2, value.length);
if (!valid) revert InvalidAddressFormat(value);
return record;
}

function _resolveText(
bytes calldata data,
bytes calldata context
) internal pure returns (bytes memory) {
(, string memory key) = abi.decode(data[4:], (bytes32, string));
bytes memory value = _findValue(
context,
bytes.concat("t[", bytes(key), "]=")
);
return value;
}

uint256 constant STATE_START = 0;
uint256 constant STATE_IGNORED_KEY = 1;
uint256 constant STATE_IGNORED_KEY_ARG = 2;
uint256 constant STATE_VALUE = 3;
uint256 constant STATE_QUOTED_VALUE = 4;
uint256 constant STATE_UNQUOTED_VALUE = 5;
uint256 constant STATE_IGNORED_VALUE = 6;
uint256 constant STATE_IGNORED_QUOTED_VALUE = 7;
uint256 constant STATE_IGNORED_UNQUOTED_VALUE = 8;

function _findValue(
bytes memory data,
bytes memory key
) internal pure returns (bytes memory value) {
uint256 state = STATE_START;
uint256 len = data.length;
for (uint256 i = 0; i < len; ) {
if (state == STATE_START) {
if (data.equals(i, key, 0, key.length)) {
i += key.length;
state = STATE_VALUE;
} else {
state = STATE_IGNORED_KEY;
}
} else if (state == STATE_IGNORED_KEY) {
for (; i < len; i++) {
if (data[i] == "=") {
state = STATE_IGNORED_VALUE;
i += 1;
break;
} else if (data[i] == "[") {
state = STATE_IGNORED_KEY_ARG;
i += 1;
break;
}
}
} else if (state == STATE_IGNORED_KEY_ARG) {
for (; i < len; i++) {
if (data[i] == "]") {
state = STATE_IGNORED_VALUE;
i += 1;
if (data[i] == "=") {
i += 1;
}
break;
}
}
} else if (state == STATE_VALUE) {
if (data[i] == "'") {
state = STATE_QUOTED_VALUE;
i += 1;
} else {
state = STATE_UNQUOTED_VALUE;
}
} else if (state == STATE_QUOTED_VALUE) {
uint256 start = i;
uint256 valueLen = 0;
bool escaped = false;
for (; i < len; i++) {
if (escaped) {
data[start + valueLen] = data[i];
valueLen += 1;
escaped = false;
} else {
if (data[i] == "\\") {
escaped = true;
} else if (data[i] == "'") {
return data.substring(start, valueLen);
} else {
data[start + valueLen] = data[i];
valueLen += 1;
}
}
}
} else if (state == STATE_UNQUOTED_VALUE) {
uint256 start = i;
for (; i < len; i++) {
if (data[i] == " ") {
return data.substring(start, i - start);
}
}
return data.substring(start, len - start);
} else if (state == STATE_IGNORED_VALUE) {
if (data[i] == "'") {
state = STATE_IGNORED_QUOTED_VALUE;
i += 1;
} else {
state = STATE_IGNORED_UNQUOTED_VALUE;
}
} else if (state == STATE_IGNORED_QUOTED_VALUE) {
bool escaped = false;
for (; i < len; i++) {
if (escaped) {
escaped = false;
} else {
if (data[i] == "\\") {
escaped = true;
} else if (data[i] == "'") {
i += 1;
while (data[i] == " ") {
i += 1;
}
state = STATE_START;
break;
}
}
}
} else {
assert(state == STATE_IGNORED_UNQUOTED_VALUE);
for (; i < len; i++) {
if (data[i] == " ") {
while (data[i] == " ") {
i += 1;
}
state = STATE_START;
break;
}
}
}
}
return "";
}
}
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
Loading