mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
contracts/ens: moved cid decoding to own file
This commit is contained in:
parent
14edd10673
commit
32de60911f
4 changed files with 314 additions and 230 deletions
114
contracts/ens/cid.go
Normal file
114
contracts/ens/cid.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// 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 (
|
||||
ns_ipfs = 0xe3
|
||||
ns_swarm = 0xe4
|
||||
|
||||
swarm_typecode = 0x99 //todo change
|
||||
swarm_hashtype = 0x1b //todo change
|
||||
|
||||
ipfs_hashtype = 0x12
|
||||
|
||||
hash_length = 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, fmt.Errorf("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, hashLength, hashBytes, err := decodeEIP1577ContentHash(buf)
|
||||
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
||||
if storageNs != ns_swarm {
|
||||
return common.Hash{}, errors.New("unknown storage system")
|
||||
}
|
||||
|
||||
if contentType != swarm_typecode { //todo pending pr
|
||||
return common.Hash{}, errors.New("unknown content type")
|
||||
}
|
||||
|
||||
if hashType != swarm_hashtype { //todo: should be bmt
|
||||
return common.Hash{}, errors.New("unknown multihash type")
|
||||
}
|
||||
|
||||
if hashLength != hash_length {
|
||||
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
|
||||
}
|
||||
|
||||
// encodeCid encodes a swarm hash into an IPLD CID
|
||||
/*func encodeCid(h common.Hash) (cid.Cid, error) {
|
||||
b := []byte{0x1b, 0x20} //0x1b = keccak256 (should be changed to bmt), 0x20 = 32 bytes hash length
|
||||
b = append(b, h.Bytes()...) // append actual hash bytes
|
||||
multi, err := mh.Cast(b)
|
||||
if err != nil {
|
||||
return cid.Cid{}, err
|
||||
}
|
||||
|
||||
c := cid.NewCidV1(cid.Raw, multi) //todo: cid.Raw should be swarm manifest
|
||||
|
||||
return c, nil
|
||||
}*/
|
||||
200
contracts/ens/cid_test.go
Normal file
200
contracts/ens/cid_test.go
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// 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"
|
||||
|
||||
dag_pb = 0x70
|
||||
sha2_256 = 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, hashBytes, err := decodeEIP1577ContentHash(b)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if storageNs != ns_ipfs {
|
||||
t.Fatal("wrong ns")
|
||||
}
|
||||
if contentType != dag_pb {
|
||||
t.Fatal("should be swarm typecode")
|
||||
}
|
||||
if hashType != sha2_256 {
|
||||
t.Fatal("should be sha2-256")
|
||||
}
|
||||
if hashLength != 32 {
|
||||
t.Fatal("should be 32")
|
||||
}
|
||||
if !bytes.Equal(hashBytes, hashBytes) {
|
||||
t.Fatal("should be equal")
|
||||
}
|
||||
|
||||
}
|
||||
func TestManualCidDecode(t *testing.T) {
|
||||
// call cid encode method with hash. expect byte slice returned, compare according to spec
|
||||
bb := []byte{}
|
||||
|
||||
for _, v := range []struct {
|
||||
name string
|
||||
headerBytes []byte
|
||||
fails bool
|
||||
}{
|
||||
{
|
||||
name: "values correct, should not fail",
|
||||
headerBytes: []byte{0xe4, 0x01, 0x99, 0x1b, 0x20},
|
||||
fails: false,
|
||||
},
|
||||
{
|
||||
name: "cid version wrong, should fail",
|
||||
headerBytes: []byte{0xe4, 0x00, 0x99, 0x1b, 0x20},
|
||||
fails: true,
|
||||
},
|
||||
{
|
||||
name: "hash length wrong, should fail",
|
||||
headerBytes: []byte{0xe4, 0x01, 0x99, 0x1b, 0x1f},
|
||||
fails: true,
|
||||
},
|
||||
{
|
||||
name: "values correct for ipfs, should fail",
|
||||
headerBytes: []byte{0xe3, 0x01, 0x99, 0x1b, 0x20},
|
||||
fails: true,
|
||||
},
|
||||
} {
|
||||
t.Run(v.name, func(t *testing.T) {
|
||||
buf := make([]byte, binary.MaxVarintLen64)
|
||||
for _, vv := range v.headerBytes {
|
||||
n := binary.PutUvarint(buf, uint64(vv))
|
||||
bb = append(bb, buf[:n]...)
|
||||
}
|
||||
|
||||
h := common.HexToHash("29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f")
|
||||
bb = append(bb, h[:]...)
|
||||
str := hex.EncodeToString(bb)
|
||||
fmt.Println(str)
|
||||
decodedHash, e := extractContentHash(bb)
|
||||
switch v.fails {
|
||||
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")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
/* from the EIP documentation
|
||||
storage system: Swarm (0xe4)
|
||||
CID version: 1 (0x01)
|
||||
content type: swarm-manifest (0x??)
|
||||
hash function: keccak-256 (0x1B)
|
||||
hash length: 32 bytes (0x20)
|
||||
hash: 29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
func TestManuelCidEncode(t *testing.T) {
|
||||
// call cid encode method with hash. expect byte slice returned, compare according to spec
|
||||
|
||||
/* from the EIP documentation
|
||||
storage system: Swarm (0xe4)
|
||||
CID version: 1 (0x01)
|
||||
content type: swarm-manifest (0x??)
|
||||
hash function: keccak-256 (0x1B)
|
||||
hash length: 32 bytes (0x20)
|
||||
hash: 29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
func TestCIDSanity(t *testing.T) {
|
||||
hashStr := "d1de9994b4d039f6548d191eb26786769f580809256b4685ef316805265ea162"
|
||||
hash := common.HexToHash(hashStr) //this always yields a 32 byte long hash
|
||||
cc, err := encodeCid(hash)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if cc.Prefix().MhLength != 32 {
|
||||
t.Fatal("w00t")
|
||||
}
|
||||
decoded, err := mh.Decode(cc.Hash())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decoded.Length != 32 {
|
||||
t.Fatal("invalid length")
|
||||
}
|
||||
if !bytes.Equal(decoded.Digest, hash[:]) {
|
||||
t.Fatalf("hashes not equal")
|
||||
}
|
||||
|
||||
if decoded.Length != 32 {
|
||||
t.Fatal("wrong length")
|
||||
}
|
||||
fmt.Println(cc.StringOfBase(multibase.Base16))
|
||||
|
||||
bbbb, e := cc.StringOfBase(multibase.Base16)
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
fmt.Println(bbbb)
|
||||
//create the CID string artificially
|
||||
hashStr = "f01551b20" + hashStr
|
||||
|
||||
c, err := cid.Decode(hashStr)
|
||||
if err != nil {
|
||||
t.Fatalf("Error decoding CID: %v", err)
|
||||
}
|
||||
|
||||
fmt.Sprintf("Got CID: %v", c)
|
||||
fmt.Println("Got CID:", c.Prefix())
|
||||
|
||||
}*/
|
||||
|
|
@ -23,8 +23,6 @@ package ens
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
|
|
@ -262,61 +260,3 @@ func (ens *ENS) SetContentHash(name string, hash []byte) (*types.Transaction, er
|
|||
// END DEPRECATED CODE
|
||||
return resolver.Contract.SetContenthash(&opts, node, hash)
|
||||
}
|
||||
|
||||
func manualDecode(buf []byte) (common.Hash, error) {
|
||||
if len(buf) < 2 {
|
||||
return common.Hash{}, errors.New("buffer too short")
|
||||
}
|
||||
|
||||
storageSys, n := binary.Uvarint(buf)
|
||||
if storageSys != 0xe3 && storageSys != 0xe4 {
|
||||
return common.Hash{}, errors.New("unknown storage system")
|
||||
}
|
||||
buf = buf[n:]
|
||||
vers, n := binary.Uvarint(buf)
|
||||
|
||||
if vers != 1 {
|
||||
return common.Hash{}, fmt.Errorf("expected 1 as the cid version number, got: %d", vers)
|
||||
}
|
||||
|
||||
buf = buf[n:]
|
||||
ctype, n := binary.Uvarint(buf)
|
||||
|
||||
if ctype < 0 { // ctype != 0x99 {
|
||||
return common.Hash{}, errors.New("unknown content type")
|
||||
}
|
||||
buf = buf[n:]
|
||||
hashType, n := binary.Uvarint(buf)
|
||||
|
||||
if hashType != 0x1b && hashType != 0x12 {
|
||||
return common.Hash{}, errors.New("unknown multihash type")
|
||||
}
|
||||
|
||||
buf = buf[n:]
|
||||
hashLen, n := binary.Uvarint(buf)
|
||||
|
||||
if hashLen != 32 {
|
||||
return common.Hash{}, errors.New("odd hash length, swarm expects 32 bytes")
|
||||
}
|
||||
buf = buf[n:]
|
||||
|
||||
if len(buf) != int(hashLen) {
|
||||
return common.Hash{}, errors.New("hash length mismatch")
|
||||
}
|
||||
|
||||
return common.BytesToHash(buf), nil
|
||||
}
|
||||
|
||||
// encodeCid encodes a swarm hash into an IPLD CID
|
||||
/*func encodeCid(h common.Hash) (cid.Cid, error) {
|
||||
b := []byte{0x1b, 0x20} //0x1b = keccak256 (should be changed to bmt), 0x20 = 32 bytes hash length
|
||||
b = append(b, h.Bytes()...) // append actual hash bytes
|
||||
multi, err := mh.Cast(b)
|
||||
if err != nil {
|
||||
return cid.Cid{}, err
|
||||
}
|
||||
|
||||
c := cid.NewCidV1(cid.Raw, multi) //todo: cid.Raw should be swarm manifest
|
||||
|
||||
return c, nil
|
||||
}*/
|
||||
|
|
|
|||
|
|
@ -17,10 +17,6 @@
|
|||
package ens
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
|
|
@ -124,169 +120,3 @@ func TestENS(t *testing.T) {
|
|||
}
|
||||
t.Fatal("todo: try to set old contract with new multicodec stuff and assert fail, set new contract with multicodec stuff, encode, decode and assert returns correct hash")
|
||||
}
|
||||
|
||||
func TestEIPSpecCidDecode(t *testing.T) {
|
||||
/*storage system: IPFS (0xe3)
|
||||
CID version: 1 (0x01)
|
||||
content type: dag-pb (0x70)
|
||||
hash function: sha2-256 (0x12)
|
||||
hash length: 32 bytes (0x20)
|
||||
hash: 29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f
|
||||
*/
|
||||
|
||||
const eipSpecHash = "e3010170122029f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f"
|
||||
const eipHash = "29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f"
|
||||
|
||||
b, err := hex.DecodeString(eipSpecHash)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hashBytes, err := hex.DecodeString(eipHash)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h, err := manualDecode(b)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(h[:], hashBytes) {
|
||||
t.Fatal("should be equal")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestManualCidDecode(t *testing.T) {
|
||||
// call cid encode method with hash. expect byte slice returned, compare according to spec
|
||||
bb := []byte{}
|
||||
|
||||
for _, v := range []struct {
|
||||
name string
|
||||
headerBytes []byte
|
||||
fails bool
|
||||
}{
|
||||
{
|
||||
name: "values correct, should not fail",
|
||||
headerBytes: []byte{0xe4, 0x01, 0x99, 0x1b, 0x20},
|
||||
fails: false,
|
||||
},
|
||||
{
|
||||
name: "cid version wrong, should fail",
|
||||
headerBytes: []byte{0xe4, 0x01, 0x99, 0x1b, 0x20},
|
||||
fails: true,
|
||||
},
|
||||
{
|
||||
name: "hash length wrong, should fail",
|
||||
headerBytes: []byte{0xe4, 0x01, 0x99, 0x1b, 0x1f},
|
||||
fails: true,
|
||||
},
|
||||
{
|
||||
name: "values correct for ipfs, should fail",
|
||||
headerBytes: []byte{0xe3, 0x01, 0x99, 0x1b, 0x20},
|
||||
fails: true,
|
||||
},
|
||||
} {
|
||||
t.Run(v.name, func(t *testing.T) {
|
||||
buf := make([]byte, binary.MaxVarintLen64)
|
||||
for _, vv := range v.headerBytes {
|
||||
n := binary.PutUvarint(buf, uint64(vv))
|
||||
bb = append(bb, buf[:n]...)
|
||||
}
|
||||
|
||||
h := common.HexToHash("29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f")
|
||||
bb = append(bb, h[:]...)
|
||||
str := hex.EncodeToString(bb)
|
||||
fmt.Println(str)
|
||||
decodedHash, e := manualDecode(bb)
|
||||
switch v.fails {
|
||||
case true:
|
||||
if e == nil {
|
||||
t.Fatal("the decode should fail")
|
||||
}
|
||||
case false:
|
||||
if e != nil {
|
||||
t.Fatal("the deccode shouldnt fail")
|
||||
}
|
||||
}
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
|
||||
if !bytes.Equal(decodedHash[:], h[:]) {
|
||||
t.Fatal("hashes not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/* from the EIP documentation
|
||||
storage system: Swarm (0xe4)
|
||||
CID version: 1 (0x01)
|
||||
content type: swarm-manifest (0x??)
|
||||
hash function: keccak-256 (0x1B)
|
||||
hash length: 32 bytes (0x20)
|
||||
hash: 29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
func TestManuelCidEncode(t *testing.T) {
|
||||
// call cid encode method with hash. expect byte slice returned, compare according to spec
|
||||
|
||||
/* from the EIP documentation
|
||||
storage system: Swarm (0xe4)
|
||||
CID version: 1 (0x01)
|
||||
content type: swarm-manifest (0x??)
|
||||
hash function: keccak-256 (0x1B)
|
||||
hash length: 32 bytes (0x20)
|
||||
hash: 29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
func TestCIDSanity(t *testing.T) {
|
||||
hashStr := "d1de9994b4d039f6548d191eb26786769f580809256b4685ef316805265ea162"
|
||||
hash := common.HexToHash(hashStr) //this always yields a 32 byte long hash
|
||||
cc, err := encodeCid(hash)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if cc.Prefix().MhLength != 32 {
|
||||
t.Fatal("w00t")
|
||||
}
|
||||
decoded, err := mh.Decode(cc.Hash())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decoded.Length != 32 {
|
||||
t.Fatal("invalid length")
|
||||
}
|
||||
if !bytes.Equal(decoded.Digest, hash[:]) {
|
||||
t.Fatalf("hashes not equal")
|
||||
}
|
||||
|
||||
if decoded.Length != 32 {
|
||||
t.Fatal("wrong length")
|
||||
}
|
||||
fmt.Println(cc.StringOfBase(multibase.Base16))
|
||||
|
||||
bbbb, e := cc.StringOfBase(multibase.Base16)
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
fmt.Println(bbbb)
|
||||
//create the CID string artificially
|
||||
hashStr = "f01551b20" + hashStr
|
||||
|
||||
c, err := cid.Decode(hashStr)
|
||||
if err != nil {
|
||||
t.Fatalf("Error decoding CID: %v", err)
|
||||
}
|
||||
|
||||
fmt.Sprintf("Got CID: %v", c)
|
||||
fmt.Println("Got CID:", c.Prefix())
|
||||
|
||||
}*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue