Преглед изворни кода

contracts, swarm: implement EIP-1577 (#19285)

* contracts/ens: update public resolver solidity code

* contracts/ens: update public resolver, update go bindings

* update build

* fix ens.sol

* contracts/ens: change contract interface

* contracts/ens: implement public resolver changes

* contracts/ens: added ENSRegistry contract

* contracts/ens: reinstate old contract code

* contracts/ens: update README.md

* contracts/ens: added test coverage for fallback contract

* contracts/ens: added support for fallback contract

* contracts/ens: removed unused contract code

* contracts/ens: add todo and decode multicodec stub

* add encode

* vendor: add ipfs cid libraries

* contracts/ens: cid sanity tests

* contracts/ens: more cid sanity checks

* contracts/ens: wip integration

* wip

* Revert "vendor: add ipfs cid libraries"

This reverts commit 29d9b6b294ded903a1065d96c8149119713cfd12.

* contracts/ens: removed multiformats dependencies

* contracts/ens: added decode tests

* contracts/ens: added eip spec test, minor changes to exiting tests

* contracts/ens: moved cid decoding to own file

* contracts/ens: added unit test to encode hash to content hash

* contracts/ens: removed unused code

* contracts/ens: fix ens tests to use cid decode and encode

* contracts/ens: adjust swarm multicodecs after pr merge

* contracts/ens: fix linter error

* constracts/ens: address PR comments

* cmd, contracts: make peoples lives easier

* contracts/ens: fix linter error

* contracts/ens: address PR comments
Elad пре 6 година
родитељ
комит
e7d1867964

+ 58 - 1
cmd/swarm/hash.go

@@ -19,10 +19,13 @@ package main
 
 import (
 	"context"
+	"encoding/hex"
 	"fmt"
 	"os"
 
 	"github.com/ethereum/go-ethereum/cmd/utils"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/contracts/ens"
 	"github.com/ethereum/go-ethereum/swarm/storage"
 	"gopkg.in/urfave/cli.v1"
 )
@@ -34,7 +37,33 @@ var hashCommand = cli.Command{
 	Usage:              "print the swarm hash of a file or directory",
 	ArgsUsage:          "<file>",
 	Description:        "Prints the swarm hash of file or directory",
-}
+	Subcommands: []cli.Command{
+		{
+			CustomHelpTemplate: helpTemplate,
+			Name:               "ens",
+			Usage:              "converts a swarm hash to an ens EIP1577 compatible CIDv1 hash",
+			ArgsUsage:          "<ref>",
+			Description:        "",
+			Subcommands: []cli.Command{
+				{
+					Action:             encodeEipHash,
+					CustomHelpTemplate: helpTemplate,
+					Name:               "contenthash",
+					Usage:              "converts a swarm hash to an ens EIP1577 compatible CIDv1 hash",
+					ArgsUsage:          "<ref>",
+					Description:        "",
+				},
+				{
+					Action:             ensNodeHash,
+					CustomHelpTemplate: helpTemplate,
+					Name:               "node",
+					Usage:              "converts an ens name to an ENS node hash",
+					ArgsUsage:          "<ref>",
+					Description:        "",
+				},
+			},
+		},
+	}}
 
 func hash(ctx *cli.Context) {
 	args := ctx.Args()
@@ -56,3 +85,31 @@ func hash(ctx *cli.Context) {
 		fmt.Printf("%v\n", addr)
 	}
 }
+func ensNodeHash(ctx *cli.Context) {
+	args := ctx.Args()
+	if len(args) < 1 {
+		utils.Fatalf("Usage: swarm hash ens node <ens name>")
+	}
+	ensName := args[0]
+
+	hash := ens.EnsNode(ensName)
+
+	stringHex := hex.EncodeToString(hash[:])
+	fmt.Println(stringHex)
+}
+func encodeEipHash(ctx *cli.Context) {
+	args := ctx.Args()
+	if len(args) < 1 {
+		utils.Fatalf("Usage: swarm hash ens <swarm hash>")
+	}
+	swarmHash := args[0]
+
+	hash := common.HexToHash(swarmHash)
+	ensHash, err := ens.EncodeSwarmHash(hash)
+	if err != nil {
+		utils.Fatalf("error converting swarm hash", err)
+	}
+
+	stringHex := hex.EncodeToString(ensHash)
+	fmt.Println(stringHex)
+}

+ 10 - 0
contracts/ens/README.md

@@ -18,3 +18,13 @@ The go bindings for ENS contracts are generated using `abigen` via the go genera
 ```shell
 go generate ./contracts/ens
 ```
+
+## Fallback contract support
+
+In order to better support content resolution on different service providers (such as Swarm and IPFS), [EIP-1577](https://eips.ethereum.org/EIPS/eip-1577)
+was introduced and with it changes that allow applications to know _where_ content hashes are stored (i.e. if the
+requested hash resides on Swarm or IPFS).
+
+The code under `contracts/ens/contract` reflects the new Public Resolver changes and the code under `fallback_contract` allows
+us to support the old contract resolution in cases where the ENS name owner did not update her Resolver contract, until the migration
+period ends (date arbitrarily set to June 1st, 2019).

+ 121 - 0
contracts/ens/cid.go

@@ -0,0 +1,121 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package ens
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+
+	"github.com/ethereum/go-ethereum/common"
+)
+
+const (
+	cidv1 = 0x1
+
+	nsIpfs  = 0xe3
+	nsSwarm = 0xe4
+
+	swarmTypecode = 0xfa //swarm manifest, see https://github.com/multiformats/multicodec/blob/master/table.csv
+	swarmHashtype = 0xd6 // BMT, see https://github.com/multiformats/multicodec/blob/master/table.csv
+
+	hashLength = 32
+)
+
+// deocodeEIP1577ContentHash decodes a chain-stored content hash from an ENS record according to EIP-1577
+// a successful decode will result the different parts of the content hash in accordance to the CID spec
+// Note: only CIDv1 is supported
+func decodeEIP1577ContentHash(buf []byte) (storageNs, contentType, hashType, hashLength uint64, hash []byte, err error) {
+	if len(buf) < 10 {
+		return 0, 0, 0, 0, nil, errors.New("buffer too short")
+	}
+
+	storageNs, n := binary.Uvarint(buf)
+
+	buf = buf[n:]
+	vers, n := binary.Uvarint(buf)
+
+	if vers != 1 {
+		return 0, 0, 0, 0, nil, fmt.Errorf("expected cid v1, got: %d", vers)
+	}
+	buf = buf[n:]
+	contentType, n = binary.Uvarint(buf)
+
+	buf = buf[n:]
+	hashType, n = binary.Uvarint(buf)
+
+	buf = buf[n:]
+	hashLength, n = binary.Uvarint(buf)
+
+	hash = buf[n:]
+
+	if len(hash) != int(hashLength) {
+		return 0, 0, 0, 0, nil, errors.New("hash length mismatch")
+	}
+	return storageNs, contentType, hashType, hashLength, hash, nil
+}
+
+func extractContentHash(buf []byte) (common.Hash, error) {
+	storageNs, _ /*contentType*/, _ /* hashType*/, decodedHashLength, hashBytes, err := decodeEIP1577ContentHash(buf)
+
+	if err != nil {
+		return common.Hash{}, err
+	}
+
+	if storageNs != nsSwarm {
+		return common.Hash{}, errors.New("unknown storage system")
+	}
+
+	//todo: for the time being we implement loose enforcement for the EIP rules until ENS manager is updated
+	/*if contentType != swarmTypecode {
+		return common.Hash{}, errors.New("unknown content type")
+	}
+
+	if hashType != swarmHashtype {
+		return common.Hash{}, errors.New("unknown multihash type")
+	}*/
+
+	if decodedHashLength != hashLength {
+		return common.Hash{}, errors.New("odd hash length, swarm expects 32 bytes")
+	}
+
+	if len(hashBytes) != int(hashLength) {
+		return common.Hash{}, errors.New("hash length mismatch")
+	}
+
+	return common.BytesToHash(buf), nil
+}
+
+func EncodeSwarmHash(hash common.Hash) ([]byte, error) {
+	var cidBytes []byte
+	var headerBytes = []byte{
+		nsSwarm,       //swarm namespace
+		cidv1,         // CIDv1
+		swarmTypecode, // swarm hash
+		swarmHashtype, // swarm bmt hash
+		hashLength,    //hash length. 32 bytes
+	}
+
+	varintbuf := make([]byte, binary.MaxVarintLen64)
+	for _, v := range headerBytes {
+		n := binary.PutUvarint(varintbuf, uint64(v))
+		cidBytes = append(cidBytes, varintbuf[:n]...)
+	}
+
+	cidBytes = append(cidBytes, hash[:]...)
+	return cidBytes, nil
+}

+ 158 - 0
contracts/ens/cid_test.go

@@ -0,0 +1,158 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package ens
+
+import (
+	"bytes"
+	"encoding/binary"
+	"encoding/hex"
+	"fmt"
+	"testing"
+
+	"github.com/ethereum/go-ethereum/common"
+)
+
+// Tests for the decoding of the example ENS
+func TestEIPSpecCidDecode(t *testing.T) {
+	const (
+		eipSpecHash = "e3010170122029f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f"
+		eipHash     = "29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f"
+		dagPb       = 0x70
+		sha2256     = 0x12
+	)
+	b, err := hex.DecodeString(eipSpecHash)
+	if err != nil {
+		t.Fatal(err)
+	}
+	hashBytes, err := hex.DecodeString(eipHash)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	storageNs, contentType, hashType, hashLength, decodedHashBytes, err := decodeEIP1577ContentHash(b)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	if storageNs != nsIpfs {
+		t.Fatal("wrong ns")
+	}
+	if contentType != dagPb {
+		t.Fatal("should be ipfs typecode")
+	}
+	if hashType != sha2256 {
+		t.Fatal("should be sha2-256")
+	}
+	if hashLength != 32 {
+		t.Fatal("should be 32")
+	}
+	if !bytes.Equal(hashBytes, decodedHashBytes) {
+		t.Fatal("should be equal")
+	}
+
+}
+func TestManualCidDecode(t *testing.T) {
+	// call cid encode method with hash. expect byte slice returned, compare according to spec
+
+	for _, v := range []struct {
+		name        string
+		headerBytes []byte
+		wantErr     bool
+	}{
+		{
+			name:        "values correct, should not fail",
+			headerBytes: []byte{0xe4, 0x01, 0xfa, 0xd6, 0x20},
+			wantErr:     false,
+		},
+		{
+			name:        "cid version wrong, should fail",
+			headerBytes: []byte{0xe4, 0x00, 0xfa, 0xd6, 0x20},
+			wantErr:     true,
+		},
+		{
+			name:        "hash length wrong, should fail",
+			headerBytes: []byte{0xe4, 0x01, 0xfa, 0xd6, 0x1f},
+			wantErr:     true,
+		},
+		{
+			name:        "values correct for ipfs, should fail",
+			headerBytes: []byte{0xe3, 0x01, 0x70, 0x12, 0x20},
+			wantErr:     true,
+		},
+		{
+			name:        "loose values for swarm, todo remove, should not fail",
+			headerBytes: []byte{0xe4, 0x01, 0x70, 0x12, 0x20},
+			wantErr:     false,
+		},
+		{
+			name:        "loose values for swarm, todo remove, should not fail",
+			headerBytes: []byte{0xe4, 0x01, 0x99, 0x99, 0x20},
+			wantErr:     false,
+		},
+	} {
+		t.Run(v.name, func(t *testing.T) {
+			const eipHash = "29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f"
+
+			var bb []byte
+			buf := make([]byte, binary.MaxVarintLen64)
+			for _, vv := range v.headerBytes {
+				n := binary.PutUvarint(buf, uint64(vv))
+				bb = append(bb, buf[:n]...)
+			}
+
+			h := common.HexToHash(eipHash)
+			bb = append(bb, h[:]...)
+			str := hex.EncodeToString(bb)
+			fmt.Println(str)
+			decodedHash, e := extractContentHash(bb)
+			switch v.wantErr {
+			case true:
+				if e == nil {
+					t.Fatal("the decode should fail")
+				}
+			case false:
+				if e != nil {
+					t.Fatalf("the deccode shouldnt fail: %v", e)
+				}
+				if !bytes.Equal(decodedHash[:], h[:]) {
+					t.Fatal("hashes not equal")
+				}
+			}
+		})
+	}
+}
+
+func TestManuelCidEncode(t *testing.T) {
+	// call cid encode method with hash. expect byte slice returned, compare according to spec
+	const eipHash = "29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f"
+	cidBytes, err := EncodeSwarmHash(common.HexToHash(eipHash))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// logic in extractContentHash is unit tested thoroughly
+	// hence we just check that the returned hash is equal
+	h, err := extractContentHash(cidBytes)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if bytes.Equal(h[:], cidBytes) {
+		t.Fatal("should be equal")
+	}
+}

+ 0 - 23
contracts/ens/contract/AbstractENS.sol

@@ -1,23 +0,0 @@
-pragma solidity ^0.4.0;
-
-contract AbstractENS {
-    function owner(bytes32 node) constant returns(address);
-    function resolver(bytes32 node) constant returns(address);
-    function ttl(bytes32 node) constant returns(uint64);
-    function setOwner(bytes32 node, address owner);
-    function setSubnodeOwner(bytes32 node, bytes32 label, address owner);
-    function setResolver(bytes32 node, address resolver);
-    function setTTL(bytes32 node, uint64 ttl);
-
-    // Logged when the owner of a node assigns a new owner to a subnode.
-    event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
-
-    // Logged when the owner of a node transfers ownership to a new account.
-    event Transfer(bytes32 indexed node, address owner);
-
-    // Logged when the resolver for a node changes.
-    event NewResolver(bytes32 indexed node, address resolver);
-
-    // Logged when the TTL of a node changes
-    event NewTTL(bytes32 indexed node, uint64 ttl);
-}

+ 18 - 86
contracts/ens/contract/ENS.sol

@@ -1,94 +1,26 @@
-pragma solidity ^0.4.0;
+pragma solidity >=0.4.24;
 
-import './AbstractENS.sol';
+interface ENS {
 
-/**
- * The ENS registry contract.
- */
-contract ENS is AbstractENS {
-    struct Record {
-        address owner;
-        address resolver;
-        uint64 ttl;
-    }
+    // Logged when the owner of a node assigns a new owner to a subnode.
+    event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
 
-    mapping(bytes32=>Record) records;
+    // Logged when the owner of a node transfers ownership to a new account.
+    event Transfer(bytes32 indexed node, address owner);
 
-    // Permits modifications only by the owner of the specified node.
-    modifier only_owner(bytes32 node) {
-        if (records[node].owner != msg.sender) throw;
-        _;
-    }
+    // Logged when the resolver for a node changes.
+    event NewResolver(bytes32 indexed node, address resolver);
 
-    /**
-     * Constructs a new ENS registrar.
-     */
-    function ENS() {
-        records[0].owner = msg.sender;
-    }
+    // Logged when the TTL of a node changes
+    event NewTTL(bytes32 indexed node, uint64 ttl);
 
-    /**
-     * Returns the address that owns the specified node.
-     */
-    function owner(bytes32 node) constant returns (address) {
-        return records[node].owner;
-    }
 
-    /**
-     * Returns the address of the resolver for the specified node.
-     */
-    function resolver(bytes32 node) constant returns (address) {
-        return records[node].resolver;
-    }
+    function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external;
+    function setResolver(bytes32 node, address resolver) external;
+    function setOwner(bytes32 node, address owner) external;
+    function setTTL(bytes32 node, uint64 ttl) external;
+    function owner(bytes32 node) external view returns (address);
+    function resolver(bytes32 node) external view returns (address);
+    function ttl(bytes32 node) external view returns (uint64);
 
-    /**
-     * Returns the TTL of a node, and any records associated with it.
-     */
-    function ttl(bytes32 node) constant returns (uint64) {
-        return records[node].ttl;
-    }
-
-    /**
-     * Transfers ownership of a node to a new address. May only be called by the current
-     * owner of the node.
-     * @param node The node to transfer ownership of.
-     * @param owner The address of the new owner.
-     */
-    function setOwner(bytes32 node, address owner) only_owner(node) {
-        Transfer(node, owner);
-        records[node].owner = owner;
-    }
-
-    /**
-     * Transfers ownership of a subnode sha3(node, label) to a new address. May only be
-     * called by the owner of the parent node.
-     * @param node The parent node.
-     * @param label The hash of the label specifying the subnode.
-     * @param owner The address of the new owner.
-     */
-    function setSubnodeOwner(bytes32 node, bytes32 label, address owner) only_owner(node) {
-        var subnode = sha3(node, label);
-        NewOwner(node, label, owner);
-        records[subnode].owner = owner;
-    }
-
-    /**
-     * Sets the resolver address for the specified node.
-     * @param node The node to update.
-     * @param resolver The address of the resolver.
-     */
-    function setResolver(bytes32 node, address resolver) only_owner(node) {
-        NewResolver(node, resolver);
-        records[node].resolver = resolver;
-    }
-
-    /**
-     * Sets the TTL for the specified node.
-     * @param node The node to update.
-     * @param ttl The TTL in seconds.
-     */
-    function setTTL(bytes32 node, uint64 ttl) only_owner(node) {
-        NewTTL(node, ttl);
-        records[node].ttl = ttl;
-    }
-}
+}

+ 99 - 0
contracts/ens/contract/ENSRegistry.sol

@@ -0,0 +1,99 @@
+pragma solidity ^0.5.0;
+
+import "./ENS.sol";
+
+/**
+ * The ENS registry contract.
+ */
+contract ENSRegistry is ENS {
+    struct Record {
+        address owner;
+        address resolver;
+        uint64 ttl;
+    }
+
+    mapping (bytes32 => Record) records;
+
+    // Permits modifications only by the owner of the specified node.
+    modifier only_owner(bytes32 node) {
+        require(records[node].owner == msg.sender);
+        _;
+    }
+
+    /**
+     * @dev Constructs a new ENS registrar.
+     */
+    constructor() public {
+        records[0x0].owner = msg.sender;
+    }
+
+    /**
+     * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node.
+     * @param node The node to transfer ownership of.
+     * @param owner The address of the new owner.
+     */
+    function setOwner(bytes32 node, address owner) external only_owner(node) {
+        emit Transfer(node, owner);
+        records[node].owner = owner;
+    }
+
+    /**
+     * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node.
+     * @param node The parent node.
+     * @param label The hash of the label specifying the subnode.
+     * @param owner The address of the new owner.
+     */
+    function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external only_owner(node) {
+        bytes32 subnode = keccak256(abi.encodePacked(node, label));
+        emit NewOwner(node, label, owner);
+        records[subnode].owner = owner;
+    }
+
+    /**
+     * @dev Sets the resolver address for the specified node.
+     * @param node The node to update.
+     * @param resolver The address of the resolver.
+     */
+    function setResolver(bytes32 node, address resolver) external only_owner(node) {
+        emit NewResolver(node, resolver);   
+        records[node].resolver = resolver;
+    }
+
+    /**
+     * @dev Sets the TTL for the specified node.
+     * @param node The node to update.
+     * @param ttl The TTL in seconds.
+     */
+    function setTTL(bytes32 node, uint64 ttl) external only_owner(node) {
+        emit NewTTL(node, ttl);
+        records[node].ttl = ttl;
+    }
+
+    /**
+     * @dev Returns the address that owns the specified node.
+     * @param node The specified node.
+     * @return address of the owner.
+     */
+    function owner(bytes32 node) external view returns (address) {
+        return records[node].owner;
+    }
+
+    /**
+     * @dev Returns the address of the resolver for the specified node.
+     * @param node The specified node.
+     * @return address of the resolver.
+     */
+    function resolver(bytes32 node) external view returns (address) {
+        return records[node].resolver;
+    }
+
+    /**
+     * @dev Returns the TTL of a node, and any records associated with it.
+     * @param node The specified node.
+     * @return ttl of the node.
+     */
+    function ttl(bytes32 node) external view returns (uint64) {
+        return records[node].ttl;
+    }
+
+}

+ 11 - 14
contracts/ens/contract/FIFSRegistrar.sol

@@ -1,20 +1,17 @@
-pragma solidity ^0.4.0;
+pragma solidity ^0.5.0;
 
-import './AbstractENS.sol';
+import "./ENS.sol";
 
 /**
  * A registrar that allocates subdomains to the first person to claim them.
  */
 contract FIFSRegistrar {
-    AbstractENS ens;
+    ENS ens;
     bytes32 rootNode;
 
-    modifier only_owner(bytes32 subnode) {
-        var node = sha3(rootNode, subnode);
-        var currentOwner = ens.owner(node);
-
-        if (currentOwner != 0 && currentOwner != msg.sender) throw;
-
+    modifier only_owner(bytes32 label) {
+        address currentOwner = ens.owner(keccak256(abi.encodePacked(rootNode, label)));
+        require(currentOwner == address(0x0) || currentOwner == msg.sender);
         _;
     }
 
@@ -23,17 +20,17 @@ contract FIFSRegistrar {
      * @param ensAddr The address of the ENS registry.
      * @param node The node that this registrar administers.
      */
-    function FIFSRegistrar(AbstractENS ensAddr, bytes32 node) {
+    constructor(ENS ensAddr, bytes32 node) public {
         ens = ensAddr;
         rootNode = node;
     }
 
     /**
      * Register a name, or change the owner of an existing registration.
-     * @param subnode The hash of the label to register.
+     * @param label The hash of the label to register.
      * @param owner The address of the new owner.
      */
-    function register(bytes32 subnode, address owner) only_owner(subnode) {
-        ens.setSubnodeOwner(rootNode, subnode, owner);
+    function register(bytes32 label, address owner) public only_owner(label) {
+        ens.setSubnodeOwner(rootNode, label, owner);
     }
-}
+}

+ 105 - 105
contracts/ens/contract/PublicResolver.sol

@@ -1,26 +1,27 @@
-pragma solidity ^0.4.0;
+pragma solidity >=0.4.25;
 
-import './AbstractENS.sol';
+import "./ENS.sol";
 
 /**
  * A simple resolver anyone can use; only allows the owner of a node to set its
  * address.
  */
 contract PublicResolver {
+
     bytes4 constant INTERFACE_META_ID = 0x01ffc9a7;
     bytes4 constant ADDR_INTERFACE_ID = 0x3b3b57de;
-    bytes4 constant CONTENT_INTERFACE_ID = 0xd8389dc5;
     bytes4 constant NAME_INTERFACE_ID = 0x691f3431;
     bytes4 constant ABI_INTERFACE_ID = 0x2203ab56;
     bytes4 constant PUBKEY_INTERFACE_ID = 0xc8690233;
     bytes4 constant TEXT_INTERFACE_ID = 0x59d1d43c;
+    bytes4 constant CONTENTHASH_INTERFACE_ID = 0xbc1c58d1;
 
     event AddrChanged(bytes32 indexed node, address a);
-    event ContentChanged(bytes32 indexed node, bytes32 hash);
     event NameChanged(bytes32 indexed node, string name);
     event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
     event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
-    event TextChanged(bytes32 indexed node, string indexed indexedKey, string key);
+    event TextChanged(bytes32 indexed node, string indexedKey, string key);
+    event ContenthashChanged(bytes32 indexed node, bytes hash);
 
     struct PublicKey {
         bytes32 x;
@@ -29,18 +30,19 @@ contract PublicResolver {
 
     struct Record {
         address addr;
-        bytes32 content;
         string name;
         PublicKey pubkey;
         mapping(string=>string) text;
         mapping(uint256=>bytes) abis;
+        bytes contenthash;
     }
 
-    AbstractENS ens;
-    mapping(bytes32=>Record) records;
+    ENS ens;
+
+    mapping (bytes32 => Record) records;
 
-    modifier only_owner(bytes32 node) {
-        if (ens.owner(node) != msg.sender) throw;
+    modifier onlyOwner(bytes32 node) {
+        require(ens.owner(node) == msg.sender);
         _;
     }
 
@@ -48,88 +50,100 @@ contract PublicResolver {
      * Constructor.
      * @param ensAddr The ENS registrar contract.
      */
-    function PublicResolver(AbstractENS ensAddr) {
+    constructor(ENS ensAddr) public {
         ens = ensAddr;
     }
 
     /**
-     * Returns true if the resolver implements the interface specified by the provided hash.
-     * @param interfaceID The ID of the interface to check for.
-     * @return True if the contract implements the requested interface.
+     * Sets the address associated with an ENS node.
+     * May only be called by the owner of that node in the ENS registry.
+     * @param node The node to update.
+     * @param addr The address to set.
      */
-    function supportsInterface(bytes4 interfaceID) constant returns (bool) {
-        return interfaceID == ADDR_INTERFACE_ID ||
-               interfaceID == CONTENT_INTERFACE_ID ||
-               interfaceID == NAME_INTERFACE_ID ||
-               interfaceID == ABI_INTERFACE_ID ||
-               interfaceID == PUBKEY_INTERFACE_ID ||
-               interfaceID == TEXT_INTERFACE_ID ||
-               interfaceID == INTERFACE_META_ID;
+    function setAddr(bytes32 node, address addr) external onlyOwner(node) {
+        records[node].addr = addr;
+        emit AddrChanged(node, addr);
     }
 
     /**
-     * Returns the address associated with an ENS node.
-     * @param node The ENS node to query.
-     * @return The associated address.
+     * Sets the contenthash associated with an ENS node.
+     * May only be called by the owner of that node in the ENS registry.
+     * @param node The node to update.
+     * @param hash The contenthash to set
      */
-    function addr(bytes32 node) constant returns (address ret) {
-        ret = records[node].addr;
+    function setContenthash(bytes32 node, bytes calldata hash) external onlyOwner(node) {
+        records[node].contenthash = hash;
+        emit ContenthashChanged(node, hash);
     }
 
     /**
-     * Sets the address associated with an ENS node.
+     * Sets the name associated with an ENS node, for reverse records.
      * May only be called by the owner of that node in the ENS registry.
      * @param node The node to update.
-     * @param addr The address to set.
+     * @param name The name to set.
      */
-    function setAddr(bytes32 node, address addr) only_owner(node) {
-        records[node].addr = addr;
-        AddrChanged(node, addr);
+    function setName(bytes32 node, string calldata name) external onlyOwner(node) {
+        records[node].name = name;
+        emit NameChanged(node, name);
     }
 
     /**
-     * Returns the content hash associated with an ENS node.
-     * Note that this resource type is not standardized, and will likely change
-     * in future to a resource type based on multihash.
-     * @param node The ENS node to query.
-     * @return The associated content hash.
+     * Sets the ABI associated with an ENS node.
+     * Nodes may have one ABI of each content type. To remove an ABI, set it to
+     * the empty string.
+     * @param node The node to update.
+     * @param contentType The content type of the ABI
+     * @param data The ABI data.
+     */
+    function setABI(bytes32 node, uint256 contentType, bytes calldata data) external onlyOwner(node) {
+        // Content types must be powers of 2
+        require(((contentType - 1) & contentType) == 0);
+
+        records[node].abis[contentType] = data;
+        emit ABIChanged(node, contentType);
+    }
+
+    /**
+     * Sets the SECP256k1 public key associated with an ENS node.
+     * @param node The ENS node to query
+     * @param x the X coordinate of the curve point for the public key.
+     * @param y the Y coordinate of the curve point for the public key.
      */
-    function content(bytes32 node) constant returns (bytes32 ret) {
-        ret = records[node].content;
+    function setPubkey(bytes32 node, bytes32 x, bytes32 y) external onlyOwner(node) {
+        records[node].pubkey = PublicKey(x, y);
+        emit PubkeyChanged(node, x, y);
     }
 
     /**
-     * Sets the content hash associated with an ENS node.
+     * Sets the text data associated with an ENS node and key.
      * May only be called by the owner of that node in the ENS registry.
-     * Note that this resource type is not standardized, and will likely change
-     * in future to a resource type based on multihash.
      * @param node The node to update.
-     * @param hash The content hash to set
+     * @param key The key to set.
+     * @param value The text data value to set.
      */
-    function setContent(bytes32 node, bytes32 hash) only_owner(node) {
-        records[node].content = hash;
-        ContentChanged(node, hash);
+    function setText(bytes32 node, string calldata key, string calldata value) external onlyOwner(node) {
+        records[node].text[key] = value;
+        emit TextChanged(node, key, key);
     }
 
     /**
-     * Returns the name associated with an ENS node, for reverse records.
-     * Defined in EIP181.
+     * Returns the text data associated with an ENS node and key.
      * @param node The ENS node to query.
-     * @return The associated name.
+     * @param key The text data key to query.
+     * @return The associated text data.
      */
-    function name(bytes32 node) constant returns (string ret) {
-        ret = records[node].name;
+    function text(bytes32 node, string calldata key) external view returns (string memory) {
+        return records[node].text[key];
     }
 
     /**
-     * Sets the name associated with an ENS node, for reverse records.
-     * May only be called by the owner of that node in the ENS registry.
-     * @param node The node to update.
-     * @param name The name to set.
+     * Returns the SECP256k1 public key associated with an ENS node.
+     * Defined in EIP 619.
+     * @param node The ENS node to query
+     * @return x, y the X and Y coordinates of the curve point for the public key.
      */
-    function setName(bytes32 node, string name) only_owner(node) {
-        records[node].name = name;
-        NameChanged(node, name);
+    function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y) {
+        return (records[node].pubkey.x, records[node].pubkey.y);
     }
 
     /**
@@ -140,73 +154,59 @@ contract PublicResolver {
      * @return contentType The content type of the return value
      * @return data The ABI data
      */
-    function ABI(bytes32 node, uint256 contentTypes) constant returns (uint256 contentType, bytes data) {
-        var record = records[node];
-        for(contentType = 1; contentType <= contentTypes; contentType <<= 1) {
+    function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory) {
+        Record storage record = records[node];
+
+        for (uint256 contentType = 1; contentType <= contentTypes; contentType <<= 1) {
             if ((contentType & contentTypes) != 0 && record.abis[contentType].length > 0) {
-                data = record.abis[contentType];
-                return;
+                return (contentType, record.abis[contentType]);
             }
         }
-        contentType = 0;
-    }
-
-    /**
-     * Sets the ABI associated with an ENS node.
-     * Nodes may have one ABI of each content type. To remove an ABI, set it to
-     * the empty string.
-     * @param node The node to update.
-     * @param contentType The content type of the ABI
-     * @param data The ABI data.
-     */
-    function setABI(bytes32 node, uint256 contentType, bytes data) only_owner(node) {
-        // Content types must be powers of 2
-        if (((contentType - 1) & contentType) != 0) throw;
 
-        records[node].abis[contentType] = data;
-        ABIChanged(node, contentType);
+        bytes memory empty;
+        return (0, empty);
     }
 
     /**
-     * Returns the SECP256k1 public key associated with an ENS node.
-     * Defined in EIP 619.
-     * @param node The ENS node to query
-     * @return x, y the X and Y coordinates of the curve point for the public key.
+     * Returns the name associated with an ENS node, for reverse records.
+     * Defined in EIP181.
+     * @param node The ENS node to query.
+     * @return The associated name.
      */
-    function pubkey(bytes32 node) constant returns (bytes32 x, bytes32 y) {
-        return (records[node].pubkey.x, records[node].pubkey.y);
+    function name(bytes32 node) external view returns (string memory) {
+        return records[node].name;
     }
 
     /**
-     * Sets the SECP256k1 public key associated with an ENS node.
-     * @param node The ENS node to query
-     * @param x the X coordinate of the curve point for the public key.
-     * @param y the Y coordinate of the curve point for the public key.
+     * Returns the address associated with an ENS node.
+     * @param node The ENS node to query.
+     * @return The associated address.
      */
-    function setPubkey(bytes32 node, bytes32 x, bytes32 y) only_owner(node) {
-        records[node].pubkey = PublicKey(x, y);
-        PubkeyChanged(node, x, y);
+    function addr(bytes32 node) external view returns (address) {
+        return records[node].addr;
     }
 
     /**
-     * Returns the text data associated with an ENS node and key.
+     * Returns the contenthash associated with an ENS node.
      * @param node The ENS node to query.
-     * @param key The text data key to query.
-     * @return The associated text data.
+     * @return The associated contenthash.
      */
-    function text(bytes32 node, string key) constant returns (string ret) {
-        ret = records[node].text[key];
+    function contenthash(bytes32 node) external view returns (bytes memory) {
+        return records[node].contenthash;
     }
 
     /**
-     * Sets the text data associated with an ENS node and key.
-     * May only be called by the owner of that node in the ENS registry.
-     * @param node The node to update.
-     * @param key The key to set.
-     * @param value The text data value to set.
+     * Returns true if the resolver implements the interface specified by the provided hash.
+     * @param interfaceID The ID of the interface to check for.
+     * @return True if the contract implements the requested interface.
      */
-    function setText(bytes32 node, string key, string value) only_owner(node) {
-        records[node].text[key] = value;
-        TextChanged(node, key, key);
+    function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
+        return interfaceID == ADDR_INTERFACE_ID ||
+        interfaceID == NAME_INTERFACE_ID ||
+        interfaceID == ABI_INTERFACE_ID ||
+        interfaceID == PUBKEY_INTERFACE_ID ||
+        interfaceID == TEXT_INTERFACE_ID ||
+        interfaceID == CONTENTHASH_INTERFACE_ID ||
+        interfaceID == INTERFACE_META_ID;
     }
 }

Разлика између датотеке није приказан због своје велике величине
+ 13 - 0
contracts/ens/contract/ens.go


Разлика између датотеке није приказан због своје велике величине
+ 30 - 0
contracts/ens/contract/ensregistry.go


+ 26 - 11
contracts/ens/contract/fifsregistrar.go

@@ -4,19 +4,34 @@
 package contract
 
 import (
+	"math/big"
 	"strings"
 
+	ethereum "github.com/ethereum/go-ethereum"
 	"github.com/ethereum/go-ethereum/accounts/abi"
 	"github.com/ethereum/go-ethereum/accounts/abi/bind"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/event"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var (
+	_ = big.NewInt
+	_ = strings.NewReader
+	_ = ethereum.NotFound
+	_ = abi.U256
+	_ = bind.Bind
+	_ = common.Big1
+	_ = types.BloomLookup
+	_ = event.NewSubscription
 )
 
 // FIFSRegistrarABI is the input ABI used to generate the binding from.
-const FIFSRegistrarABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"subnode\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"register\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"ensAddr\",\"type\":\"address\"},{\"name\":\"node\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"
+const FIFSRegistrarABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"label\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"register\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"ensAddr\",\"type\":\"address\"},{\"name\":\"node\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"
 
 // FIFSRegistrarBin is the compiled bytecode used for deploying new contracts.
-const FIFSRegistrarBin = `0x6060604052341561000f57600080fd5b604051604080610224833981016040528080519190602001805160008054600160a060020a03909516600160a060020a03199095169490941790935550506001556101c58061005f6000396000f3006060604052600436106100275763ffffffff60e060020a600035041663d22057a9811461002c575b600080fd5b341561003757600080fd5b61004e600435600160a060020a0360243516610050565b005b816000806001548360405191825260208201526040908101905190819003902060008054919350600160a060020a03909116906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b15156100c857600080fd5b6102c65a03f115156100d957600080fd5b5050506040518051915050600160a060020a0381161580159061010e575033600160a060020a031681600160a060020a031614155b1561011857600080fd5b600054600154600160a060020a03909116906306ab592390878760405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b151561017e57600080fd5b6102c65a03f1151561018f57600080fd5b50505050505050505600a165627a7a723058206fb963cb168d5e3a51af12cd6bb23e324dbd32dd4954f43653ba27e66b68ea650029`
+const FIFSRegistrarBin = `0x608060405234801561001057600080fd5b506040516040806102cc8339810180604052604081101561003057600080fd5b50805160209091015160008054600160a060020a031916600160a060020a0390931692909217825560015561026190819061006b90396000f3fe6080604052600436106100405763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663d22057a98114610045575b600080fd5b34801561005157600080fd5b5061008b6004803603604081101561006857600080fd5b508035906020013573ffffffffffffffffffffffffffffffffffffffff1661008d565b005b6000805460015460408051602080820193909352808201879052815180820383018152606082018084528151918501919091207f02571be3000000000000000000000000000000000000000000000000000000009091526064820152905186949373ffffffffffffffffffffffffffffffffffffffff16926302571be39260848082019391829003018186803b15801561012657600080fd5b505afa15801561013a573d6000803e3d6000fd5b505050506040513d602081101561015057600080fd5b5051905073ffffffffffffffffffffffffffffffffffffffff8116158061018c575073ffffffffffffffffffffffffffffffffffffffff811633145b151561019757600080fd5b60008054600154604080517f06ab592300000000000000000000000000000000000000000000000000000000815260048101929092526024820188905273ffffffffffffffffffffffffffffffffffffffff878116604484015290519216926306ab59239260648084019382900301818387803b15801561021757600080fd5b505af115801561022b573d6000803e3d6000fd5b505050505050505056fea165627a7a723058200f21424d48c6fc6f2bc79f5b36b3a0e3067a97d4ce084ab0e0f9106303a3ee520029`
 
 // DeployFIFSRegistrar deploys a new Ethereum contract, binding an instance of FIFSRegistrar to it.
 func DeployFIFSRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend, ensAddr common.Address, node [32]byte) (common.Address, *types.Transaction, *FIFSRegistrar, error) {
@@ -175,21 +190,21 @@ func (_FIFSRegistrar *FIFSRegistrarTransactorRaw) Transact(opts *bind.TransactOp
 
 // Register is a paid mutator transaction binding the contract method 0xd22057a9.
 //
-// Solidity: function register(subnode bytes32, owner address) returns()
-func (_FIFSRegistrar *FIFSRegistrarTransactor) Register(opts *bind.TransactOpts, subnode [32]byte, owner common.Address) (*types.Transaction, error) {
-	return _FIFSRegistrar.contract.Transact(opts, "register", subnode, owner)
+// Solidity: function register(bytes32 label, address owner) returns()
+func (_FIFSRegistrar *FIFSRegistrarTransactor) Register(opts *bind.TransactOpts, label [32]byte, owner common.Address) (*types.Transaction, error) {
+	return _FIFSRegistrar.contract.Transact(opts, "register", label, owner)
 }
 
 // Register is a paid mutator transaction binding the contract method 0xd22057a9.
 //
-// Solidity: function register(subnode bytes32, owner address) returns()
-func (_FIFSRegistrar *FIFSRegistrarSession) Register(subnode [32]byte, owner common.Address) (*types.Transaction, error) {
-	return _FIFSRegistrar.Contract.Register(&_FIFSRegistrar.TransactOpts, subnode, owner)
+// Solidity: function register(bytes32 label, address owner) returns()
+func (_FIFSRegistrar *FIFSRegistrarSession) Register(label [32]byte, owner common.Address) (*types.Transaction, error) {
+	return _FIFSRegistrar.Contract.Register(&_FIFSRegistrar.TransactOpts, label, owner)
 }
 
 // Register is a paid mutator transaction binding the contract method 0xd22057a9.
 //
-// Solidity: function register(subnode bytes32, owner address) returns()
-func (_FIFSRegistrar *FIFSRegistrarTransactorSession) Register(subnode [32]byte, owner common.Address) (*types.Transaction, error) {
-	return _FIFSRegistrar.Contract.Register(&_FIFSRegistrar.TransactOpts, subnode, owner)
+// Solidity: function register(bytes32 label, address owner) returns()
+func (_FIFSRegistrar *FIFSRegistrarTransactorSession) Register(label [32]byte, owner common.Address) (*types.Transaction, error) {
+	return _FIFSRegistrar.Contract.Register(&_FIFSRegistrar.TransactOpts, label, owner)
 }

Разлика између датотеке није приказан због своје велике величине
+ 12 - 0
contracts/ens/contract/publicresolver.go


+ 79 - 11
contracts/ens/ens.go

@@ -16,25 +16,35 @@
 
 package ens
 
-//go:generate abigen --sol contract/ENS.sol --exc contract/AbstractENS.sol:AbstractENS --pkg contract --out contract/ens.go
-//go:generate abigen --sol contract/FIFSRegistrar.sol --exc contract/AbstractENS.sol:AbstractENS --pkg contract --out contract/fifsregistrar.go
-//go:generate abigen --sol contract/PublicResolver.sol --exc contract/AbstractENS.sol:AbstractENS --pkg contract --out contract/publicresolver.go
+//go:generate abigen --sol contract/ENS.sol --pkg contract --out contract/ens.go
+//go:generate abigen --sol contract/ENSRegistry.sol --exc contract/ENS.sol:ENS --pkg contract --out contract/ensregistry.go
+//go:generate abigen --sol contract/FIFSRegistrar.sol --exc contract/ENS.sol:ENS --pkg contract --out contract/fifsregistrar.go
+//go:generate abigen --sol contract/PublicResolver.sol --exc contract/ENS.sol:ENS --pkg contract --out contract/publicresolver.go
 
 import (
+	"encoding/binary"
 	"strings"
 
 	"github.com/ethereum/go-ethereum/accounts/abi/bind"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/contracts/ens/contract"
+	"github.com/ethereum/go-ethereum/contracts/ens/fallback_contract"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/crypto"
 )
 
 var (
-	MainNetAddress = common.HexToAddress("0x314159265dD8dbb310642f98f50C066173C1259b")
-	TestNetAddress = common.HexToAddress("0x112234455c3a32fd11230c42e7bccd4a84e02010")
+	MainNetAddress           = common.HexToAddress("0x314159265dD8dbb310642f98f50C066173C1259b")
+	TestNetAddress           = common.HexToAddress("0x112234455c3a32fd11230c42e7bccd4a84e02010")
+	contentHash_Interface_Id [4]byte
 )
 
+const contentHash_Interface_Id_Spec = 0xbc1c58d1
+
+func init() {
+	binary.BigEndian.PutUint32(contentHash_Interface_Id[:], contentHash_Interface_Id_Spec)
+}
+
 // ENS is the swarm domain name registry and resolver
 type ENS struct {
 	*contract.ENSSession
@@ -60,7 +70,7 @@ func NewENS(transactOpts *bind.TransactOpts, contractAddr common.Address, contra
 // DeployENS deploys an instance of the ENS nameservice, with a 'first-in, first-served' root registrar.
 func DeployENS(transactOpts *bind.TransactOpts, contractBackend bind.ContractBackend) (common.Address, *ENS, error) {
 	// Deploy the ENS registry
-	ensAddr, _, _, err := contract.DeployENS(transactOpts, contractBackend)
+	ensAddr, _, _, err := contract.DeployENSRegistry(transactOpts, contractBackend)
 	if err != nil {
 		return ensAddr, nil, err
 	}
@@ -110,6 +120,21 @@ func (ens *ENS) getResolver(node [32]byte) (*contract.PublicResolverSession, err
 	}, nil
 }
 
+func (ens *ENS) getFallbackResolver(node [32]byte) (*fallback_contract.PublicResolverSession, error) {
+	resolverAddr, err := ens.Resolver(node)
+	if err != nil {
+		return nil, err
+	}
+	resolver, err := fallback_contract.NewPublicResolver(resolverAddr, ens.contractBackend)
+	if err != nil {
+		return nil, err
+	}
+	return &fallback_contract.PublicResolverSession{
+		Contract:     resolver,
+		TransactOpts: ens.TransactOpts,
+	}, nil
+}
+
 func (ens *ENS) getRegistrar(node [32]byte) (*contract.FIFSRegistrarSession, error) {
 	registrarAddr, err := ens.Owner(node)
 	if err != nil {
@@ -133,11 +158,33 @@ func (ens *ENS) Resolve(name string) (common.Hash, error) {
 	if err != nil {
 		return common.Hash{}, err
 	}
-	ret, err := resolver.Content(node)
+
+	// IMPORTANT: The old contract is deprecated. This code should be removed latest on June 1st 2019
+	supported, err := resolver.SupportsInterface(contentHash_Interface_Id)
+	if err != nil {
+		return common.Hash{}, err
+	}
+
+	if !supported {
+		resolver, err := ens.getFallbackResolver(node)
+		if err != nil {
+			return common.Hash{}, err
+		}
+		ret, err := resolver.Content(node)
+		if err != nil {
+			return common.Hash{}, err
+		}
+		return common.BytesToHash(ret[:]), nil
+	}
+
+	// END DEPRECATED CODE
+
+	contentHash, err := resolver.Contenthash(node)
 	if err != nil {
 		return common.Hash{}, err
 	}
-	return common.BytesToHash(ret[:]), nil
+
+	return extractContentHash(contentHash)
 }
 
 // Addr is a non-transactional call that returns the address associated with a name.
@@ -181,15 +228,36 @@ func (ens *ENS) Register(name string) (*types.Transaction, error) {
 }
 
 // SetContentHash sets the content hash associated with a name. Only works if the caller
-// owns the name, and the associated resolver implements a `setContent` function.
-func (ens *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) {
+// owns the name, and the associated resolver implements a `setContenthash` function.
+func (ens *ENS) SetContentHash(name string, hash []byte) (*types.Transaction, error) {
 	node := EnsNode(name)
 
 	resolver, err := ens.getResolver(node)
 	if err != nil {
 		return nil, err
 	}
+
 	opts := ens.TransactOpts
 	opts.GasLimit = 200000
-	return resolver.Contract.SetContent(&opts, node, hash)
+
+	// IMPORTANT: The old contract is deprecated. This code should be removed latest on June 1st 2019
+	supported, err := resolver.SupportsInterface(contentHash_Interface_Id)
+	if err != nil {
+		return nil, err
+	}
+
+	if !supported {
+		resolver, err := ens.getFallbackResolver(node)
+		if err != nil {
+			return nil, err
+		}
+		opts := ens.TransactOpts
+		opts.GasLimit = 200000
+		var b [32]byte
+		copy(b[:], hash)
+		return resolver.Contract.SetContent(&opts, node, b)
+	}
+
+	// END DEPRECATED CODE
+	return resolver.Contract.SetContenthash(&opts, node, hash)
 }

+ 42 - 10
contracts/ens/ens_test.go

@@ -24,16 +24,18 @@ import (
 	"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/contracts/ens/contract"
+	"github.com/ethereum/go-ethereum/contracts/ens/fallback_contract"
 	"github.com/ethereum/go-ethereum/core"
 	"github.com/ethereum/go-ethereum/crypto"
 )
 
 var (
-	key, _   = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
-	name     = "my name on ENS"
-	hash     = crypto.Keccak256Hash([]byte("my content"))
-	addr     = crypto.PubkeyToAddress(key.PublicKey)
-	testAddr = common.HexToAddress("0x1234123412341234123412341234123412341234")
+	key, _       = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+	name         = "my name on ENS"
+	hash         = crypto.Keccak256Hash([]byte("my content"))
+	fallbackHash = crypto.Keccak256Hash([]byte("my content hash"))
+	addr         = crypto.PubkeyToAddress(key.PublicKey)
+	testAddr     = common.HexToAddress("0x1234123412341234123412341234123412341234")
 )
 
 func TestENS(t *testing.T) {
@@ -57,24 +59,29 @@ func TestENS(t *testing.T) {
 	if err != nil {
 		t.Fatalf("can't deploy resolver: %v", err)
 	}
+
 	if _, err := ens.SetResolver(EnsNode(name), resolverAddr); err != nil {
 		t.Fatalf("can't set resolver: %v", err)
 	}
 	contractBackend.Commit()
 
 	// Set the content hash for the name.
-	if _, err = ens.SetContentHash(name, hash); err != nil {
+	cid, err := EncodeSwarmHash(hash)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err = ens.SetContentHash(name, cid); err != nil {
 		t.Fatalf("can't set content hash: %v", err)
 	}
 	contractBackend.Commit()
 
 	// Try to resolve the name.
-	vhost, err := ens.Resolve(name)
+	resolvedHash, err := ens.Resolve(name)
 	if err != nil {
 		t.Fatalf("expected no error, got %v", err)
 	}
-	if vhost != hash {
-		t.Fatalf("resolve error, expected %v, got %v", hash.Hex(), vhost.Hex())
+	if resolvedHash.Hex() != hash.Hex() {
+		t.Fatalf("resolve error, expected %v, got %v", hash.Hex(), resolvedHash.Hex())
 	}
 
 	// set the address for the name
@@ -88,7 +95,32 @@ func TestENS(t *testing.T) {
 	if err != nil {
 		t.Fatalf("expected no error, got %v", err)
 	}
-	if vhost != hash {
+	if testAddr.Hex() != recoveredAddr.Hex() {
 		t.Fatalf("resolve error, expected %v, got %v", testAddr.Hex(), recoveredAddr.Hex())
 	}
+
+	// deploy the fallback contract and see that the fallback mechanism works
+	fallbackResolverAddr, _, _, err := fallback_contract.DeployPublicResolver(transactOpts, contractBackend, ensAddr)
+	if err != nil {
+		t.Fatalf("can't deploy resolver: %v", err)
+	}
+	if _, err := ens.SetResolver(EnsNode(name), fallbackResolverAddr); err != nil {
+		t.Fatalf("can't set resolver: %v", err)
+	}
+	contractBackend.Commit()
+
+	// Set the content hash for the name.
+	if _, err = ens.SetContentHash(name, fallbackHash.Bytes()); err != nil {
+		t.Fatalf("can't set content hash: %v", err)
+	}
+	contractBackend.Commit()
+
+	// Try to resolve the name.
+	fallbackResolvedHash, err := ens.Resolve(name)
+	if err != nil {
+		t.Fatalf("expected no error, got %v", err)
+	}
+	if fallbackResolvedHash.Hex() != fallbackHash.Hex() {
+		t.Fatalf("resolve error, expected %v, got %v", hash.Hex(), resolvedHash.Hex())
+	}
 }

+ 212 - 0
contracts/ens/fallback_contract/PublicResolver.sol

@@ -0,0 +1,212 @@
+pragma solidity ^0.4.0;
+
+import './AbstractENS.sol';
+
+/**
+ * A simple resolver anyone can use; only allows the owner of a node to set its
+ * address.
+ */
+contract PublicResolver {
+    bytes4 constant INTERFACE_META_ID = 0x01ffc9a7;
+    bytes4 constant ADDR_INTERFACE_ID = 0x3b3b57de;
+    bytes4 constant CONTENT_INTERFACE_ID = 0xd8389dc5;
+    bytes4 constant NAME_INTERFACE_ID = 0x691f3431;
+    bytes4 constant ABI_INTERFACE_ID = 0x2203ab56;
+    bytes4 constant PUBKEY_INTERFACE_ID = 0xc8690233;
+    bytes4 constant TEXT_INTERFACE_ID = 0x59d1d43c;
+
+    event AddrChanged(bytes32 indexed node, address a);
+    event ContentChanged(bytes32 indexed node, bytes32 hash);
+    event NameChanged(bytes32 indexed node, string name);
+    event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
+    event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
+    event TextChanged(bytes32 indexed node, string indexed indexedKey, string key);
+
+    struct PublicKey {
+        bytes32 x;
+        bytes32 y;
+    }
+
+    struct Record {
+        address addr;
+        bytes32 content;
+        string name;
+        PublicKey pubkey;
+        mapping(string=>string) text;
+        mapping(uint256=>bytes) abis;
+    }
+
+    AbstractENS ens;
+    mapping(bytes32=>Record) records;
+
+    modifier only_owner(bytes32 node) {
+        if (ens.owner(node) != msg.sender) throw;
+        _;
+    }
+
+    /**
+     * Constructor.
+     * @param ensAddr The ENS registrar contract.
+     */
+    function PublicResolver(AbstractENS ensAddr) {
+        ens = ensAddr;
+    }
+
+    /**
+     * Returns true if the resolver implements the interface specified by the provided hash.
+     * @param interfaceID The ID of the interface to check for.
+     * @return True if the contract implements the requested interface.
+     */
+    function supportsInterface(bytes4 interfaceID) constant returns (bool) {
+        return interfaceID == ADDR_INTERFACE_ID ||
+               interfaceID == CONTENT_INTERFACE_ID ||
+               interfaceID == NAME_INTERFACE_ID ||
+               interfaceID == ABI_INTERFACE_ID ||
+               interfaceID == PUBKEY_INTERFACE_ID ||
+               interfaceID == TEXT_INTERFACE_ID ||
+               interfaceID == INTERFACE_META_ID;
+    }
+
+    /**
+     * Returns the address associated with an ENS node.
+     * @param node The ENS node to query.
+     * @return The associated address.
+     */
+    function addr(bytes32 node) constant returns (address ret) {
+        ret = records[node].addr;
+    }
+
+    /**
+     * Sets the address associated with an ENS node.
+     * May only be called by the owner of that node in the ENS registry.
+     * @param node The node to update.
+     * @param addr The address to set.
+     */
+    function setAddr(bytes32 node, address addr) only_owner(node) {
+        records[node].addr = addr;
+        AddrChanged(node, addr);
+    }
+
+    /**
+     * Returns the content hash associated with an ENS node.
+     * Note that this resource type is not standardized, and will likely change
+     * in future to a resource type based on multihash.
+     * @param node The ENS node to query.
+     * @return The associated content hash.
+     */
+    function content(bytes32 node) constant returns (bytes32 ret) {
+        ret = records[node].content;
+    }
+
+    /**
+     * Sets the content hash associated with an ENS node.
+     * May only be called by the owner of that node in the ENS registry.
+     * Note that this resource type is not standardized, and will likely change
+     * in future to a resource type based on multihash.
+     * @param node The node to update.
+     * @param hash The content hash to set
+     */
+    function setContent(bytes32 node, bytes32 hash) only_owner(node) {
+        records[node].content = hash;
+        ContentChanged(node, hash);
+    }
+
+    /**
+     * Returns the name associated with an ENS node, for reverse records.
+     * Defined in EIP181.
+     * @param node The ENS node to query.
+     * @return The associated name.
+     */
+    function name(bytes32 node) constant returns (string ret) {
+        ret = records[node].name;
+    }
+
+    /**
+     * Sets the name associated with an ENS node, for reverse records.
+     * May only be called by the owner of that node in the ENS registry.
+     * @param node The node to update.
+     * @param name The name to set.
+     */
+    function setName(bytes32 node, string name) only_owner(node) {
+        records[node].name = name;
+        NameChanged(node, name);
+    }
+
+    /**
+     * Returns the ABI associated with an ENS node.
+     * Defined in EIP205.
+     * @param node The ENS node to query
+     * @param contentTypes A bitwise OR of the ABI formats accepted by the caller.
+     * @return contentType The content type of the return value
+     * @return data The ABI data
+     */
+    function ABI(bytes32 node, uint256 contentTypes) constant returns (uint256 contentType, bytes data) {
+        var record = records[node];
+        for(contentType = 1; contentType <= contentTypes; contentType <<= 1) {
+            if ((contentType & contentTypes) != 0 && record.abis[contentType].length > 0) {
+                data = record.abis[contentType];
+                return;
+            }
+        }
+        contentType = 0;
+    }
+
+    /**
+     * Sets the ABI associated with an ENS node.
+     * Nodes may have one ABI of each content type. To remove an ABI, set it to
+     * the empty string.
+     * @param node The node to update.
+     * @param contentType The content type of the ABI
+     * @param data The ABI data.
+     */
+    function setABI(bytes32 node, uint256 contentType, bytes data) only_owner(node) {
+        // Content types must be powers of 2
+        if (((contentType - 1) & contentType) != 0) throw;
+
+        records[node].abis[contentType] = data;
+        ABIChanged(node, contentType);
+    }
+
+    /**
+     * Returns the SECP256k1 public key associated with an ENS node.
+     * Defined in EIP 619.
+     * @param node The ENS node to query
+     * @return x, y the X and Y coordinates of the curve point for the public key.
+     */
+    function pubkey(bytes32 node) constant returns (bytes32 x, bytes32 y) {
+        return (records[node].pubkey.x, records[node].pubkey.y);
+    }
+
+    /**
+     * Sets the SECP256k1 public key associated with an ENS node.
+     * @param node The ENS node to query
+     * @param x the X coordinate of the curve point for the public key.
+     * @param y the Y coordinate of the curve point for the public key.
+     */
+    function setPubkey(bytes32 node, bytes32 x, bytes32 y) only_owner(node) {
+        records[node].pubkey = PublicKey(x, y);
+        PubkeyChanged(node, x, y);
+    }
+
+    /**
+     * Returns the text data associated with an ENS node and key.
+     * @param node The ENS node to query.
+     * @param key The text data key to query.
+     * @return The associated text data.
+     */
+    function text(bytes32 node, string key) constant returns (string ret) {
+        ret = records[node].text[key];
+    }
+
+    /**
+     * Sets the text data associated with an ENS node and key.
+     * May only be called by the owner of that node in the ENS registry.
+     * @param node The node to update.
+     * @param key The key to set.
+     * @param value The text data value to set.
+     */
+    function setText(bytes32 node, string key, string value) only_owner(node) {
+        records[node].text[key] = value;
+        TextChanged(node, key, key);
+    }
+}

Разлика између датотеке није приказан због своје велике величине
+ 18 - 0
contracts/ens/fallback_contract/publicresolver.go


Неке датотеке нису приказане због велике количине промена