Registrar and contractInfo handling
* resolver -> common/registrar * global registrar name registry interface * add Call to resolver backend interface * the hashReg and UrlHing contracts now initialised from global registry * initialization of contracts uniform * improve errors and more econsistent method names * common/registrar/ethreg: versioned registrar * integrate new naming and registrar in natspec * js console api: setGlobalRegistrar, setHashReg, setUrlHint * js test TestContract uses mining - tests fixed all pass * eth/backend: allow PoW test mode (small ethash DAG) * console jsre refers to resolver.abi/addr, * cmd/geth/contracts.go moved to common/registrar
This commit is contained in:
@ -185,12 +185,12 @@ func (sol *Solidity) Compile(source string) (contracts map[string]*Contract, err
|
||||
return
|
||||
}
|
||||
|
||||
func ExtractInfo(contract *Contract, filename string) (contenthash common.Hash, err error) {
|
||||
contractInfo, err := json.Marshal(contract.Info)
|
||||
func SaveInfo(info *ContractInfo, filename string) (contenthash common.Hash, err error) {
|
||||
infojson, err := json.Marshal(info)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
contenthash = common.BytesToHash(crypto.Sha3(contractInfo))
|
||||
err = ioutil.WriteFile(filename, contractInfo, 0600)
|
||||
contenthash = common.BytesToHash(crypto.Sha3(infojson))
|
||||
err = ioutil.WriteFile(filename, infojson, 0600)
|
||||
return
|
||||
}
|
||||
|
@ -72,19 +72,15 @@ func TestNoCompiler(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractInfo(t *testing.T) {
|
||||
func TestSaveInfo(t *testing.T) {
|
||||
var cinfo ContractInfo
|
||||
err := json.Unmarshal([]byte(info), &cinfo)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
contract := &Contract{
|
||||
Code: "",
|
||||
Info: cinfo,
|
||||
}
|
||||
filename := "/tmp/solctest.info.json"
|
||||
os.Remove(filename)
|
||||
cinfohash, err := ExtractInfo(contract, filename)
|
||||
cinfohash, err := SaveInfo(&cinfo, filename)
|
||||
if err != nil {
|
||||
t.Errorf("error extracting info: %v", err)
|
||||
}
|
||||
|
@ -4,34 +4,25 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
// http://golang.org/pkg/net/http/#RoundTripper
|
||||
var (
|
||||
schemes = map[string]func(*DocServer) http.RoundTripper{
|
||||
// Simple File server from local disk file:///etc/passwd :)
|
||||
"file": fileServerOnDocRoot,
|
||||
}
|
||||
)
|
||||
|
||||
func fileServerOnDocRoot(ds *DocServer) http.RoundTripper {
|
||||
return http.NewFileTransport(http.Dir(ds.DocRoot))
|
||||
}
|
||||
|
||||
type DocServer struct {
|
||||
*http.Transport
|
||||
DocRoot string
|
||||
schemes []string
|
||||
}
|
||||
|
||||
func New(docRoot string) (self *DocServer, err error) {
|
||||
func New(docRoot string) (self *DocServer) {
|
||||
self = &DocServer{
|
||||
Transport: &http.Transport{},
|
||||
DocRoot: docRoot,
|
||||
schemes: []string{"file"},
|
||||
}
|
||||
err = self.RegisterProtocols(schemes)
|
||||
self.RegisterProtocol("file", http.NewFileTransport(http.Dir(self.DocRoot)))
|
||||
return
|
||||
}
|
||||
|
||||
@ -45,25 +36,25 @@ func (self *DocServer) Client() *http.Client {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *DocServer) RegisterProtocols(schemes map[string]func(*DocServer) http.RoundTripper) (err error) {
|
||||
for scheme, rtf := range schemes {
|
||||
self.RegisterProtocol(scheme, rtf(self))
|
||||
func (self *DocServer) RegisterScheme(scheme string, rt http.RoundTripper) {
|
||||
self.schemes = append(self.schemes, scheme)
|
||||
self.RegisterProtocol(scheme, rt)
|
||||
}
|
||||
|
||||
func (self *DocServer) HasScheme(scheme string) bool {
|
||||
for _, s := range self.schemes {
|
||||
if s == scheme {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *DocServer) GetAuthContent(uri string, hash common.Hash) (content []byte, err error) {
|
||||
// retrieve content
|
||||
resp, err := self.Client().Get(uri)
|
||||
defer func() {
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
content, err = ioutil.ReadAll(resp.Body)
|
||||
url := uri
|
||||
fmt.Printf("uri: %v\n", url)
|
||||
content, err = self.Get(url, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -80,3 +71,32 @@ func (self *DocServer) GetAuthContent(uri string, hash common.Hash) (content []b
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// Get(uri, path) downloads the document at uri, if path is non-empty it
|
||||
// is interpreted as a filepath to which the contents are saved
|
||||
func (self *DocServer) Get(uri, path string) (content []byte, err error) {
|
||||
// retrieve content
|
||||
resp, err := self.Client().Get(uri)
|
||||
|
||||
defer func() {
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
content, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if path != "" {
|
||||
var abspath string
|
||||
abspath, err = filepath.Abs(path)
|
||||
ioutil.WriteFile(abspath, content, 0700)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package docserver
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@ -15,7 +16,7 @@ func TestGetAuthContent(t *testing.T) {
|
||||
copy(hash[:], crypto.Sha3([]byte(text)))
|
||||
ioutil.WriteFile("/tmp/test.content", []byte(text), os.ModePerm)
|
||||
|
||||
ds, err := New("/tmp/")
|
||||
ds := New("/tmp/")
|
||||
content, err := ds.GetAuthContent("file:///test.content", hash)
|
||||
if err != nil {
|
||||
t.Errorf("no error expected, got %v", err)
|
||||
@ -36,3 +37,18 @@ func TestGetAuthContent(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type rt struct{}
|
||||
|
||||
func (rt) RoundTrip(req *http.Request) (resp *http.Response, err error) { return }
|
||||
|
||||
func TestRegisterScheme(t *testing.T) {
|
||||
ds := New("/tmp/")
|
||||
if ds.HasScheme("scheme") {
|
||||
t.Errorf("expected scheme not to be registered")
|
||||
}
|
||||
ds.RegisterScheme("scheme", rt{})
|
||||
if !ds.HasScheme("scheme") {
|
||||
t.Errorf("expected scheme to be registered")
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/docserver"
|
||||
"github.com/ethereum/go-ethereum/common/resolver"
|
||||
"github.com/ethereum/go-ethereum/common/registrar"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
@ -88,7 +88,7 @@ func New(xeth *xeth.XEth, jsontx string, http *docserver.DocServer) (self *NatSp
|
||||
}
|
||||
|
||||
// also called by admin.contractInfo.get
|
||||
func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, http *docserver.DocServer) (content []byte, err error) {
|
||||
func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, ds *docserver.DocServer) (content []byte, err error) {
|
||||
// retrieve contract hash from state
|
||||
codehex := xeth.CodeAt(contractAddress)
|
||||
codeb := xeth.CodeAtBytes(contractAddress)
|
||||
@ -99,20 +99,32 @@ func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, http *docserv
|
||||
}
|
||||
codehash := common.BytesToHash(crypto.Sha3(codeb))
|
||||
// set up nameresolver with natspecreg + urlhint contract addresses
|
||||
res := resolver.New(xeth)
|
||||
reg := registrar.New(xeth)
|
||||
|
||||
// resolve host via HashReg/UrlHint Resolver
|
||||
uri, hash, err := res.KeyToUrl(codehash)
|
||||
hash, err := reg.HashToHash(codehash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if ds.HasScheme("bzz") {
|
||||
content, err = ds.Get("bzz://"+hash.Hex()[2:], "")
|
||||
if err == nil { // non-fatal
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
//falling back to urlhint
|
||||
}
|
||||
|
||||
uri, err := reg.HashToUrl(hash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// get content via http client and authenticate content using hash
|
||||
content, err = http.GetAuthContent(uri, hash)
|
||||
content, err = ds.GetAuthContent(uri, hash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/docserver"
|
||||
"github.com/ethereum/go-ethereum/common/resolver"
|
||||
"github.com/ethereum/go-ethereum/common/registrar"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@ -73,8 +73,7 @@ const (
|
||||
)
|
||||
|
||||
type testFrontend struct {
|
||||
t *testing.T
|
||||
// resolver *resolver.Resolver
|
||||
t *testing.T
|
||||
ethereum *eth.Ethereum
|
||||
xeth *xe.XEth
|
||||
coinbase common.Address
|
||||
@ -91,10 +90,7 @@ func (self *testFrontend) UnlockAccount(acc []byte) bool {
|
||||
|
||||
func (self *testFrontend) ConfirmTransaction(tx string) bool {
|
||||
if self.wantNatSpec {
|
||||
ds, err := docserver.New("/tmp/")
|
||||
if err != nil {
|
||||
self.t.Errorf("Error creating DocServer: %v", err)
|
||||
}
|
||||
ds := docserver.New("/tmp/")
|
||||
self.lastConfirm = GetNotice(self.xeth, tx, ds)
|
||||
}
|
||||
return true
|
||||
@ -159,11 +155,16 @@ func testInit(t *testing.T) (self *testFrontend) {
|
||||
self.stateDb = self.ethereum.ChainManager().State().Copy()
|
||||
|
||||
// initialise the registry contracts
|
||||
// self.resolver.CreateContracts(addr)
|
||||
resolver.New(self.xeth).CreateContracts(addr)
|
||||
reg := registrar.New(self.xeth)
|
||||
err = reg.SetHashReg("", addr)
|
||||
if err != nil {
|
||||
t.Errorf("error creating HashReg: %v", err)
|
||||
}
|
||||
err = reg.SetUrlHint("", addr)
|
||||
if err != nil {
|
||||
t.Errorf("error creating UrlHint: %v", err)
|
||||
}
|
||||
self.applyTxs()
|
||||
// t.Logf("HashReg contract registered at %v", resolver.HashRegContractAddress)
|
||||
// t.Logf("URLHint contract registered at %v", resolver.UrlHintContractAddress)
|
||||
|
||||
return
|
||||
|
||||
@ -192,13 +193,20 @@ func TestNatspecE2E(t *testing.T) {
|
||||
dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo)))
|
||||
|
||||
// take the codehash for the contract we wanna test
|
||||
// codehex := tf.xeth.CodeAt(resolver.HashRegContractAddress)
|
||||
codeb := tf.xeth.CodeAtBytes(resolver.HashRegContractAddress)
|
||||
// codehex := tf.xeth.CodeAt(registar.HashRegAddr)
|
||||
codeb := tf.xeth.CodeAtBytes(registrar.HashRegAddr)
|
||||
codehash := common.BytesToHash(crypto.Sha3(codeb))
|
||||
|
||||
// use resolver to register codehash->dochash->url
|
||||
registry := resolver.New(tf.xeth)
|
||||
_, err := registry.Register(tf.coinbase, codehash, dochash, "file:///"+testFileName)
|
||||
// test if globalregistry works
|
||||
// registrar.HashRefAddr = "0x0"
|
||||
// registrar.UrlHintAddr = "0x0"
|
||||
reg := registrar.New(tf.xeth)
|
||||
_, err := reg.SetHashToHash(tf.coinbase, codehash, dochash)
|
||||
if err != nil {
|
||||
t.Errorf("error registering: %v", err)
|
||||
}
|
||||
_, err = reg.SetUrlToHash(tf.coinbase, dochash, "file:///"+testFileName)
|
||||
if err != nil {
|
||||
t.Errorf("error registering: %v", err)
|
||||
}
|
||||
@ -209,18 +217,19 @@ func TestNatspecE2E(t *testing.T) {
|
||||
// now using the same transactions to check confirm messages
|
||||
|
||||
tf.wantNatSpec = true // this is set so now the backend uses natspec confirmation
|
||||
_, err = registry.RegisterContentHash(tf.coinbase, codehash, dochash)
|
||||
_, err = reg.SetHashToHash(tf.coinbase, codehash, dochash)
|
||||
if err != nil {
|
||||
t.Errorf("error calling contract registry: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("GlobalRegistrar: %v, HashReg: %v, UrlHint: %v\n", registrar.GlobalRegistrarAddr, registrar.HashRegAddr, registrar.UrlHintAddr)
|
||||
if tf.lastConfirm != testExpNotice {
|
||||
t.Errorf("Wrong confirm message. expected '%v', got '%v'", testExpNotice, tf.lastConfirm)
|
||||
}
|
||||
|
||||
// test unknown method
|
||||
exp := fmt.Sprintf(testExpNotice2, resolver.HashRegContractAddress)
|
||||
_, err = registry.SetOwner(tf.coinbase)
|
||||
exp := fmt.Sprintf(testExpNotice2, registrar.HashRegAddr)
|
||||
_, err = reg.SetOwner(tf.coinbase)
|
||||
if err != nil {
|
||||
t.Errorf("error setting owner: %v", err)
|
||||
}
|
||||
@ -230,9 +239,9 @@ func TestNatspecE2E(t *testing.T) {
|
||||
}
|
||||
|
||||
// test unknown contract
|
||||
exp = fmt.Sprintf(testExpNotice3, resolver.UrlHintContractAddress)
|
||||
exp = fmt.Sprintf(testExpNotice3, registrar.UrlHintAddr)
|
||||
|
||||
_, err = registry.RegisterUrl(tf.coinbase, dochash, "file:///test.content")
|
||||
_, err = reg.SetUrlToHash(tf.coinbase, dochash, "file:///test.content")
|
||||
if err != nil {
|
||||
t.Errorf("error registering: %v", err)
|
||||
}
|
||||
|
147
common/registrar/contracts.go
Normal file
147
common/registrar/contracts.go
Normal file
File diff suppressed because one or more lines are too long
32
common/registrar/ethreg/ethreg.go
Normal file
32
common/registrar/ethreg/ethreg.go
Normal file
@ -0,0 +1,32 @@
|
||||
package ethreg
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/registrar"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
// implements a versioned Registrar on an archiving full node
|
||||
type EthReg struct {
|
||||
backend *xeth.XEth
|
||||
registry *registrar.Registrar
|
||||
}
|
||||
|
||||
func New(xe *xeth.XEth) (self *EthReg) {
|
||||
self = &EthReg{backend: xe}
|
||||
self.registry = registrar.New(xe)
|
||||
return
|
||||
}
|
||||
|
||||
func (self *EthReg) Registry() *registrar.Registrar {
|
||||
return self.registry
|
||||
}
|
||||
|
||||
func (self *EthReg) Resolver(n *big.Int) *registrar.Registrar {
|
||||
xe := self.backend
|
||||
if n != nil {
|
||||
xe = self.backend.AtStateNum(n.Int64())
|
||||
}
|
||||
return registrar.New(xe)
|
||||
}
|
398
common/registrar/registrar.go
Normal file
398
common/registrar/registrar.go
Normal file
@ -0,0 +1,398 @@
|
||||
package registrar
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"regexp"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
/*
|
||||
Registrar implements the Ethereum name registrar services mapping
|
||||
- arbitrary strings to ethereum addresses
|
||||
- hashes to hashes
|
||||
- hashes to arbitrary strings
|
||||
(likely will provide lookup service for all three)
|
||||
|
||||
The Registrar is used by
|
||||
* the roundtripper transport implementation of
|
||||
url schemes to resolve domain names and services that register these names
|
||||
* contract info retrieval (NatSpec).
|
||||
|
||||
The Registrar uses 3 contracts on the blockchain:
|
||||
* GlobalRegistrar: Name (string) -> Address (Owner)
|
||||
* HashReg : Key Hash (hash of domain name or contract code) -> Content Hash
|
||||
* UrlHint : Content Hash -> Url Hint
|
||||
|
||||
These contracts are (currently) not included in the genesis block.
|
||||
Each Set<X> needs to be called once on each blockchain/network once.
|
||||
|
||||
Contract addresses need to be set (HashReg and UrlHint retrieved from the global
|
||||
registrar the first time any Registrar method is called in a client session
|
||||
|
||||
So the caller needs to make sure the relevant environment initialised the desired
|
||||
contracts
|
||||
*/
|
||||
var (
|
||||
UrlHintAddr = "0x0"
|
||||
HashRegAddr = "0x0"
|
||||
// GlobalRegistrarAddr = "0x0"
|
||||
GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
|
||||
zero = regexp.MustCompile("^(0x)?0*$")
|
||||
)
|
||||
|
||||
const (
|
||||
trueHex = "0000000000000000000000000000000000000000000000000000000000000001"
|
||||
falseHex = "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
)
|
||||
|
||||
func abiSignature(s string) string {
|
||||
return common.ToHex(crypto.Sha3([]byte(s))[:4])
|
||||
}
|
||||
|
||||
var (
|
||||
HashRegName = "HashReg"
|
||||
UrlHintName = "UrlHint"
|
||||
|
||||
registerContentHashAbi = abiSignature("register(uint256,uint256)")
|
||||
registerUrlAbi = abiSignature("register(uint256,uint8,uint256)")
|
||||
setOwnerAbi = abiSignature("setowner()")
|
||||
reserveAbi = abiSignature("reserve(bytes32)")
|
||||
resolveAbi = abiSignature("addr(bytes32)")
|
||||
registerAbi = abiSignature("setAddress(bytes32,address,bool)")
|
||||
addressAbiPrefix = falseHex[:24]
|
||||
)
|
||||
|
||||
// Registrar's backend is defined as an interface (implemented by xeth, but could be remote)
|
||||
type Backend interface {
|
||||
StorageAt(string, string) string
|
||||
Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error)
|
||||
Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error)
|
||||
}
|
||||
|
||||
// TODO Registrar should also just implement The Resolver and Registry interfaces
|
||||
// Simplify for now.
|
||||
type VersionedRegistrar interface {
|
||||
Resolver(*big.Int) *Registrar
|
||||
Registry() *Registrar
|
||||
}
|
||||
|
||||
type Registrar struct {
|
||||
backend Backend
|
||||
}
|
||||
|
||||
func New(b Backend) (res *Registrar) {
|
||||
res = &Registrar{b}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Registrar) SetGlobalRegistrar(namereg string, addr common.Address) (err error) {
|
||||
if namereg != "" {
|
||||
GlobalRegistrarAddr = namereg
|
||||
return
|
||||
}
|
||||
if GlobalRegistrarAddr == "0x0" || GlobalRegistrarAddr == "0x" {
|
||||
if (addr == common.Address{}) {
|
||||
err = fmt.Errorf("GlobalRegistrar address not found and sender for creation not given")
|
||||
return
|
||||
} else {
|
||||
GlobalRegistrarAddr, err = self.backend.Transact(addr.Hex(), "", "", "", "800000", "", GlobalRegistrarCode)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("GlobalRegistrar address not found and sender for creation failed: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Registrar) SetHashReg(hashreg string, addr common.Address) (err error) {
|
||||
if hashreg != "" {
|
||||
HashRegAddr = hashreg
|
||||
} else {
|
||||
if !zero.MatchString(HashRegAddr) {
|
||||
return
|
||||
}
|
||||
nameHex, extra := encodeName(HashRegName, 2)
|
||||
hashRegAbi := resolveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("\ncall HashRegAddr %v with %v\n", GlobalRegistrarAddr, hashRegAbi)
|
||||
HashRegAddr, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi)
|
||||
if err != nil || zero.MatchString(HashRegAddr) {
|
||||
if (addr == common.Address{}) {
|
||||
err = fmt.Errorf("HashReg address not found and sender for creation not given")
|
||||
return
|
||||
}
|
||||
|
||||
HashRegAddr, err = self.backend.Transact(addr.Hex(), "", "", "", "200000", "", HashRegCode)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("HashReg address not found and sender for creation failed: %v", err)
|
||||
}
|
||||
glog.V(logger.Detail).Infof("created HashRegAddr @ %v\n", HashRegAddr)
|
||||
} else {
|
||||
glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// register as HashReg
|
||||
self.ReserveName(addr, HashRegName)
|
||||
self.SetAddressToName(addr, HashRegName, common.HexToAddress(HashRegAddr))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (err error) {
|
||||
if urlhint != "" {
|
||||
UrlHintAddr = urlhint
|
||||
} else {
|
||||
if !zero.MatchString(UrlHintAddr) {
|
||||
return
|
||||
}
|
||||
nameHex, extra := encodeName(UrlHintName, 2)
|
||||
urlHintAbi := resolveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("UrlHint address query data: %s to %s", urlHintAbi, GlobalRegistrarAddr)
|
||||
UrlHintAddr, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi)
|
||||
if err != nil || zero.MatchString(UrlHintAddr) {
|
||||
if (addr == common.Address{}) {
|
||||
err = fmt.Errorf("UrlHint address not found and sender for creation not given")
|
||||
return
|
||||
}
|
||||
UrlHintAddr, err = self.backend.Transact(addr.Hex(), "", "", "", "210000", "", UrlHintCode)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("UrlHint address not found and sender for creation failed: %v", err)
|
||||
}
|
||||
glog.V(logger.Detail).Infof("created UrlHint @ %v\n", HashRegAddr)
|
||||
} else {
|
||||
glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// register as UrlHint
|
||||
self.ReserveName(addr, UrlHintName)
|
||||
self.SetAddressToName(addr, UrlHintName, common.HexToAddress(UrlHintAddr))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReserveName(from, name) reserves name for the sender address in the globalRegistrar
|
||||
// the tx needs to be mined to take effect
|
||||
func (self *Registrar) ReserveName(address common.Address, name string) (txh string, err error) {
|
||||
nameHex, extra := encodeName(name, 2)
|
||||
abi := reserveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("Reserve data: %s", abi)
|
||||
return self.backend.Transact(
|
||||
address.Hex(),
|
||||
GlobalRegistrarAddr,
|
||||
"", "", "", "",
|
||||
abi,
|
||||
)
|
||||
}
|
||||
|
||||
// SetAddressToName(from, name, addr) will set the Address to address for name
|
||||
// in the globalRegistrar using from as the sender of the transaction
|
||||
// the tx needs to be mined to take effect
|
||||
func (self *Registrar) SetAddressToName(from common.Address, name string, address common.Address) (txh string, err error) {
|
||||
nameHex, extra := encodeName(name, 6)
|
||||
addrHex := encodeAddress(address)
|
||||
|
||||
abi := registerAbi + nameHex + addrHex + trueHex + extra
|
||||
glog.V(logger.Detail).Infof("SetAddressToName data: %s to %s ", abi, GlobalRegistrarAddr)
|
||||
|
||||
return self.backend.Transact(
|
||||
from.Hex(),
|
||||
GlobalRegistrarAddr,
|
||||
"", "", "", "",
|
||||
abi,
|
||||
)
|
||||
}
|
||||
|
||||
// NameToAddr(from, name) queries the registrar for the address on name
|
||||
func (self *Registrar) NameToAddr(from common.Address, name string) (address common.Address, err error) {
|
||||
nameHex, extra := encodeName(name, 2)
|
||||
abi := resolveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("NameToAddr data: %s", abi)
|
||||
res, _, err := self.backend.Call(
|
||||
from.Hex(),
|
||||
GlobalRegistrarAddr,
|
||||
"", "", "",
|
||||
abi,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
address = common.HexToAddress(res)
|
||||
return
|
||||
}
|
||||
|
||||
// called as first step in the registration process on HashReg
|
||||
func (self *Registrar) SetOwner(address common.Address) (txh string, err error) {
|
||||
return self.backend.Transact(
|
||||
address.Hex(),
|
||||
HashRegAddr,
|
||||
"", "", "", "",
|
||||
setOwnerAbi,
|
||||
)
|
||||
}
|
||||
|
||||
// registers some content hash to a key/code hash
|
||||
// e.g., the contract Info combined Json Doc's ContentHash
|
||||
// to CodeHash of a contract or hash of a domain
|
||||
func (self *Registrar) SetHashToHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) {
|
||||
_, err = self.SetOwner(address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
codehex := common.Bytes2Hex(codehash[:])
|
||||
dochex := common.Bytes2Hex(dochash[:])
|
||||
|
||||
data := registerContentHashAbi + codehex + dochex
|
||||
glog.V(logger.Detail).Infof("SetHashToHash data: %s sent to %v\n", data, HashRegAddr)
|
||||
return self.backend.Transact(
|
||||
address.Hex(),
|
||||
HashRegAddr,
|
||||
"", "", "", "",
|
||||
data,
|
||||
)
|
||||
}
|
||||
|
||||
// SetUrlToHash(from, hash, url) registers a url to a content hash so that the content can be fetched
|
||||
// address is used as sender for the transaction and will be the owner of a new
|
||||
// registry entry on first time use
|
||||
// FIXME: silently doing nothing if sender is not the owner
|
||||
// note that with content addressed storage, this step is no longer necessary
|
||||
func (self *Registrar) SetUrlToHash(address common.Address, hash common.Hash, url string) (txh string, err error) {
|
||||
hashHex := common.Bytes2Hex(hash[:])
|
||||
var urlHex string
|
||||
urlb := []byte(url)
|
||||
var cnt byte
|
||||
n := len(urlb)
|
||||
|
||||
for n > 0 {
|
||||
if n > 32 {
|
||||
n = 32
|
||||
}
|
||||
urlHex = common.Bytes2Hex(urlb[:n])
|
||||
urlb = urlb[n:]
|
||||
n = len(urlb)
|
||||
bcnt := make([]byte, 32)
|
||||
bcnt[31] = cnt
|
||||
data := registerUrlAbi +
|
||||
hashHex +
|
||||
common.Bytes2Hex(bcnt) +
|
||||
common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32))
|
||||
txh, err = self.backend.Transact(
|
||||
address.Hex(),
|
||||
UrlHintAddr,
|
||||
"", "", "", "",
|
||||
data,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cnt++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HashToHash(key) resolves contenthash for key (a hash) using HashReg
|
||||
// resolution is costless non-transactional
|
||||
// implemented as direct retrieval from db
|
||||
func (self *Registrar) HashToHash(khash common.Hash) (chash common.Hash, err error) {
|
||||
// look up in hashReg
|
||||
at := HashRegAddr[2:]
|
||||
key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
|
||||
hash := self.backend.StorageAt(at, key)
|
||||
|
||||
if hash == "0x0" || len(hash) < 3 || (hash == common.Hash{}.Hex()) {
|
||||
err = fmt.Errorf("content hash not found for '%v'", khash.Hex())
|
||||
return
|
||||
}
|
||||
copy(chash[:], common.Hex2BytesFixed(hash[2:], 32))
|
||||
return
|
||||
}
|
||||
|
||||
// HashToUrl(contenthash) resolves the url for contenthash using UrlHint
|
||||
// resolution is costless non-transactional
|
||||
// implemented as direct retrieval from db
|
||||
// if we use content addressed storage, this step is no longer necessary
|
||||
func (self *Registrar) HashToUrl(chash common.Hash) (uri string, err error) {
|
||||
// look up in URL reg
|
||||
var str string = " "
|
||||
var idx uint32
|
||||
for len(str) > 0 {
|
||||
mapaddr := storageMapping(storageIdx2Addr(1), chash[:])
|
||||
key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx)))
|
||||
hex := self.backend.StorageAt(UrlHintAddr[2:], key)
|
||||
str = string(common.Hex2Bytes(hex[2:]))
|
||||
l := len(str)
|
||||
for (l > 0) && (str[l-1] == 0) {
|
||||
l--
|
||||
}
|
||||
|
||||
str = str[:l]
|
||||
uri = uri + str
|
||||
idx++
|
||||
}
|
||||
|
||||
l := 0
|
||||
for (l < len(uri)) && (uri[l] == 0) {
|
||||
l++
|
||||
}
|
||||
uri = uri[l:]
|
||||
|
||||
if len(uri) == 0 {
|
||||
err = fmt.Errorf("GetURLhint: URL hint not found for '%v'", chash.Hex())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func storageIdx2Addr(varidx uint32) []byte {
|
||||
data := make([]byte, 32)
|
||||
binary.BigEndian.PutUint32(data[28:32], varidx)
|
||||
return data
|
||||
}
|
||||
|
||||
func storageMapping(addr, key []byte) []byte {
|
||||
data := make([]byte, 64)
|
||||
copy(data[0:32], key[0:32])
|
||||
copy(data[32:64], addr[0:32])
|
||||
sha := crypto.Sha3(data)
|
||||
return sha
|
||||
}
|
||||
|
||||
func storageFixedArray(addr, idx []byte) []byte {
|
||||
var carry byte
|
||||
for i := 31; i >= 0; i-- {
|
||||
var b byte = addr[i] + idx[i] + carry
|
||||
if b < addr[i] {
|
||||
carry = 1
|
||||
} else {
|
||||
carry = 0
|
||||
}
|
||||
addr[i] = b
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
func storageAddress(addr []byte) string {
|
||||
return common.ToHex(addr)
|
||||
}
|
||||
|
||||
func encodeAddress(address common.Address) string {
|
||||
return addressAbiPrefix + address.Hex()[2:]
|
||||
}
|
||||
|
||||
func encodeName(name string, index uint8) (string, string) {
|
||||
extra := common.Bytes2Hex([]byte(name))
|
||||
if len(name) > 32 {
|
||||
return fmt.Sprintf("%064x", index), extra
|
||||
}
|
||||
return extra + falseHex[len(extra):], ""
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package resolver
|
||||
package registrar
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@ -20,22 +20,22 @@ var (
|
||||
)
|
||||
|
||||
func NewTestBackend() *testBackend {
|
||||
HashRegContractAddress = common.BigToAddress(common.Big0).Hex()[2:]
|
||||
UrlHintContractAddress = common.BigToAddress(common.Big1).Hex()[2:]
|
||||
HashRegAddr = common.BigToAddress(common.Big0).Hex() //[2:]
|
||||
UrlHintAddr = common.BigToAddress(common.Big1).Hex() //[2:]
|
||||
self := &testBackend{}
|
||||
self.contracts = make(map[string](map[string]string))
|
||||
|
||||
self.contracts[HashRegContractAddress] = make(map[string]string)
|
||||
self.contracts[HashRegAddr[2:]] = make(map[string]string)
|
||||
key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:]))
|
||||
self.contracts[HashRegContractAddress][key] = hash.Hex()
|
||||
self.contracts[HashRegAddr[2:]][key] = hash.Hex()
|
||||
|
||||
self.contracts[UrlHintContractAddress] = make(map[string]string)
|
||||
self.contracts[UrlHintAddr[2:]] = make(map[string]string)
|
||||
mapaddr := storageMapping(storageIdx2Addr(1), hash[:])
|
||||
|
||||
key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(0)))
|
||||
self.contracts[UrlHintContractAddress][key] = common.ToHex([]byte(url))
|
||||
self.contracts[UrlHintAddr[2:]][key] = common.ToHex([]byte(url))
|
||||
key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1)))
|
||||
self.contracts[UrlHintContractAddress][key] = "0x00"
|
||||
self.contracts[UrlHintAddr[2:]][key] = "0x0"
|
||||
return self
|
||||
}
|
||||
|
||||
@ -52,11 +52,25 @@ func (self *testBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, ga
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func TestKeyToContentHash(t *testing.T) {
|
||||
func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) {
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
func TestSetGlobalRegistrar(t *testing.T) {
|
||||
b := NewTestBackend()
|
||||
res := New(b)
|
||||
err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1))
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v'", err)
|
||||
}
|
||||
}
|
||||
|
||||
got, err := res.KeyToContentHash(codehash)
|
||||
func TestHashToHash(t *testing.T) {
|
||||
b := NewTestBackend()
|
||||
res := New(b)
|
||||
// res.SetHashReg()
|
||||
|
||||
got, err := res.HashToHash(codehash)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
} else {
|
||||
@ -66,28 +80,17 @@ func TestKeyToContentHash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestContentHashToUrl(t *testing.T) {
|
||||
func TestHashToUrl(t *testing.T) {
|
||||
b := NewTestBackend()
|
||||
res := New(b)
|
||||
got, err := res.ContentHashToUrl(hash)
|
||||
// res.SetUrlHint()
|
||||
|
||||
got, err := res.HashToUrl(hash)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
t.Errorf("expected error, got %v", err)
|
||||
} else {
|
||||
if got != url {
|
||||
t.Errorf("incorrect result, expected '%v', got '%s'", url, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyToUrl(t *testing.T) {
|
||||
b := NewTestBackend()
|
||||
res := New(b)
|
||||
got, _, err := res.KeyToUrl(codehash)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
} else {
|
||||
if got != url {
|
||||
t.Errorf("incorrect result, expected \n'%s', got \n'%s'", url, got)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package resolver
|
||||
|
||||
const ( // built-in contracts address and code
|
||||
ContractCodeURLhint = "0x60c180600c6000396000f30060003560e060020a90048063300a3bbf14601557005b6024600435602435604435602a565b60006000f35b6000600084815260200190815260200160002054600160a060020a0316600014806078575033600160a060020a03166000600085815260200190815260200160002054600160a060020a0316145b607f5760bc565b336000600085815260200190815260200160002081905550806001600085815260200190815260200160002083610100811060b657005b01819055505b50505056"
|
||||
/*
|
||||
contract URLhint {
|
||||
function register(uint256 _hash, uint8 idx, uint256 _url) {
|
||||
if (owner[_hash] == 0 || owner[_hash] == msg.sender) {
|
||||
owner[_hash] = msg.sender;
|
||||
url[_hash][idx] = _url;
|
||||
}
|
||||
}
|
||||
mapping (uint256 => address) owner;
|
||||
mapping (uint256 => uint256[256]) url;
|
||||
}
|
||||
*/
|
||||
|
||||
ContractCodeHashReg = "0x609880600c6000396000f30060003560e060020a9004806331e12c2014601f578063d66d6c1014602b57005b6025603d565b60006000f35b6037600435602435605d565b60006000f35b600054600160a060020a0316600014605357605b565b336000819055505b565b600054600160a060020a031633600160a060020a031614607b576094565b8060016000848152602001908152602001600020819055505b505056"
|
||||
/*
|
||||
contract HashReg {
|
||||
function setowner() {
|
||||
if (owner == 0) {
|
||||
owner = msg.sender;
|
||||
}
|
||||
}
|
||||
function register(uint256 _key, uint256 _content) {
|
||||
if (msg.sender == owner) {
|
||||
content[_key] = _content;
|
||||
}
|
||||
}
|
||||
address owner;
|
||||
mapping (uint256 => uint256) content;
|
||||
}
|
||||
*/
|
||||
|
||||
)
|
@ -1,232 +0,0 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
/*
|
||||
Resolver implements the Ethereum DNS mapping
|
||||
HashReg : Key Hash (hash of domain name or contract code) -> Content Hash
|
||||
UrlHint : Content Hash -> Url Hint
|
||||
|
||||
The resolver is meant to be called by the roundtripper transport implementation
|
||||
of a url scheme
|
||||
*/
|
||||
|
||||
// // contract addresses will be hardcoded after they're created
|
||||
var UrlHintContractAddress, HashRegContractAddress string
|
||||
|
||||
const (
|
||||
txValue = "0"
|
||||
txGas = "100000"
|
||||
txGasPrice = "1000000000000"
|
||||
)
|
||||
|
||||
func abi(s string) string {
|
||||
return common.ToHex(crypto.Sha3([]byte(s))[:4])
|
||||
}
|
||||
|
||||
var (
|
||||
registerContentHashAbi = abi("register(uint256,uint256)")
|
||||
registerUrlAbi = abi("register(uint256,uint8,uint256)")
|
||||
setOwnerAbi = abi("setowner()")
|
||||
)
|
||||
|
||||
type Backend interface {
|
||||
StorageAt(string, string) string
|
||||
Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error)
|
||||
}
|
||||
|
||||
type Resolver struct {
|
||||
backend Backend
|
||||
}
|
||||
|
||||
func New(eth Backend) *Resolver {
|
||||
return &Resolver{eth}
|
||||
}
|
||||
|
||||
// for testing and play temporarily
|
||||
// ideally the HashReg and UrlHint contracts should be in the genesis block
|
||||
// if we got build-in support for natspec/contract info
|
||||
// there should be only one of these officially endorsed
|
||||
// addresses as constants
|
||||
// TODO: could get around this with namereg, check
|
||||
func (self *Resolver) CreateContracts(addr common.Address) (err error) {
|
||||
HashRegContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeHashReg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
UrlHintContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeURLhint)
|
||||
glog.V(logger.Detail).Infof("HashReg @ %v\nUrlHint @ %v\n", HashRegContractAddress, UrlHintContractAddress)
|
||||
return
|
||||
}
|
||||
|
||||
// called as first step in the registration process on HashReg
|
||||
func (self *Resolver) SetOwner(address common.Address) (txh string, err error) {
|
||||
return self.backend.Transact(
|
||||
address.Hex(),
|
||||
HashRegContractAddress,
|
||||
"", txValue, txGas, txGasPrice,
|
||||
setOwnerAbi,
|
||||
)
|
||||
}
|
||||
|
||||
// registers some content hash to a key/code hash
|
||||
// e.g., the contract Info combined Json Doc's ContentHash
|
||||
// to CodeHash of a contract or hash of a domain
|
||||
// kept
|
||||
func (self *Resolver) RegisterContentHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) {
|
||||
_, err = self.SetOwner(address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
codehex := common.Bytes2Hex(codehash[:])
|
||||
dochex := common.Bytes2Hex(dochash[:])
|
||||
|
||||
data := registerContentHashAbi + codehex + dochex
|
||||
return self.backend.Transact(
|
||||
address.Hex(),
|
||||
HashRegContractAddress,
|
||||
"", txValue, txGas, txGasPrice,
|
||||
data,
|
||||
)
|
||||
}
|
||||
|
||||
// registers a url to a content hash so that the content can be fetched
|
||||
// address is used as sender for the transaction and will be the owner of a new
|
||||
// registry entry on first time use
|
||||
// FIXME: silently doing nothing if sender is not the owner
|
||||
// note that with content addressed storage, this step is no longer necessary
|
||||
// it could be purely
|
||||
func (self *Resolver) RegisterUrl(address common.Address, hash common.Hash, url string) (txh string, err error) {
|
||||
hashHex := common.Bytes2Hex(hash[:])
|
||||
var urlHex string
|
||||
urlb := []byte(url)
|
||||
var cnt byte
|
||||
n := len(urlb)
|
||||
|
||||
for n > 0 {
|
||||
if n > 32 {
|
||||
n = 32
|
||||
}
|
||||
urlHex = common.Bytes2Hex(urlb[:n])
|
||||
urlb = urlb[n:]
|
||||
n = len(urlb)
|
||||
bcnt := make([]byte, 32)
|
||||
bcnt[31] = cnt
|
||||
data := registerUrlAbi +
|
||||
hashHex +
|
||||
common.Bytes2Hex(bcnt) +
|
||||
common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32))
|
||||
txh, err = self.backend.Transact(
|
||||
address.Hex(),
|
||||
UrlHintContractAddress,
|
||||
"", txValue, txGas, txGasPrice,
|
||||
data,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cnt++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Resolver) Register(address common.Address, codehash, dochash common.Hash, url string) (txh string, err error) {
|
||||
|
||||
_, err = self.RegisterContentHash(address, codehash, dochash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return self.RegisterUrl(address, dochash, url)
|
||||
}
|
||||
|
||||
// resolution is costless non-transactional
|
||||
// implemented as direct retrieval from db
|
||||
func (self *Resolver) KeyToContentHash(khash common.Hash) (chash common.Hash, err error) {
|
||||
// look up in hashReg
|
||||
at := common.Bytes2Hex(common.FromHex(HashRegContractAddress))
|
||||
key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
|
||||
hash := self.backend.StorageAt(at, key)
|
||||
|
||||
if hash == "0x0" || len(hash) < 3 {
|
||||
err = fmt.Errorf("content hash not found for '%v'", khash.Hex())
|
||||
return
|
||||
}
|
||||
copy(chash[:], common.Hex2BytesFixed(hash[2:], 32))
|
||||
return
|
||||
}
|
||||
|
||||
// retrieves the url-hint for the content hash -
|
||||
// if we use content addressed storage, this step is no longer necessary
|
||||
func (self *Resolver) ContentHashToUrl(chash common.Hash) (uri string, err error) {
|
||||
// look up in URL reg
|
||||
var str string = " "
|
||||
var idx uint32
|
||||
for len(str) > 0 {
|
||||
mapaddr := storageMapping(storageIdx2Addr(1), chash[:])
|
||||
key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx)))
|
||||
hex := self.backend.StorageAt(UrlHintContractAddress, key)
|
||||
str = string(common.Hex2Bytes(hex[2:]))
|
||||
l := len(str)
|
||||
for (l > 0) && (str[l-1] == 0) {
|
||||
l--
|
||||
}
|
||||
str = str[:l]
|
||||
uri = uri + str
|
||||
idx++
|
||||
}
|
||||
|
||||
if len(uri) == 0 {
|
||||
err = fmt.Errorf("GetURLhint: URL hint not found for '%v'", chash.Hex())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Resolver) KeyToUrl(key common.Hash) (uri string, hash common.Hash, err error) {
|
||||
// look up in urlHint
|
||||
hash, err = self.KeyToContentHash(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uri, err = self.ContentHashToUrl(hash)
|
||||
return
|
||||
}
|
||||
|
||||
func storageIdx2Addr(varidx uint32) []byte {
|
||||
data := make([]byte, 32)
|
||||
binary.BigEndian.PutUint32(data[28:32], varidx)
|
||||
return data
|
||||
}
|
||||
|
||||
func storageMapping(addr, key []byte) []byte {
|
||||
data := make([]byte, 64)
|
||||
copy(data[0:32], key[0:32])
|
||||
copy(data[32:64], addr[0:32])
|
||||
sha := crypto.Sha3(data)
|
||||
return sha
|
||||
}
|
||||
|
||||
func storageFixedArray(addr, idx []byte) []byte {
|
||||
var carry byte
|
||||
for i := 31; i >= 0; i-- {
|
||||
var b byte = addr[i] + idx[i] + carry
|
||||
if b < addr[i] {
|
||||
carry = 1
|
||||
} else {
|
||||
carry = 0
|
||||
}
|
||||
addr[i] = b
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
func storageAddress(addr []byte) string {
|
||||
return common.ToHex(addr)
|
||||
}
|
Reference in New Issue
Block a user