From 19e4ec8211522e690d0960f52018264afc39e9d8 Mon Sep 17 00:00:00 2001 From: jim Date: Mon, 25 Nov 2019 09:45:31 -0500 Subject: [PATCH 01/11] GeoENS pull request - initial revision --- EIPS/eip-geoens.md | 166 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 EIPS/eip-geoens.md diff --git a/EIPS/eip-geoens.md b/EIPS/eip-geoens.md new file mode 100644 index 00000000000000..3aaad978dac014 --- /dev/null +++ b/EIPS/eip-geoens.md @@ -0,0 +1,166 @@ +--- +eip: +title: Geo-ENS +author: James Choncholas +discussions-to: +status: Draft +type: Standards Track +category: ERC +created: 2019-11-15 +requires ( * optional): EIP137, EIP165, EIP1062, EIP1185 +--- + + + + +## Simple Summary + +GeoDNS, but for ENS! Allows for location specific responses to ENS queries from resolver contracts. + +## Abstract + +This EIP specifies an ENS resolver interface for geographically specific address resolution. +This permits associating a human-readable name with an address associated with a specific geographic location. +Geographic resolution can extended beyond ENS address resolvers (EIP137) to DNS resolvers (EIP1185), ABI resolvers (EIP1062), and any more to be created. + +## Motivation + +There are many use cases for traditional GeoDNS systems, like Amazon's Route53, in the centralized web. +These use cases include proximity-based load balancing and serving content specific to the geographic location of the query. +Unfortunately the ENS specification does not provide a mechanism for geo-specific resolution. +ENS can respond to queries with IP addresses (as described in EIP1185) however there is no way to respond to geo-specific queries. +This EIP proposes a standard to give the ENS system geo-proximal awareness to serve a similar purpose as GeoDNS. + +Mimicking GeoDNS is not the only purpose of this EIP. +This interface specification extends the utility of GeoENS beyond plain GeoDNS. +For example, GeoENS is useful to locate digital resources (like smart contracts) that represent physical objects in the real world. +More examples include: + - Using ENS to do geo-specific IP queries as described in EIP1185. + - Smart contract managing access to a physical object associated with a specific location. + - ENS + IPFS web hosting (as described in EIP1162) with content translated to the native language of the query source. + - Tokenizing objects with a physical location. + +## Specification + +This EIP proposes a new interface to ENS resolvers such that geo-spacial information can be recorded and retrieved from the blockchain. +The interface changes are described below for "address resolvers" described in EIP137 however the idea applies to any record described in EIP1185 and EIP1062, namely DNS Resolvers, Text Resolvers, ABI Resolvers, etc. + +### function geoAddr(bytes32 node, uint64 geohash, uint64 precisionMask) returns (address[] memory ret) +Query the resolver contract for a specific node and location described by a location. +The `geohash` is masked by the `precisionMask` to describe a physical region much like a square on a map. +All resources (contract addresses, IP addresses, ABIs, TEXT records, etc.) matching the node and within the region are returned. + +### function setGeoAddr(bytes32 node, uint64 geohash, address addr) +Sets a resource (contract address, IP, ABI, TEXT, etc.) by node and geohash. +Geohashes must be unique per node. +Write default initialized resource value, like `address(0)`, to remove a resource from the resolver. + +### geohash +Geohashes is typically encoded in base 32 characters, as described https://en.m.wikipedia.org/wiki/Geohash#Algorithm_and_example. +While these could be represented using a string, it is more efficient to represent a geohash as a 64 bit unsigned integer. +This reduces gas usage during transactions. +More information regarding efficiency can be found in the implementation section. + + + +There are different ways to search geographic data using geohashes. +First, a single query using a geohash of reduced precision will search a box shaped geographic region. +Searching in a circular shaped region is slightly more complex as it requires multiple queries. +Algorithms to do so, like https://github.com/ashwin711/proximityhash, can be left to the reader to explore. + +## Rationale + +Because of the decentralized nature of ENS, geo-specific resolution is different than traditional GeoDNS. +GeoDNS works as follows. DNS queries are identified by their source IP address. +This IP is looked up in a database like GeoIP2 from MaxMind which maps the IP address to a location. +This method of locating the source of a query is error prone and unreliable. +If the GeoIP database is out of date, queried locations can be vastly different than their true location. +GeoENS does not rely on a database because the user includes a location in their query. + +An additional shortcoming of traditional DNS is the fact that there is no way to return a list of servers in a certain proximity. +This is paramount for uses cases that require discovering the resource with the lowest latency. +GeoENS allows a list of resources, like IP addresses, to be gathered within a specific location. +Then a client to determine themselves which resource has the lowest latency. + +Another benefit of GeoENS is that queries can be made for any location. +Traditional DNS will only return the resource assigned to a query's provenance. +GeoENS does not correlate a query's source with a location, allowing the entire globe to be queried from one location. + +Lastly, publicly facing GeoDNS services do not give fine granularity control over geographic regions for GeoDNS queries. +Cloud based DNS services like Amazon's Route 53 only allow specifying geographic regions at the granularity of a State in the United States. +This course granularity could be due to the unreliability of the underlying databases mapping IP to location. +GeoENS on the other hand gives 64 bits of geohash resolution which is sub-meter accuracy. + + +## Backwards Compatibility + +This EIP does not introduce issues with backwards compatibility. + +## Test Cases + +See https://github.com/james-choncholas/geoens/tree/master/test + +## Implementation + + +This single owner address resolver, written in Solidity, implements the specifications outlined above. +The same idea presented here can be applied to public resolvers as specified in EIP137. +Note that geohashes are passed and stored using 64 bit unsigned integers. +Using integers instead of strings for geohashes is more performant, especially in the `geomap` mapping. +For comparison purposes, see https://github.com/james-choncholas/geoens/tree/master/contracts/StringOwnedGeoENSResolver.sol for the inefficient string implementation. + + +```solidity +pragma solidity >=0.4.21 <0.6.0; + +contract OwnedGeoENSResolver { + // Other code + + bytes4 constant ERC165ID = 0x01ffc9a7; + bytes4 constant ERCTBDID = 0xc5505b25; + uint constant MAX_ADDR_RETURNS = 64; + mapping(uint64=>address) geomap; + address public owner = msg.sender; + + event AddrChanged(bytes32 indexed node, uint64 geohash, address addr); + + modifier isOwner() { + require(msg.sender == owner); + _; + } + + function geoAddr(bytes32 node, uint64 geohash, uint64 precisionMask) external view returns (address[] memory ret) { + bytes32(node); // single node georesolver ignores node + + ret = new address[](MAX_ADDR_RETURNS); + uint ret_i = 0; + + for(uint64 i=(geohash & precisionMask); i<= geohash + ~precisionMask; i++) { + if (geomap[i] != address(0)) { + ret[ret_i] = geomap[i]; + ret_i ++; + } + if (ret_i > MAX_ADDR_RETURNS) break; + } + + return ret; + } + + function setGeoAddr(bytes32 node, uint64 geohash, address addr) external isOwner() { + // single node georesolver ignores node + geomap[geohash] = addr; + emit AddrChanged(node, geohash, addr); + } + + function supportsInterface(bytes4 interfaceID) external pure returns (bool) { + return interfaceID == ERC165ID || interfaceID == ERCTBDID; + } + + function() external payable { + revert(); + } +} +``` + +# Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From ff616561997b5e7b9bcb6f76a812c33bf14ca739 Mon Sep 17 00:00:00 2001 From: jim Date: Mon, 25 Nov 2019 09:50:55 -0500 Subject: [PATCH 02/11] Update GeoENS with assigned PR number 2390 --- EIPS/{eip-geoens.md => eip-2390.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename EIPS/{eip-geoens.md => eip-2390.md} (99%) diff --git a/EIPS/eip-geoens.md b/EIPS/eip-2390.md similarity index 99% rename from EIPS/eip-geoens.md rename to EIPS/eip-2390.md index 3aaad978dac014..436adb300f2584 100644 --- a/EIPS/eip-geoens.md +++ b/EIPS/eip-2390.md @@ -1,8 +1,8 @@ --- -eip: +eip: 2390 title: Geo-ENS author: James Choncholas -discussions-to: +discussions-to: https://github.com/ethereum/EIPs/pull/2390 status: Draft type: Standards Track category: ERC From c8f1f8fd1bb199d1cf45ec9f2de05bfdfbbe9762 Mon Sep 17 00:00:00 2001 From: jim Date: Fri, 8 May 2020 17:42:20 -0400 Subject: [PATCH 03/11] Updates to EIP 2390 with new interface --- EIPS/eip-2390.md | 310 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 229 insertions(+), 81 deletions(-) diff --git a/EIPS/eip-2390.md b/EIPS/eip-2390.md index 436adb300f2584..892648f8a8c2cd 100644 --- a/EIPS/eip-2390.md +++ b/EIPS/eip-2390.md @@ -15,81 +15,111 @@ requires ( * optional): EIP137, EIP165, EIP1062, EIP1185 ## Simple Summary -GeoDNS, but for ENS! Allows for location specific responses to ENS queries from resolver contracts. +GeoENS brings geographic split horizon capabilities to ENS. It's GeoDNS for ENS! ## Abstract -This EIP specifies an ENS resolver interface for geographically specific address resolution. -This permits associating a human-readable name with an address associated with a specific geographic location. -Geographic resolution can extended beyond ENS address resolvers (EIP137) to DNS resolvers (EIP1185), ABI resolvers (EIP1062), and any more to be created. +This EIP specifies an ENS resolver interface for geographically split horizon DNS. +Geographic split horizon DNS returns resource records that are specific to an end +user's location. +This technique is commonly used by CDNs to direct traffic to content caches nearest users. +Geographic split horizon resolution is primarily geared towards ENS +resolvers storing DNS resource records (EIP1185), although the technique could be +used on other interfaces like IPFS content hash storage (EIP1162). ## Motivation -There are many use cases for traditional GeoDNS systems, like Amazon's Route53, in the centralized web. -These use cases include proximity-based load balancing and serving content specific to the geographic location of the query. -Unfortunately the ENS specification does not provide a mechanism for geo-specific resolution. -ENS can respond to queries with IP addresses (as described in EIP1185) however there is no way to respond to geo-specific queries. -This EIP proposes a standard to give the ENS system geo-proximal awareness to serve a similar purpose as GeoDNS. - -Mimicking GeoDNS is not the only purpose of this EIP. -This interface specification extends the utility of GeoENS beyond plain GeoDNS. -For example, GeoENS is useful to locate digital resources (like smart contracts) that represent physical objects in the real world. -More examples include: - - Using ENS to do geo-specific IP queries as described in EIP1185. +There are many use cases for traditional GeoDNS systems, like Amazon's Route53, +in the centralized web. +These use cases include proximity-based load balancing and serving content +specific to the geographic location of the query. +Unfortunately the ENS specification does not provide a mechanism for +geo-specific resolution. +ENS can respond to queries with IP addresses (as described in EIP1185) +however there is no way to respond to geo-specific queries. +This EIP proposes a standard to give the ENS system geo-proximal awareness +to serve a similar purpose as GeoDNS. + +GeoENS can do more than DNS-based solutions. +In addition to geographic split horizon DNS, GeoENS can be used for the following: + - Locating digital resources (like smart contracts) that represent physical objects in the real world. - Smart contract managing access to a physical object associated with a specific location. - ENS + IPFS web hosting (as described in EIP1162) with content translated to the native language of the query source. - Tokenizing objects with a physical location. +Because of the decentralized nature of ENS, geo-specific resolution is different than traditional GeoDNS. +GeoDNS works as follows. DNS queries are identified by their source IP address. +This IP is looked up in a database like GeoIP2 [2] from MaxMind which maps the IP address to a location. +This method of locating the source of a query is error prone and unreliable. +If the GeoIP database is out of date, queried locations can be vastly different than their true location. +GeoENS does not rely on a database because the user includes a location in their query. + +It follows that queries can be made by users for any location, not just their location. +Traditional DNS will only return the resource assigned to a query's provenance. +GeoENS does not correlate a query's provinance with a location, allowing the +entire globe to be queried from a single location. + +An additional shortcoming of traditional DNS is the fact that there is no way to return a list of servers in a certain proximity. +This is paramount for uses cases that require discovering the resource with the lowest latency. +GeoENS allows a list of resources, like IP addresses, to be gathered within a specific location. +Then a client to determine themselves which resource has the lowest latency. + +Lastly, publicly facing GeoDNS services do not give fine granularity control +over geographic regions for GeoDNS queries. +Cloud based DNS services like Amazon's Route 53 [3] only allow specifying +geographic regions at the granularity of a State in the United States. +GeoENS on the other hand gives 8 characters of geohash resolution which +corresponds to +-20 meter accuracy. + + ## Specification -This EIP proposes a new interface to ENS resolvers such that geo-spacial information can be recorded and retrieved from the blockchain. -The interface changes are described below for "address resolvers" described in EIP137 however the idea applies to any record described in EIP1185 and EIP1062, namely DNS Resolvers, Text Resolvers, ABI Resolvers, etc. +This EIP proposes a new interface to ENS resolvers such that geo-spacial information +can be recorded and retrieved from the blockchain. +The interface changes are described below for "address resolvers" described in EIP137 +however the idea applies to any record described in EIP1185 and EIP1062, namely DNS +Resolvers, Text Resolvers, ABI Resolvers, etc. + +### What is a geohash? +Geohashes are typically encoded in base 32 characters, as described +https://en.m.wikipedia.org/wiki/Geohash#Algorithm_and_example. +A geohash is an interleaving of latitude and longitude bits, whos +length determines it's precision. -### function geoAddr(bytes32 node, uint64 geohash, uint64 precisionMask) returns (address[] memory ret) -Query the resolver contract for a specific node and location described by a location. -The `geohash` is masked by the `precisionMask` to describe a physical region much like a square on a map. -All resources (contract addresses, IP addresses, ABIs, TEXT records, etc.) matching the node and within the region are returned. -### function setGeoAddr(bytes32 node, uint64 geohash, address addr) +### function setGeoAddr(bytes32 node, string calldata geohash, address addr) external authorised(node) Sets a resource (contract address, IP, ABI, TEXT, etc.) by node and geohash. -Geohashes must be unique per node. -Write default initialized resource value, like `address(0)`, to remove a resource from the resolver. +Geohashes must be unique per address and are exactly 8 characters long. +This leads to an accuracy of +-20 meters. +Write default initialized resource value, `address(0)`, to remove a resource from the resolver. -### geohash -Geohashes is typically encoded in base 32 characters, as described https://en.m.wikipedia.org/wiki/Geohash#Algorithm_and_example. -While these could be represented using a string, it is more efficient to represent a geohash as a 64 bit unsigned integer. -This reduces gas usage during transactions. -More information regarding efficiency can be found in the implementation section. +### function geoAddr(bytes32 node, string calldata geohash) external view returns (address[] memory ret) +Query the resolver contract for a specific node and location. +All resources (contract addresses, IP addresses, ABIs, TEXT records, etc.) matching +the node and prefix geohash provided are returned. +This permits querying by exact geohash of 8 characters to return the contect at that location, +or querying by geographic bounding box described by a geohash of less than 8 character precision. -There are different ways to search geographic data using geohashes. -First, a single query using a geohash of reduced precision will search a box shaped geographic region. -Searching in a circular shaped region is slightly more complex as it requires multiple queries. -Algorithms to do so, like https://github.com/ashwin711/proximityhash, can be left to the reader to explore. +There are more advanced ways to search geographic data using geohashes. +Searching a circular shaped regions is slightly more complex as it requires multiple queries. +Algorithms to do so, like https://github.com/ashwin711/proximityhash, are left to the reader to explore. ## Rationale -Because of the decentralized nature of ENS, geo-specific resolution is different than traditional GeoDNS. -GeoDNS works as follows. DNS queries are identified by their source IP address. -This IP is looked up in a database like GeoIP2 from MaxMind which maps the IP address to a location. -This method of locating the source of a query is error prone and unreliable. -If the GeoIP database is out of date, queried locations can be vastly different than their true location. -GeoENS does not rely on a database because the user includes a location in their query. +The proposed implementation uses a sparse Quadtree [1] trie as an index for +resource records as it has low storage overhead and good search performance. +The leaf nodes of the tree store resource records while non-leaves represent one geohash character. +Each node in the tree at depth d corresponds to a geohash of precision d. +The tree has depth 8 because the maximum precision of a geohash is 8 characters. +The tree has fanout 32 because the radix of a geohash character is 32. +The path to get to a leaf node always has depth 8 and the leaf contains the content (like IP address) +of the geohash represented by the path to the leaf. +The tree is sparse as 71% of the Earth's surface is covered by water. +The tree facilitates common traversal algorithms (DFS, BFS) to return +lists of resource records within a geographic bounding box. -An additional shortcoming of traditional DNS is the fact that there is no way to return a list of servers in a certain proximity. -This is paramount for uses cases that require discovering the resource with the lowest latency. -GeoENS allows a list of resources, like IP addresses, to be gathered within a specific location. -Then a client to determine themselves which resource has the lowest latency. - -Another benefit of GeoENS is that queries can be made for any location. -Traditional DNS will only return the resource assigned to a query's provenance. -GeoENS does not correlate a query's source with a location, allowing the entire globe to be queried from one location. - -Lastly, publicly facing GeoDNS services do not give fine granularity control over geographic regions for GeoDNS queries. -Cloud based DNS services like Amazon's Route 53 only allow specifying geographic regions at the granularity of a State in the United States. -This course granularity could be due to the unreliability of the underlying databases mapping IP to location. -GeoENS on the other hand gives 64 bits of geohash resolution which is sub-meter accuracy. ## Backwards Compatibility @@ -98,69 +128,187 @@ This EIP does not introduce issues with backwards compatibility. ## Test Cases -See https://github.com/james-choncholas/geoens/tree/master/test +See https://github.com/james-choncholas/resolvers/blob/master/test/TestPublicResolver.js ## Implementation -This single owner address resolver, written in Solidity, implements the specifications outlined above. -The same idea presented here can be applied to public resolvers as specified in EIP137. +This address resolver, written in Solidity, implements the specifications outlined above. +The same idea presented here can be applied to other resolver interfaces as specified in EIP137. Note that geohashes are passed and stored using 64 bit unsigned integers. Using integers instead of strings for geohashes is more performant, especially in the `geomap` mapping. For comparison purposes, see https://github.com/james-choncholas/geoens/tree/master/contracts/StringOwnedGeoENSResolver.sol for the inefficient string implementation. ```solidity -pragma solidity >=0.4.21 <0.6.0; +pragma solidity ^0.5.0; -contract OwnedGeoENSResolver { - // Other code +import "../ResolverBase.sol"; - bytes4 constant ERC165ID = 0x01ffc9a7; - bytes4 constant ERCTBDID = 0xc5505b25; +contract GeoENSResolver is ResolverBase { + bytes4 constant ERC2390 = 0xa263115e; uint constant MAX_ADDR_RETURNS = 64; - mapping(uint64=>address) geomap; - address public owner = msg.sender; - - event AddrChanged(bytes32 indexed node, uint64 geohash, address addr); + uint constant TREE_VISITATION_QUEUESZ = 64; + uint8 constant ASCII_0 = 48; + uint8 constant ASCII_9 = 57; + uint8 constant ASCII_a = 97; + uint8 constant ASCII_b = 98; + uint8 constant ASCII_i = 105; + uint8 constant ASCII_l = 108; + uint8 constant ASCII_o = 111; + uint8 constant ASCII_z = 122; + + struct Node { + address data; // 0 if not leaf + uint256 parent; + uint256[] children; // always length 32 + } - modifier isOwner() { - require(msg.sender == owner); - _; + // A geohash is 8, base-32 characters. + // A geomap is stored as tree of fan-out 32 (because + // geohash is base 32) and height 8 (because geohash + // length is 8 characters) + mapping(bytes32=>Node[]) private geomap; + + event GeoENSRecordChanged(bytes32 indexed node, string geohash, address addr); + + // only 5 bits of ret value are used + function chartobase32(byte c) pure internal returns (uint8 b) { + uint8 ascii = uint8(c); + require( (ascii >= ASCII_0 && ascii <= ASCII_9) || + (ascii > ASCII_a && ascii <= ASCII_z)); + require(ascii != ASCII_a); + require(ascii != ASCII_i); + require(ascii != ASCII_l); + require(ascii != ASCII_o); + + if (ascii <= (ASCII_0 + 9)) { + b = ascii - ASCII_0; + + } else { + // base32 b = 10 + // ascii 'b' = 0x60 + // note base32 skips the letter 'a' + b = ascii - ASCII_b + 10; + + // base32 also skips the following letters + if (ascii > ASCII_i) + b --; + if (ascii > ASCII_l) + b --; + if (ascii > ASCII_o) + b --; + } + require(b < 32); // base 32 cant be larger than 32 + return b; } - function geoAddr(bytes32 node, uint64 geohash, uint64 precisionMask) external view returns (address[] memory ret) { + function geoAddr(bytes32 node, string calldata geohash) external view returns (address[] memory ret) { bytes32(node); // single node georesolver ignores node + require(bytes(geohash).length < 9); // 8 characters = +-1.9 meter resolution ret = new address[](MAX_ADDR_RETURNS); + if (geomap[node].length == 0) { return ret; } uint ret_i = 0; - for(uint64 i=(geohash & precisionMask); i<= geohash + ~precisionMask; i++) { - if (geomap[i] != address(0)) { - ret[ret_i] = geomap[i]; + // walk into the geomap data structure + uint pointer = 0; // not actual pointer but index into geomap + for(uint i=0; i < bytes(geohash).length; i++) { + + uint8 c = chartobase32(bytes(geohash)[i]); + uint next = geomap[node][pointer].children[c]; + if (next == 0) { + // nothing found for this geohash. + // return early. + return ret; + } else { + pointer = next; + } + } + + // pointer is now node representing the resolution of the query geohash. + // DFS until all addresses found or ret[] is full. + // Do not use recursion because this is a blockchain... + uint[] memory indexes_to_visit = new uint[](TREE_VISITATION_QUEUESZ); + indexes_to_visit[0] = pointer; + uint front_i = 0; + uint back_i = 1; + + while(front_i != back_i) { + Node memory cur_node = geomap[node][indexes_to_visit[front_i]]; + front_i ++; + + // if not a leaf node... + if (cur_node.data == address(0)) { + // visit all the chilin's + for(uint i=0; i MAX_ADDR_RETURNS) break; } - if (ret_i > MAX_ADDR_RETURNS) break; } return ret; } - function setGeoAddr(bytes32 node, uint64 geohash, address addr) external isOwner() { - // single node georesolver ignores node - geomap[geohash] = addr; - emit AddrChanged(node, geohash, addr); - } + // when setting, geohash must be precise to 8 digits. + function setGeoAddr(bytes32 node, string calldata geohash, address addr) external authorised(node) { + bytes32(node); // single node georesolver ignores node + require(bytes(geohash).length == 8); // 8 characters = +-1.9 meter resolution + + // create root node if not yet created + if (geomap[node].length == 0) { + geomap[node].push( Node({ + data: address(0), + parent: 0, + children: new uint256[](32) + })); + } - function supportsInterface(bytes4 interfaceID) external pure returns (bool) { - return interfaceID == ERC165ID || interfaceID == ERCTBDID; + // walk into the geomap data structure + uint pointer = 0; // not actual pointer but index into geomap + for(uint i=0; i < bytes(geohash).length; i++) { + + uint8 c = chartobase32(bytes(geohash)[i]); + + if (geomap[node][pointer].children[c] == 0) { + // nothing found for this geohash. + // we need to create a path to the leaf + geomap[node].push( Node({ + data: address(0), + parent: pointer, + children: new uint256[](32) + })); + geomap[node][pointer].children[c] = geomap[node].length - 1; + } + pointer = geomap[node][pointer].children[c]; + } + + Node storage cur_node = geomap[node][pointer]; // storage = get reference + cur_node.data = addr; + + emit GeoENSRecordChanged(node, geohash, addr); } - function() external payable { - revert(); + function supportsInterface(bytes4 interfaceID) public pure returns (bool) { + return interfaceID == ERC2390 || super.supportsInterface(interfaceID); } } ``` +# References + +[1] https://dl.acm.org/doi/10.1007/BF00288933 +[2] https://www.maxmind.com/en/geoip2-services-and-databases +[3] https://aws.amazon.com/route53/ # Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 5c92c6f0f3f9eb54cbb1e226f776b07de601dd59 Mon Sep 17 00:00:00 2001 From: James Choncholas <34432426+james-choncholas@users.noreply.github.com> Date: Wed, 9 Sep 2020 12:48:43 -0400 Subject: [PATCH 04/11] Apply suggestions from code review Co-authored-by: Micah Zoltu --- EIPS/eip-2390.md | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/EIPS/eip-2390.md b/EIPS/eip-2390.md index 892648f8a8c2cd..6132f8dca910ba 100644 --- a/EIPS/eip-2390.md +++ b/EIPS/eip-2390.md @@ -1,24 +1,19 @@ --- eip: 2390 title: Geo-ENS -author: James Choncholas +author: James Choncholas (@james-choncholas) discussions-to: https://github.com/ethereum/EIPs/pull/2390 status: Draft type: Standards Track category: ERC created: 2019-11-15 -requires ( * optional): EIP137, EIP165, EIP1062, EIP1185 +requires: 137, 165, 1062, 1185 --- - - - ## Simple Summary - GeoENS brings geographic split horizon capabilities to ENS. It's GeoDNS for ENS! ## Abstract - This EIP specifies an ENS resolver interface for geographically split horizon DNS. Geographic split horizon DNS returns resource records that are specific to an end user's location. @@ -28,7 +23,6 @@ resolvers storing DNS resource records (EIP1185), although the technique could b used on other interfaces like IPFS content hash storage (EIP1162). ## Motivation - There are many use cases for traditional GeoDNS systems, like Amazon's Route53, in the centralized web. These use cases include proximity-based load balancing and serving content @@ -73,7 +67,6 @@ corresponds to +-20 meter accuracy. ## Specification - This EIP proposes a new interface to ENS resolvers such that geo-spacial information can be recorded and retrieved from the blockchain. The interface changes are described below for "address resolvers" described in EIP137 @@ -83,7 +76,7 @@ Resolvers, Text Resolvers, ABI Resolvers, etc. ### What is a geohash? Geohashes are typically encoded in base 32 characters, as described https://en.m.wikipedia.org/wiki/Geohash#Algorithm_and_example. -A geohash is an interleaving of latitude and longitude bits, whos +A geohash is an interleaving of latitude and longitude bits, whose length determines it's precision. @@ -107,7 +100,6 @@ Searching a circular shaped regions is slightly more complex as it requires mult Algorithms to do so, like https://github.com/ashwin711/proximityhash, are left to the reader to explore. ## Rationale - The proposed implementation uses a sparse Quadtree [1] trie as an index for resource records as it has low storage overhead and good search performance. The leaf nodes of the tree store resource records while non-leaves represent one geohash character. @@ -120,19 +112,13 @@ The tree is sparse as 71% of the Earth's surface is covered by water. The tree facilitates common traversal algorithms (DFS, BFS) to return lists of resource records within a geographic bounding box. - - ## Backwards Compatibility - This EIP does not introduce issues with backwards compatibility. ## Test Cases - See https://github.com/james-choncholas/resolvers/blob/master/test/TestPublicResolver.js ## Implementation - - This address resolver, written in Solidity, implements the specifications outlined above. The same idea presented here can be applied to other resolver interfaces as specified in EIP137. Note that geohashes are passed and stored using 64 bit unsigned integers. @@ -199,7 +185,7 @@ contract GeoENSResolver is ResolverBase { if (ascii > ASCII_o) b --; } - require(b < 32); // base 32 cant be larger than 32 + require(b < 32); // base 32 can't be larger than 32 return b; } @@ -304,11 +290,11 @@ contract GeoENSResolver is ResolverBase { } } ``` -# References +## References [1] https://dl.acm.org/doi/10.1007/BF00288933 [2] https://www.maxmind.com/en/geoip2-services-and-databases [3] https://aws.amazon.com/route53/ -# Copyright +## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 41b42cd64d2f6039277754372059741180a15f22 Mon Sep 17 00:00:00 2001 From: James Choncholas <34432426+james-choncholas@users.noreply.github.com> Date: Wed, 9 Sep 2020 13:00:18 -0400 Subject: [PATCH 05/11] Incorperate more changes suggested by @MicahZoltu Flatten references and point discussion to new GitHub Issue --- EIPS/eip-2390.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/EIPS/eip-2390.md b/EIPS/eip-2390.md index 6132f8dca910ba..38cd8e9c88b387 100644 --- a/EIPS/eip-2390.md +++ b/EIPS/eip-2390.md @@ -2,7 +2,7 @@ eip: 2390 title: Geo-ENS author: James Choncholas (@james-choncholas) -discussions-to: https://github.com/ethereum/EIPs/pull/2390 +discussions-to: https://github.com/ethereum/EIPs/issues/2959 status: Draft type: Standards Track category: ERC @@ -43,7 +43,8 @@ In addition to geographic split horizon DNS, GeoENS can be used for the followin Because of the decentralized nature of ENS, geo-specific resolution is different than traditional GeoDNS. GeoDNS works as follows. DNS queries are identified by their source IP address. -This IP is looked up in a database like GeoIP2 [2] from MaxMind which maps the IP address to a location. +This IP is looked up in a database like [GeoIP2](https://www.maxmind.com/en/geoip2-services-and-databases) +from MaxMind which maps the IP address to a location. This method of locating the source of a query is error prone and unreliable. If the GeoIP database is out of date, queried locations can be vastly different than their true location. GeoENS does not rely on a database because the user includes a location in their query. @@ -60,8 +61,9 @@ Then a client to determine themselves which resource has the lowest latency. Lastly, publicly facing GeoDNS services do not give fine granularity control over geographic regions for GeoDNS queries. -Cloud based DNS services like Amazon's Route 53 [3] only allow specifying -geographic regions at the granularity of a State in the United States. +Cloud based DNS services like [Amazon's Route 53](https://aws.amazon.com/route53/) +only allow specifying geographic regions at the granularity of a State in +the United States. GeoENS on the other hand gives 8 characters of geohash resolution which corresponds to +-20 meter accuracy. @@ -100,7 +102,7 @@ Searching a circular shaped regions is slightly more complex as it requires mult Algorithms to do so, like https://github.com/ashwin711/proximityhash, are left to the reader to explore. ## Rationale -The proposed implementation uses a sparse Quadtree [1] trie as an index for +The proposed implementation uses a sparse [Quadtree](https://dl.acm.org/doi/10.1007/BF00288933) trie as an index for resource records as it has low storage overhead and good search performance. The leaf nodes of the tree store resource records while non-leaves represent one geohash character. Each node in the tree at depth d corresponds to a geohash of precision d. @@ -290,11 +292,6 @@ contract GeoENSResolver is ResolverBase { } } ``` -## References - -[1] https://dl.acm.org/doi/10.1007/BF00288933 -[2] https://www.maxmind.com/en/geoip2-services-and-databases -[3] https://aws.amazon.com/route53/ ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From d868b9284216ec273edec44167187473382fd9e0 Mon Sep 17 00:00:00 2001 From: James Choncholas <34432426+james-choncholas@users.noreply.github.com> Date: Fri, 11 Sep 2020 10:06:53 -0400 Subject: [PATCH 06/11] Apply suggestions from code review Co-authored-by: Micah Zoltu --- EIPS/eip-2390.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/EIPS/eip-2390.md b/EIPS/eip-2390.md index 38cd8e9c88b387..9ad3dfef604906 100644 --- a/EIPS/eip-2390.md +++ b/EIPS/eip-2390.md @@ -67,7 +67,6 @@ the United States. GeoENS on the other hand gives 8 characters of geohash resolution which corresponds to +-20 meter accuracy. - ## Specification This EIP proposes a new interface to ENS resolvers such that geo-spacial information can be recorded and retrieved from the blockchain. @@ -293,5 +292,8 @@ contract GeoENSResolver is ResolverBase { } ``` +## Security Considerations +TBD + ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 1b4d2c746a4653132a954160e43ea1e5e616326e Mon Sep 17 00:00:00 2001 From: James Choncholas <34432426+james-choncholas@users.noreply.github.com> Date: Fri, 11 Sep 2020 10:30:53 -0400 Subject: [PATCH 07/11] Update eip-2390.md Add security considerations --- EIPS/eip-2390.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-2390.md b/EIPS/eip-2390.md index 9ad3dfef604906..2567e8c5d45fc9 100644 --- a/EIPS/eip-2390.md +++ b/EIPS/eip-2390.md @@ -91,7 +91,7 @@ Write default initialized resource value, `address(0)`, to remove a resource fro Query the resolver contract for a specific node and location. All resources (contract addresses, IP addresses, ABIs, TEXT records, etc.) matching the node and prefix geohash provided are returned. -This permits querying by exact geohash of 8 characters to return the contect at that location, +This permits querying by exact geohash of 8 characters to return the content at that location, or querying by geographic bounding box described by a geohash of less than 8 character precision. @@ -293,7 +293,23 @@ contract GeoENSResolver is ResolverBase { ``` ## Security Considerations -TBD +This contract has similar functionality to ENS Resolvers - refer there for security considerations. +Additionally, this contract has a dimension of data privacy. +Users query via the geoAddr function specifying a geohash of less than 8 characters +which defines the query region. +Users who run light clients leak the query region to their connected full-nodes. +Users who rely on nodes run by third parties (like Infura) will also leak +the query region. +Users who run their own full node or have access to a trusted full node do +not leak any location data. + +Given the way most location services work, the query region is likely to contain +the user's actual location. +The difference between API access, light, and full nodes has always had +an impact on privacy but now the impact is underscored by the involvement +of coarse granularity user location. + + ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From f5b3e8da0a8134f61c4df6fc0bd61bba12157823 Mon Sep 17 00:00:00 2001 From: James Choncholas <34432426+james-choncholas@users.noreply.github.com> Date: Fri, 11 Sep 2020 10:46:28 -0400 Subject: [PATCH 08/11] Update eip-2390.md Improve formatting and add note about different types of geohash --- EIPS/eip-2390.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/EIPS/eip-2390.md b/EIPS/eip-2390.md index 2567e8c5d45fc9..d07dcebf2ea64b 100644 --- a/EIPS/eip-2390.md +++ b/EIPS/eip-2390.md @@ -19,8 +19,8 @@ Geographic split horizon DNS returns resource records that are specific to an en user's location. This technique is commonly used by CDNs to direct traffic to content caches nearest users. Geographic split horizon resolution is primarily geared towards ENS -resolvers storing DNS resource records (EIP1185), although the technique could be -used on other interfaces like IPFS content hash storage (EIP1162). +resolvers storing DNS resource records [EIP-1185](./eip-1185.md), although the technique could be +used on other interfaces like IPFS content hash storage [EIP-1162](./eip-1162.md). ## Motivation There are many use cases for traditional GeoDNS systems, like Amazon's Route53, @@ -29,7 +29,7 @@ These use cases include proximity-based load balancing and serving content specific to the geographic location of the query. Unfortunately the ENS specification does not provide a mechanism for geo-specific resolution. -ENS can respond to queries with IP addresses (as described in EIP1185) +ENS can respond to queries with IP addresses (as described in [EIP-1185](./eip-1185.md)) however there is no way to respond to geo-specific queries. This EIP proposes a standard to give the ENS system geo-proximal awareness to serve a similar purpose as GeoDNS. @@ -38,7 +38,7 @@ GeoENS can do more than DNS-based solutions. In addition to geographic split horizon DNS, GeoENS can be used for the following: - Locating digital resources (like smart contracts) that represent physical objects in the real world. - Smart contract managing access to a physical object associated with a specific location. - - ENS + IPFS web hosting (as described in EIP1162) with content translated to the native language of the query source. + - ENS + IPFS web hosting (as described in [EIP-1162](./eip-1162.md)) with content translated to the native language of the query source. - Tokenizing objects with a physical location. Because of the decentralized nature of ENS, geo-specific resolution is different than traditional GeoDNS. @@ -75,11 +75,10 @@ however the idea applies to any record described in EIP1185 and EIP1062, namely Resolvers, Text Resolvers, ABI Resolvers, etc. ### What is a geohash? -Geohashes are typically encoded in base 32 characters, as described -https://en.m.wikipedia.org/wiki/Geohash#Algorithm_and_example. -A geohash is an interleaving of latitude and longitude bits, whose +A [Geohash](https://en.m.wikipedia.org/wiki/Geohash#Algorithm_and_example) +is an interleaving of latitude and longitude bits, whose length determines it's precision. - +Geohashes are typically encoded in base 32 characters. ### function setGeoAddr(bytes32 node, string calldata geohash, address addr) external authorised(node) Sets a resource (contract address, IP, ABI, TEXT, etc.) by node and geohash. @@ -94,11 +93,14 @@ the node and prefix geohash provided are returned. This permits querying by exact geohash of 8 characters to return the content at that location, or querying by geographic bounding box described by a geohash of less than 8 character precision. - - -There are more advanced ways to search geographic data using geohashes. -Searching a circular shaped regions is slightly more complex as it requires multiple queries. -Algorithms to do so, like https://github.com/ashwin711/proximityhash, are left to the reader to explore. +Any type of geohash can be used including [Z-order](https://en.wikipedia.org/wiki/Z-order_curve) +[Hilbert](https://en.wikipedia.org/wiki/Hilbert_curve) or the more accurate +[S2 Geometry](https://s2geometry.io/devguide/s2cell_hierarchy.html) library +from Google. +There are also ways to search the geographic data using geohashes without +always ending up with a rectangular query region. +[Searching circular shaped regions](https://github.com/ashwin711/proximityhash) is +slightly more complex as it requires multiple queries. ## Rationale The proposed implementation uses a sparse [Quadtree](https://dl.acm.org/doi/10.1007/BF00288933) trie as an index for From 382c90848e402709dda8e60bbeb79d589155c7eb Mon Sep 17 00:00:00 2001 From: James Choncholas <34432426+james-choncholas@users.noreply.github.com> Date: Fri, 11 Sep 2020 13:09:34 -0400 Subject: [PATCH 09/11] Reference implementation geohash uses bytes not string --- EIPS/eip-2390.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/EIPS/eip-2390.md b/EIPS/eip-2390.md index d07dcebf2ea64b..56d2229f0c7ecc 100644 --- a/EIPS/eip-2390.md +++ b/EIPS/eip-2390.md @@ -135,7 +135,7 @@ pragma solidity ^0.5.0; import "../ResolverBase.sol"; contract GeoENSResolver is ResolverBase { - bytes4 constant ERC2390 = 0xa263115e; + bytes4 constant ERC2390 = 0x8fbcc5ce; uint constant MAX_ADDR_RETURNS = 64; uint constant TREE_VISITATION_QUEUESZ = 64; uint8 constant ASCII_0 = 48; @@ -159,7 +159,7 @@ contract GeoENSResolver is ResolverBase { // length is 8 characters) mapping(bytes32=>Node[]) private geomap; - event GeoENSRecordChanged(bytes32 indexed node, string geohash, address addr); + event GeoENSRecordChanged(bytes32 indexed node, bytes8 geohash, address addr); // only 5 bits of ret value are used function chartobase32(byte c) pure internal returns (uint8 b) { @@ -188,13 +188,13 @@ contract GeoENSResolver is ResolverBase { if (ascii > ASCII_o) b --; } - require(b < 32); // base 32 can't be larger than 32 + require(b < 32); // base 32 cant be larger than 32 return b; } - function geoAddr(bytes32 node, string calldata geohash) external view returns (address[] memory ret) { + function geoAddr(bytes32 node, bytes8 geohash, uint8 precision) external view returns (address[] memory ret) { bytes32(node); // single node georesolver ignores node - require(bytes(geohash).length < 9); // 8 characters = +-1.9 meter resolution + assert(precision <= geohash.length); ret = new address[](MAX_ADDR_RETURNS); if (geomap[node].length == 0) { return ret; } @@ -202,9 +202,9 @@ contract GeoENSResolver is ResolverBase { // walk into the geomap data structure uint pointer = 0; // not actual pointer but index into geomap - for(uint i=0; i < bytes(geohash).length; i++) { + for(uint8 i=0; i < precision; i++) { - uint8 c = chartobase32(bytes(geohash)[i]); + uint8 c = chartobase32(geohash[i]); uint next = geomap[node][pointer].children[c]; if (next == 0) { // nothing found for this geohash. @@ -217,7 +217,7 @@ contract GeoENSResolver is ResolverBase { // pointer is now node representing the resolution of the query geohash. // DFS until all addresses found or ret[] is full. - // Do not use recursion because this is a blockchain... + // Do not use recursion because blockchain... uint[] memory indexes_to_visit = new uint[](TREE_VISITATION_QUEUESZ); indexes_to_visit[0] = pointer; uint front_i = 0; @@ -229,7 +229,7 @@ contract GeoENSResolver is ResolverBase { // if not a leaf node... if (cur_node.data == address(0)) { - // visit all the chilin's + // visit all the chilins for(uint i=0; i Date: Sat, 12 Sep 2020 13:53:17 -0400 Subject: [PATCH 10/11] Apply suggestions from code review Co-authored-by: Micah Zoltu --- EIPS/eip-2390.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-2390.md b/EIPS/eip-2390.md index 56d2229f0c7ecc..91b12ef7a1a0eb 100644 --- a/EIPS/eip-2390.md +++ b/EIPS/eip-2390.md @@ -188,7 +188,7 @@ contract GeoENSResolver is ResolverBase { if (ascii > ASCII_o) b --; } - require(b < 32); // base 32 cant be larger than 32 + require(b < 32); // base 32 can't be larger than 32 return b; } From 0d84edfb88fa1cf706ea5e1fddfd7f6adb7c51ce Mon Sep 17 00:00:00 2001 From: James Choncholas <34432426+james-choncholas@users.noreply.github.com> Date: Sat, 12 Sep 2020 13:58:02 -0400 Subject: [PATCH 11/11] Fix typo reference to EIP1062 --- EIPS/eip-2390.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-2390.md b/EIPS/eip-2390.md index 91b12ef7a1a0eb..832efb82c8a5f9 100644 --- a/EIPS/eip-2390.md +++ b/EIPS/eip-2390.md @@ -20,7 +20,7 @@ user's location. This technique is commonly used by CDNs to direct traffic to content caches nearest users. Geographic split horizon resolution is primarily geared towards ENS resolvers storing DNS resource records [EIP-1185](./eip-1185.md), although the technique could be -used on other interfaces like IPFS content hash storage [EIP-1162](./eip-1162.md). +used on other interfaces like IPFS content hash storage [EIP-1062](./eip-1062.md). ## Motivation There are many use cases for traditional GeoDNS systems, like Amazon's Route53, @@ -38,7 +38,7 @@ GeoENS can do more than DNS-based solutions. In addition to geographic split horizon DNS, GeoENS can be used for the following: - Locating digital resources (like smart contracts) that represent physical objects in the real world. - Smart contract managing access to a physical object associated with a specific location. - - ENS + IPFS web hosting (as described in [EIP-1162](./eip-1162.md)) with content translated to the native language of the query source. + - ENS + IPFS web hosting (as described in [EIP-1062](./eip-1062.md)) with content translated to the native language of the query source. - Tokenizing objects with a physical location. Because of the decentralized nature of ENS, geo-specific resolution is different than traditional GeoDNS.