mirror of https://github.com/ethereum/go-ethereum
commit
a3a07350dc
@ -0,0 +1,189 @@ |
|||||||
|
// Copyright 2018 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 bind |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
"reflect" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi" |
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
) |
||||||
|
|
||||||
|
// makeTopics converts a filter query argument list into a filter topic set.
|
||||||
|
func makeTopics(query ...[]interface{}) ([][]common.Hash, error) { |
||||||
|
topics := make([][]common.Hash, len(query)) |
||||||
|
for i, filter := range query { |
||||||
|
for _, rule := range filter { |
||||||
|
var topic common.Hash |
||||||
|
|
||||||
|
// Try to generate the topic based on simple types
|
||||||
|
switch rule := rule.(type) { |
||||||
|
case common.Hash: |
||||||
|
copy(topic[:], rule[:]) |
||||||
|
case common.Address: |
||||||
|
copy(topic[common.HashLength-common.AddressLength:], rule[:]) |
||||||
|
case *big.Int: |
||||||
|
blob := rule.Bytes() |
||||||
|
copy(topic[common.HashLength-len(blob):], blob) |
||||||
|
case bool: |
||||||
|
if rule { |
||||||
|
topic[common.HashLength-1] = 1 |
||||||
|
} |
||||||
|
case int8: |
||||||
|
blob := big.NewInt(int64(rule)).Bytes() |
||||||
|
copy(topic[common.HashLength-len(blob):], blob) |
||||||
|
case int16: |
||||||
|
blob := big.NewInt(int64(rule)).Bytes() |
||||||
|
copy(topic[common.HashLength-len(blob):], blob) |
||||||
|
case int32: |
||||||
|
blob := big.NewInt(int64(rule)).Bytes() |
||||||
|
copy(topic[common.HashLength-len(blob):], blob) |
||||||
|
case int64: |
||||||
|
blob := big.NewInt(rule).Bytes() |
||||||
|
copy(topic[common.HashLength-len(blob):], blob) |
||||||
|
case uint8: |
||||||
|
blob := new(big.Int).SetUint64(uint64(rule)).Bytes() |
||||||
|
copy(topic[common.HashLength-len(blob):], blob) |
||||||
|
case uint16: |
||||||
|
blob := new(big.Int).SetUint64(uint64(rule)).Bytes() |
||||||
|
copy(topic[common.HashLength-len(blob):], blob) |
||||||
|
case uint32: |
||||||
|
blob := new(big.Int).SetUint64(uint64(rule)).Bytes() |
||||||
|
copy(topic[common.HashLength-len(blob):], blob) |
||||||
|
case uint64: |
||||||
|
blob := new(big.Int).SetUint64(rule).Bytes() |
||||||
|
copy(topic[common.HashLength-len(blob):], blob) |
||||||
|
case string: |
||||||
|
hash := crypto.Keccak256Hash([]byte(rule)) |
||||||
|
copy(topic[:], hash[:]) |
||||||
|
case []byte: |
||||||
|
hash := crypto.Keccak256Hash(rule) |
||||||
|
copy(topic[:], hash[:]) |
||||||
|
|
||||||
|
default: |
||||||
|
// Attempt to generate the topic from funky types
|
||||||
|
val := reflect.ValueOf(rule) |
||||||
|
|
||||||
|
switch { |
||||||
|
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8: |
||||||
|
reflect.Copy(reflect.ValueOf(topic[common.HashLength-val.Len():]), val) |
||||||
|
|
||||||
|
default: |
||||||
|
return nil, fmt.Errorf("unsupported indexed type: %T", rule) |
||||||
|
} |
||||||
|
} |
||||||
|
topics[i] = append(topics[i], topic) |
||||||
|
} |
||||||
|
} |
||||||
|
return topics, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Big batch of reflect types for topic reconstruction.
|
||||||
|
var ( |
||||||
|
reflectHash = reflect.TypeOf(common.Hash{}) |
||||||
|
reflectAddress = reflect.TypeOf(common.Address{}) |
||||||
|
reflectBigInt = reflect.TypeOf(new(big.Int)) |
||||||
|
) |
||||||
|
|
||||||
|
// parseTopics converts the indexed topic fields into actual log field values.
|
||||||
|
//
|
||||||
|
// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
|
||||||
|
// hashes as the topic value!
|
||||||
|
func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) error { |
||||||
|
// Sanity check that the fields and topics match up
|
||||||
|
if len(fields) != len(topics) { |
||||||
|
return errors.New("topic/field count mismatch") |
||||||
|
} |
||||||
|
// Iterate over all the fields and reconstruct them from topics
|
||||||
|
for _, arg := range fields { |
||||||
|
if !arg.Indexed { |
||||||
|
return errors.New("non-indexed field in topic reconstruction") |
||||||
|
} |
||||||
|
field := reflect.ValueOf(out).Elem().FieldByName(capitalise(arg.Name)) |
||||||
|
|
||||||
|
// Try to parse the topic back into the fields based on primitive types
|
||||||
|
switch field.Kind() { |
||||||
|
case reflect.Bool: |
||||||
|
if topics[0][common.HashLength-1] == 1 { |
||||||
|
field.Set(reflect.ValueOf(true)) |
||||||
|
} |
||||||
|
case reflect.Int8: |
||||||
|
num := new(big.Int).SetBytes(topics[0][:]) |
||||||
|
field.Set(reflect.ValueOf(int8(num.Int64()))) |
||||||
|
|
||||||
|
case reflect.Int16: |
||||||
|
num := new(big.Int).SetBytes(topics[0][:]) |
||||||
|
field.Set(reflect.ValueOf(int16(num.Int64()))) |
||||||
|
|
||||||
|
case reflect.Int32: |
||||||
|
num := new(big.Int).SetBytes(topics[0][:]) |
||||||
|
field.Set(reflect.ValueOf(int32(num.Int64()))) |
||||||
|
|
||||||
|
case reflect.Int64: |
||||||
|
num := new(big.Int).SetBytes(topics[0][:]) |
||||||
|
field.Set(reflect.ValueOf(num.Int64())) |
||||||
|
|
||||||
|
case reflect.Uint8: |
||||||
|
num := new(big.Int).SetBytes(topics[0][:]) |
||||||
|
field.Set(reflect.ValueOf(uint8(num.Uint64()))) |
||||||
|
|
||||||
|
case reflect.Uint16: |
||||||
|
num := new(big.Int).SetBytes(topics[0][:]) |
||||||
|
field.Set(reflect.ValueOf(uint16(num.Uint64()))) |
||||||
|
|
||||||
|
case reflect.Uint32: |
||||||
|
num := new(big.Int).SetBytes(topics[0][:]) |
||||||
|
field.Set(reflect.ValueOf(uint32(num.Uint64()))) |
||||||
|
|
||||||
|
case reflect.Uint64: |
||||||
|
num := new(big.Int).SetBytes(topics[0][:]) |
||||||
|
field.Set(reflect.ValueOf(num.Uint64())) |
||||||
|
|
||||||
|
default: |
||||||
|
// Ran out of plain primitive types, try custom types
|
||||||
|
switch field.Type() { |
||||||
|
case reflectHash: // Also covers all dynamic types
|
||||||
|
field.Set(reflect.ValueOf(topics[0])) |
||||||
|
|
||||||
|
case reflectAddress: |
||||||
|
var addr common.Address |
||||||
|
copy(addr[:], topics[0][common.HashLength-common.AddressLength:]) |
||||||
|
field.Set(reflect.ValueOf(addr)) |
||||||
|
|
||||||
|
case reflectBigInt: |
||||||
|
num := new(big.Int).SetBytes(topics[0][:]) |
||||||
|
field.Set(reflect.ValueOf(num)) |
||||||
|
|
||||||
|
default: |
||||||
|
// Ran out of custom types, try the crazies
|
||||||
|
switch { |
||||||
|
case arg.Type.T == abi.FixedBytesTy: |
||||||
|
reflect.Copy(field, reflect.ValueOf(topics[0][common.HashLength-arg.Type.Size:])) |
||||||
|
|
||||||
|
default: |
||||||
|
return fmt.Errorf("unsupported indexed type: %v", arg.Type) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
topics = topics[1:] |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
ethkey |
||||||
|
====== |
||||||
|
|
||||||
|
ethkey is a simple command-line tool for working with Ethereum keyfiles. |
||||||
|
|
||||||
|
|
||||||
|
# Usage |
||||||
|
|
||||||
|
### `ethkey generate` |
||||||
|
|
||||||
|
Generate a new keyfile. |
||||||
|
If you want to use an existing private key to use in the keyfile, it can be |
||||||
|
specified by setting `--privatekey` with the location of the file containing the |
||||||
|
private key. |
||||||
|
|
||||||
|
|
||||||
|
### `ethkey inspect <keyfile>` |
||||||
|
|
||||||
|
Print various information about the keyfile. |
||||||
|
Private key information can be printed by using the `--private` flag; |
||||||
|
make sure to use this feature with great caution! |
||||||
|
|
||||||
|
|
||||||
|
### `ethkey sign <keyfile> <message/file>` |
||||||
|
|
||||||
|
Sign the message with a keyfile. |
||||||
|
It is possible to refer to a file containing the message. |
||||||
|
|
||||||
|
|
||||||
|
### `ethkey verify <address> <signature> <message/file>` |
||||||
|
|
||||||
|
Verify the signature of the message. |
||||||
|
It is possible to refer to a file containing the message. |
||||||
|
|
||||||
|
|
||||||
|
## Passphrases |
||||||
|
|
||||||
|
For every command that uses a keyfile, you will be prompted to provide the |
||||||
|
passphrase for decrypting the keyfile. To avoid this message, it is possible |
||||||
|
to pass the passphrase by using the `--passphrase` flag pointing to a file that |
||||||
|
contains the passphrase. |
@ -0,0 +1,118 @@ |
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"crypto/ecdsa" |
||||||
|
"fmt" |
||||||
|
"io/ioutil" |
||||||
|
"os" |
||||||
|
"path/filepath" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/keystore" |
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/pborman/uuid" |
||||||
|
"gopkg.in/urfave/cli.v1" |
||||||
|
) |
||||||
|
|
||||||
|
type outputGenerate struct { |
||||||
|
Address string |
||||||
|
AddressEIP55 string |
||||||
|
} |
||||||
|
|
||||||
|
var commandGenerate = cli.Command{ |
||||||
|
Name: "generate", |
||||||
|
Usage: "generate new keyfile", |
||||||
|
ArgsUsage: "[ <keyfile> ]", |
||||||
|
Description: ` |
||||||
|
Generate a new keyfile. |
||||||
|
|
||||||
|
If you want to encrypt an existing private key, it can be specified by setting |
||||||
|
--privatekey with the location of the file containing the private key. |
||||||
|
`, |
||||||
|
Flags: []cli.Flag{ |
||||||
|
passphraseFlag, |
||||||
|
jsonFlag, |
||||||
|
cli.StringFlag{ |
||||||
|
Name: "privatekey", |
||||||
|
Usage: "file containing a raw private key to encrypt", |
||||||
|
}, |
||||||
|
}, |
||||||
|
Action: func(ctx *cli.Context) error { |
||||||
|
// Check if keyfile path given and make sure it doesn't already exist.
|
||||||
|
keyfilepath := ctx.Args().First() |
||||||
|
if keyfilepath == "" { |
||||||
|
keyfilepath = defaultKeyfileName |
||||||
|
} |
||||||
|
if _, err := os.Stat(keyfilepath); err == nil { |
||||||
|
utils.Fatalf("Keyfile already exists at %s.", keyfilepath) |
||||||
|
} else if !os.IsNotExist(err) { |
||||||
|
utils.Fatalf("Error checking if keyfile exists: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
var privateKey *ecdsa.PrivateKey |
||||||
|
var err error |
||||||
|
if file := ctx.String("privatekey"); file != "" { |
||||||
|
// Load private key from file.
|
||||||
|
privateKey, err = crypto.LoadECDSA(file) |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Can't load private key: %v", err) |
||||||
|
} |
||||||
|
} else { |
||||||
|
// If not loaded, generate random.
|
||||||
|
privateKey, err = crypto.GenerateKey() |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Failed to generate random private key: %v", err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Create the keyfile object with a random UUID.
|
||||||
|
id := uuid.NewRandom() |
||||||
|
key := &keystore.Key{ |
||||||
|
Id: id, |
||||||
|
Address: crypto.PubkeyToAddress(privateKey.PublicKey), |
||||||
|
PrivateKey: privateKey, |
||||||
|
} |
||||||
|
|
||||||
|
// Encrypt key with passphrase.
|
||||||
|
passphrase := getPassPhrase(ctx, true) |
||||||
|
keyjson, err := keystore.EncryptKey(key, passphrase, keystore.StandardScryptN, keystore.StandardScryptP) |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Error encrypting key: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
// Store the file to disk.
|
||||||
|
if err := os.MkdirAll(filepath.Dir(keyfilepath), 0700); err != nil { |
||||||
|
utils.Fatalf("Could not create directory %s", filepath.Dir(keyfilepath)) |
||||||
|
} |
||||||
|
if err := ioutil.WriteFile(keyfilepath, keyjson, 0600); err != nil { |
||||||
|
utils.Fatalf("Failed to write keyfile to %s: %v", keyfilepath, err) |
||||||
|
} |
||||||
|
|
||||||
|
// Output some information.
|
||||||
|
out := outputGenerate{ |
||||||
|
Address: key.Address.Hex(), |
||||||
|
} |
||||||
|
if ctx.Bool(jsonFlag.Name) { |
||||||
|
mustPrintJSON(out) |
||||||
|
} else { |
||||||
|
fmt.Println("Address:", out.Address) |
||||||
|
} |
||||||
|
return nil |
||||||
|
}, |
||||||
|
} |
@ -0,0 +1,75 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/hex" |
||||||
|
"fmt" |
||||||
|
"io/ioutil" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/keystore" |
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"gopkg.in/urfave/cli.v1" |
||||||
|
) |
||||||
|
|
||||||
|
type outputInspect struct { |
||||||
|
Address string |
||||||
|
PublicKey string |
||||||
|
PrivateKey string |
||||||
|
} |
||||||
|
|
||||||
|
var commandInspect = cli.Command{ |
||||||
|
Name: "inspect", |
||||||
|
Usage: "inspect a keyfile", |
||||||
|
ArgsUsage: "<keyfile>", |
||||||
|
Description: ` |
||||||
|
Print various information about the keyfile. |
||||||
|
|
||||||
|
Private key information can be printed by using the --private flag; |
||||||
|
make sure to use this feature with great caution!`, |
||||||
|
Flags: []cli.Flag{ |
||||||
|
passphraseFlag, |
||||||
|
jsonFlag, |
||||||
|
cli.BoolFlag{ |
||||||
|
Name: "private", |
||||||
|
Usage: "include the private key in the output", |
||||||
|
}, |
||||||
|
}, |
||||||
|
Action: func(ctx *cli.Context) error { |
||||||
|
keyfilepath := ctx.Args().First() |
||||||
|
|
||||||
|
// Read key from file.
|
||||||
|
keyjson, err := ioutil.ReadFile(keyfilepath) |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Failed to read the keyfile at '%s': %v", keyfilepath, err) |
||||||
|
} |
||||||
|
|
||||||
|
// Decrypt key with passphrase.
|
||||||
|
passphrase := getPassPhrase(ctx, false) |
||||||
|
key, err := keystore.DecryptKey(keyjson, passphrase) |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Error decrypting key: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
// Output all relevant information we can retrieve.
|
||||||
|
showPrivate := ctx.Bool("private") |
||||||
|
out := outputInspect{ |
||||||
|
Address: key.Address.Hex(), |
||||||
|
PublicKey: hex.EncodeToString( |
||||||
|
crypto.FromECDSAPub(&key.PrivateKey.PublicKey)), |
||||||
|
} |
||||||
|
if showPrivate { |
||||||
|
out.PrivateKey = hex.EncodeToString(crypto.FromECDSA(key.PrivateKey)) |
||||||
|
} |
||||||
|
|
||||||
|
if ctx.Bool(jsonFlag.Name) { |
||||||
|
mustPrintJSON(out) |
||||||
|
} else { |
||||||
|
fmt.Println("Address: ", out.Address) |
||||||
|
fmt.Println("Public key: ", out.PublicKey) |
||||||
|
if showPrivate { |
||||||
|
fmt.Println("Private key: ", out.PrivateKey) |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
}, |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"os" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils" |
||||||
|
"gopkg.in/urfave/cli.v1" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
defaultKeyfileName = "keyfile.json" |
||||||
|
) |
||||||
|
|
||||||
|
// Git SHA1 commit hash of the release (set via linker flags)
|
||||||
|
var gitCommit = "" |
||||||
|
|
||||||
|
var app *cli.App |
||||||
|
|
||||||
|
func init() { |
||||||
|
app = utils.NewApp(gitCommit, "an Ethereum key manager") |
||||||
|
app.Commands = []cli.Command{ |
||||||
|
commandGenerate, |
||||||
|
commandInspect, |
||||||
|
commandSignMessage, |
||||||
|
commandVerifyMessage, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Commonly used command line flags.
|
||||||
|
var ( |
||||||
|
passphraseFlag = cli.StringFlag{ |
||||||
|
Name: "passwordfile", |
||||||
|
Usage: "the file that contains the passphrase for the keyfile", |
||||||
|
} |
||||||
|
jsonFlag = cli.BoolFlag{ |
||||||
|
Name: "json", |
||||||
|
Usage: "output JSON instead of human-readable format", |
||||||
|
} |
||||||
|
messageFlag = cli.StringFlag{ |
||||||
|
Name: "message", |
||||||
|
Usage: "the file that contains the message to sign/verify", |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
func main() { |
||||||
|
if err := app.Run(os.Args); err != nil { |
||||||
|
fmt.Fprintln(os.Stderr, err) |
||||||
|
os.Exit(1) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,159 @@ |
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/hex" |
||||||
|
"fmt" |
||||||
|
"io/ioutil" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/keystore" |
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils" |
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"gopkg.in/urfave/cli.v1" |
||||||
|
) |
||||||
|
|
||||||
|
type outputSign struct { |
||||||
|
Signature string |
||||||
|
} |
||||||
|
|
||||||
|
var msgfileFlag = cli.StringFlag{ |
||||||
|
Name: "msgfile", |
||||||
|
Usage: "file containing the message to sign/verify", |
||||||
|
} |
||||||
|
|
||||||
|
var commandSignMessage = cli.Command{ |
||||||
|
Name: "signmessage", |
||||||
|
Usage: "sign a message", |
||||||
|
ArgsUsage: "<keyfile> <message>", |
||||||
|
Description: ` |
||||||
|
Sign the message with a keyfile. |
||||||
|
|
||||||
|
To sign a message contained in a file, use the --msgfile flag. |
||||||
|
`, |
||||||
|
Flags: []cli.Flag{ |
||||||
|
passphraseFlag, |
||||||
|
jsonFlag, |
||||||
|
msgfileFlag, |
||||||
|
}, |
||||||
|
Action: func(ctx *cli.Context) error { |
||||||
|
message := getMessage(ctx, 1) |
||||||
|
|
||||||
|
// Load the keyfile.
|
||||||
|
keyfilepath := ctx.Args().First() |
||||||
|
keyjson, err := ioutil.ReadFile(keyfilepath) |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Failed to read the keyfile at '%s': %v", keyfilepath, err) |
||||||
|
} |
||||||
|
|
||||||
|
// Decrypt key with passphrase.
|
||||||
|
passphrase := getPassPhrase(ctx, false) |
||||||
|
key, err := keystore.DecryptKey(keyjson, passphrase) |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Error decrypting key: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
signature, err := crypto.Sign(signHash(message), key.PrivateKey) |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Failed to sign message: %v", err) |
||||||
|
} |
||||||
|
out := outputSign{Signature: hex.EncodeToString(signature)} |
||||||
|
if ctx.Bool(jsonFlag.Name) { |
||||||
|
mustPrintJSON(out) |
||||||
|
} else { |
||||||
|
fmt.Println("Signature:", out.Signature) |
||||||
|
} |
||||||
|
return nil |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
type outputVerify struct { |
||||||
|
Success bool |
||||||
|
RecoveredAddress string |
||||||
|
RecoveredPublicKey string |
||||||
|
} |
||||||
|
|
||||||
|
var commandVerifyMessage = cli.Command{ |
||||||
|
Name: "verifymessage", |
||||||
|
Usage: "verify the signature of a signed message", |
||||||
|
ArgsUsage: "<address> <signature> <message>", |
||||||
|
Description: ` |
||||||
|
Verify the signature of the message. |
||||||
|
It is possible to refer to a file containing the message.`, |
||||||
|
Flags: []cli.Flag{ |
||||||
|
jsonFlag, |
||||||
|
msgfileFlag, |
||||||
|
}, |
||||||
|
Action: func(ctx *cli.Context) error { |
||||||
|
addressStr := ctx.Args().First() |
||||||
|
signatureHex := ctx.Args().Get(1) |
||||||
|
message := getMessage(ctx, 2) |
||||||
|
|
||||||
|
if !common.IsHexAddress(addressStr) { |
||||||
|
utils.Fatalf("Invalid address: %s", addressStr) |
||||||
|
} |
||||||
|
address := common.HexToAddress(addressStr) |
||||||
|
signature, err := hex.DecodeString(signatureHex) |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Signature encoding is not hexadecimal: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
recoveredPubkey, err := crypto.SigToPub(signHash(message), signature) |
||||||
|
if err != nil || recoveredPubkey == nil { |
||||||
|
utils.Fatalf("Signature verification failed: %v", err) |
||||||
|
} |
||||||
|
recoveredPubkeyBytes := crypto.FromECDSAPub(recoveredPubkey) |
||||||
|
recoveredAddress := crypto.PubkeyToAddress(*recoveredPubkey) |
||||||
|
success := address == recoveredAddress |
||||||
|
|
||||||
|
out := outputVerify{ |
||||||
|
Success: success, |
||||||
|
RecoveredPublicKey: hex.EncodeToString(recoveredPubkeyBytes), |
||||||
|
RecoveredAddress: recoveredAddress.Hex(), |
||||||
|
} |
||||||
|
if ctx.Bool(jsonFlag.Name) { |
||||||
|
mustPrintJSON(out) |
||||||
|
} else { |
||||||
|
if out.Success { |
||||||
|
fmt.Println("Signature verification successful!") |
||||||
|
} else { |
||||||
|
fmt.Println("Signature verification failed!") |
||||||
|
} |
||||||
|
fmt.Println("Recovered public key:", out.RecoveredPublicKey) |
||||||
|
fmt.Println("Recovered address:", out.RecoveredAddress) |
||||||
|
} |
||||||
|
return nil |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
func getMessage(ctx *cli.Context, msgarg int) []byte { |
||||||
|
if file := ctx.String("msgfile"); file != "" { |
||||||
|
if len(ctx.Args()) > msgarg { |
||||||
|
utils.Fatalf("Can't use --msgfile and message argument at the same time.") |
||||||
|
} |
||||||
|
msg, err := ioutil.ReadFile(file) |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Can't read message file: %v", err) |
||||||
|
} |
||||||
|
return msg |
||||||
|
} else if len(ctx.Args()) == msgarg+1 { |
||||||
|
return []byte(ctx.Args().Get(msgarg)) |
||||||
|
} |
||||||
|
utils.Fatalf("Invalid number of arguments: want %d, got %d", msgarg+1, len(ctx.Args())) |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"io/ioutil" |
||||||
|
"os" |
||||||
|
"path/filepath" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
func TestMessageSignVerify(t *testing.T) { |
||||||
|
tmpdir, err := ioutil.TempDir("", "ethkey-test") |
||||||
|
if err != nil { |
||||||
|
t.Fatal("Can't create temporary directory:", err) |
||||||
|
} |
||||||
|
defer os.RemoveAll(tmpdir) |
||||||
|
|
||||||
|
keyfile := filepath.Join(tmpdir, "the-keyfile") |
||||||
|
message := "test message" |
||||||
|
|
||||||
|
// Create the key.
|
||||||
|
generate := runEthkey(t, "generate", keyfile) |
||||||
|
generate.Expect(` |
||||||
|
!! Unsupported terminal, password will be echoed. |
||||||
|
Passphrase: {{.InputLine "foobar"}} |
||||||
|
Repeat passphrase: {{.InputLine "foobar"}} |
||||||
|
`) |
||||||
|
_, matches := generate.ExpectRegexp(`Address: (0x[0-9a-fA-F]{40})\n`) |
||||||
|
address := matches[1] |
||||||
|
generate.ExpectExit() |
||||||
|
|
||||||
|
// Sign a message.
|
||||||
|
sign := runEthkey(t, "signmessage", keyfile, message) |
||||||
|
sign.Expect(` |
||||||
|
!! Unsupported terminal, password will be echoed. |
||||||
|
Passphrase: {{.InputLine "foobar"}} |
||||||
|
`) |
||||||
|
_, matches = sign.ExpectRegexp(`Signature: ([0-9a-f]+)\n`) |
||||||
|
signature := matches[1] |
||||||
|
sign.ExpectExit() |
||||||
|
|
||||||
|
// Verify the message.
|
||||||
|
verify := runEthkey(t, "verifymessage", address, signature, message) |
||||||
|
_, matches = verify.ExpectRegexp(` |
||||||
|
Signature verification successful! |
||||||
|
Recovered public key: [0-9a-f]+ |
||||||
|
Recovered address: (0x[0-9a-fA-F]{40}) |
||||||
|
`) |
||||||
|
recovered := matches[1] |
||||||
|
verify.ExpectExit() |
||||||
|
|
||||||
|
if recovered != address { |
||||||
|
t.Error("recovered address doesn't match generated key") |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"os" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/reexec" |
||||||
|
"github.com/ethereum/go-ethereum/internal/cmdtest" |
||||||
|
) |
||||||
|
|
||||||
|
type testEthkey struct { |
||||||
|
*cmdtest.TestCmd |
||||||
|
} |
||||||
|
|
||||||
|
// spawns ethkey with the given command line args.
|
||||||
|
func runEthkey(t *testing.T, args ...string) *testEthkey { |
||||||
|
tt := new(testEthkey) |
||||||
|
tt.TestCmd = cmdtest.NewTestCmd(t, tt) |
||||||
|
tt.Run("ethkey-test", args...) |
||||||
|
return tt |
||||||
|
} |
||||||
|
|
||||||
|
func TestMain(m *testing.M) { |
||||||
|
// Run the app if we've been exec'd as "ethkey-test" in runEthkey.
|
||||||
|
reexec.Register("ethkey-test", func() { |
||||||
|
if err := app.Run(os.Args); err != nil { |
||||||
|
fmt.Fprintln(os.Stderr, err) |
||||||
|
os.Exit(1) |
||||||
|
} |
||||||
|
os.Exit(0) |
||||||
|
}) |
||||||
|
// check if we have been reexec'd
|
||||||
|
if reexec.Init() { |
||||||
|
return |
||||||
|
} |
||||||
|
os.Exit(m.Run()) |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"io/ioutil" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils" |
||||||
|
"github.com/ethereum/go-ethereum/console" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"gopkg.in/urfave/cli.v1" |
||||||
|
) |
||||||
|
|
||||||
|
// getPassPhrase obtains a passphrase given by the user. It first checks the
|
||||||
|
// --passphrase command line flag and ultimately prompts the user for a
|
||||||
|
// passphrase.
|
||||||
|
func getPassPhrase(ctx *cli.Context, confirmation bool) string { |
||||||
|
// Look for the --passphrase flag.
|
||||||
|
passphraseFile := ctx.String(passphraseFlag.Name) |
||||||
|
if passphraseFile != "" { |
||||||
|
content, err := ioutil.ReadFile(passphraseFile) |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Failed to read passphrase file '%s': %v", |
||||||
|
passphraseFile, err) |
||||||
|
} |
||||||
|
return strings.TrimRight(string(content), "\r\n") |
||||||
|
} |
||||||
|
|
||||||
|
// Otherwise prompt the user for the passphrase.
|
||||||
|
passphrase, err := console.Stdin.PromptPassword("Passphrase: ") |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Failed to read passphrase: %v", err) |
||||||
|
} |
||||||
|
if confirmation { |
||||||
|
confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Failed to read passphrase confirmation: %v", err) |
||||||
|
} |
||||||
|
if passphrase != confirm { |
||||||
|
utils.Fatalf("Passphrases do not match") |
||||||
|
} |
||||||
|
} |
||||||
|
return passphrase |
||||||
|
} |
||||||
|
|
||||||
|
// signHash is a helper function that calculates a hash for the given message
|
||||||
|
// that can be safely used to calculate a signature from.
|
||||||
|
//
|
||||||
|
// The hash is calulcated as
|
||||||
|
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
|
||||||
|
//
|
||||||
|
// This gives context to the signed message and prevents signing of transactions.
|
||||||
|
func signHash(data []byte) []byte { |
||||||
|
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) |
||||||
|
return crypto.Keccak256([]byte(msg)) |
||||||
|
} |
||||||
|
|
||||||
|
// mustPrintJSON prints the JSON encoding of the given object and
|
||||||
|
// exits the program with an error message when the marshaling fails.
|
||||||
|
func mustPrintJSON(jsonObject interface{}) { |
||||||
|
str, err := json.MarshalIndent(jsonObject, "", " ") |
||||||
|
if err != nil { |
||||||
|
utils.Fatalf("Failed to marshal JSON object: %v", err) |
||||||
|
} |
||||||
|
fmt.Println(string(str)) |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
import "./owned.sol"; |
||||||
|
|
||||||
|
contract mortal is owned { |
||||||
|
function kill() public { |
||||||
|
if (msg.sender == owner) |
||||||
|
selfdestruct(owner); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
contract owned { |
||||||
|
address owner; |
||||||
|
|
||||||
|
modifier onlyowner() { |
||||||
|
if (msg.sender == owner) { |
||||||
|
_; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function owned() public { |
||||||
|
owner = msg.sender; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
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); |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
import './AbstractENS.sol'; |
||||||
|
|
||||||
|
/** |
||||||
|
* The ENS registry contract. |
||||||
|
*/ |
||||||
|
contract ENS is AbstractENS { |
||||||
|
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) { |
||||||
|
if (records[node].owner != msg.sender) throw; |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new ENS registrar. |
||||||
|
*/ |
||||||
|
function ENS() { |
||||||
|
records[0].owner = msg.sender; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
import './AbstractENS.sol'; |
||||||
|
|
||||||
|
/** |
||||||
|
* A registrar that allocates subdomains to the first person to claim them. |
||||||
|
*/ |
||||||
|
contract FIFSRegistrar { |
||||||
|
AbstractENS 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; |
||||||
|
|
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* @param ensAddr The address of the ENS registry. |
||||||
|
* @param node The node that this registrar administers. |
||||||
|
*/ |
||||||
|
function FIFSRegistrar(AbstractENS ensAddr, bytes32 node) { |
||||||
|
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 owner The address of the new owner. |
||||||
|
*/ |
||||||
|
function register(bytes32 subnode, address owner) only_owner(subnode) { |
||||||
|
ens.setSubnodeOwner(rootNode, subnode, owner); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -1,226 +0,0 @@ |
|||||||
// Ethereum Name Service contracts by Nick Johnson <nick@ethereum.org> |
|
||||||
// |
|
||||||
// To the extent possible under law, the person who associated CC0 with |
|
||||||
// ENS contracts has waived all copyright and related or neighboring rights |
|
||||||
// to ENS. |
|
||||||
// |
|
||||||
// You should have received a copy of the CC0 legalcode along with this |
|
||||||
// work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. |
|
||||||
|
|
||||||
/** |
|
||||||
* The ENS registry contract. |
|
||||||
*/ |
|
||||||
contract ENS { |
|
||||||
struct Record { |
|
||||||
address owner; |
|
||||||
address resolver; |
|
||||||
} |
|
||||||
|
|
||||||
mapping(bytes32=>Record) records; |
|
||||||
|
|
||||||
// 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 owner of a node changes the resolver for that node. |
|
||||||
event NewResolver(bytes32 indexed node, address resolver); |
|
||||||
|
|
||||||
// Permits modifications only by the owner of the specified node. |
|
||||||
modifier only_owner(bytes32 node) { |
|
||||||
if(records[node].owner != msg.sender) throw; |
|
||||||
_ |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Constructs a new ENS registrar, with the provided address as the owner of the root node. |
|
||||||
*/ |
|
||||||
function ENS(address owner) { |
|
||||||
records[0].owner = owner; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* A registrar that allocates subdomains to the first person to claim them. It also deploys |
|
||||||
* a simple resolver contract and sets that as the default resolver on new names for |
|
||||||
* convenience. |
|
||||||
*/ |
|
||||||
contract FIFSRegistrar { |
|
||||||
ENS ens; |
|
||||||
PublicResolver defaultResolver; |
|
||||||
bytes32 rootNode; |
|
||||||
|
|
||||||
/** |
|
||||||
* Constructor. |
|
||||||
* @param ensAddr The address of the ENS registry. |
|
||||||
* @param node The node that this registrar administers. |
|
||||||
*/ |
|
||||||
function FIFSRegistrar(address ensAddr, bytes32 node) { |
|
||||||
ens = ENS(ensAddr); |
|
||||||
defaultResolver = new PublicResolver(ensAddr); |
|
||||||
rootNode = node; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Register a name, or change the owner of an existing registration. |
|
||||||
* @param subnode The hash of the label to register. |
|
||||||
* @param owner The address of the new owner. |
|
||||||
*/ |
|
||||||
function register(bytes32 subnode, address owner) { |
|
||||||
var node = sha3(rootNode, subnode); |
|
||||||
var currentOwner = ens.owner(node); |
|
||||||
if(currentOwner != 0 && currentOwner != msg.sender) |
|
||||||
throw; |
|
||||||
|
|
||||||
// Temporarily set ourselves as the owner |
|
||||||
ens.setSubnodeOwner(rootNode, subnode, this); |
|
||||||
// Set up the default resolver |
|
||||||
ens.setResolver(node, defaultResolver); |
|
||||||
// Set the owner to the real owner |
|
||||||
ens.setOwner(node, owner); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
contract Resolver { |
|
||||||
event AddrChanged(bytes32 indexed node, address a); |
|
||||||
event ContentChanged(bytes32 indexed node, bytes32 hash); |
|
||||||
|
|
||||||
function has(bytes32 node, bytes32 kind) returns (bool); |
|
||||||
function addr(bytes32 node) constant returns (address ret); |
|
||||||
function content(bytes32 node) constant returns (bytes32 ret); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* A simple resolver anyone can use; only allows the owner of a node to set its |
|
||||||
* address. |
|
||||||
*/ |
|
||||||
contract PublicResolver is Resolver { |
|
||||||
ENS ens; |
|
||||||
mapping(bytes32=>address) addresses; |
|
||||||
mapping(bytes32=>bytes32) contents; |
|
||||||
|
|
||||||
modifier only_owner(bytes32 node) { |
|
||||||
if(ens.owner(node) != msg.sender) throw; |
|
||||||
_ |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Constructor. |
|
||||||
* @param ensAddr The ENS registrar contract. |
|
||||||
*/ |
|
||||||
function PublicResolver(address ensAddr) { |
|
||||||
ens = ENS(ensAddr); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Fallback function. |
|
||||||
*/ |
|
||||||
function() { |
|
||||||
throw; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns true if the specified node has the specified record type. |
|
||||||
* @param node The ENS node to query. |
|
||||||
* @param kind The record type name, as specified in EIP137. |
|
||||||
* @return True if this resolver has a record of the provided type on the |
|
||||||
* provided node. |
|
||||||
*/ |
|
||||||
function has(bytes32 node, bytes32 kind) returns (bool) { |
|
||||||
return (kind == "addr" && addresses[node] != 0) || |
|
||||||
(kind == "content" && contents[node] != 0); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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 = addresses[node]; |
|
||||||
if(ret == 0) |
|
||||||
throw; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the content hash associated with an ENS node. |
|
||||||
* @param node The ENS node to query. |
|
||||||
* @return The associated content hash. |
|
||||||
*/ |
|
||||||
function content(bytes32 node) constant returns (bytes32 ret) { |
|
||||||
ret = contents[node]; |
|
||||||
if(ret == 0) |
|
||||||
throw; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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) { |
|
||||||
addresses[node] = addr; |
|
||||||
AddrChanged(node, addr); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Sets the content hash 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 content hash to set. |
|
||||||
*/ |
|
||||||
function setContent(bytes32 node, bytes32 hash) only_owner(node) { |
|
||||||
contents[node] = hash; |
|
||||||
ContentChanged(node, hash); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,195 @@ |
|||||||
|
// Code generated - DO NOT EDIT.
|
||||||
|
// This file is a generated binding and any manual changes will be lost.
|
||||||
|
|
||||||
|
package contract |
||||||
|
|
||||||
|
import ( |
||||||
|
"strings" |
||||||
|
|
||||||
|
"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" |
||||||
|
) |
||||||
|
|
||||||
|
// 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\"}]" |
||||||
|
|
||||||
|
// FIFSRegistrarBin is the compiled bytecode used for deploying new contracts.
|
||||||
|
const FIFSRegistrarBin = `0x6060604052341561000f57600080fd5b604051604080610224833981016040528080519190602001805160008054600160a060020a03909516600160a060020a03199095169490941790935550506001556101c58061005f6000396000f3006060604052600436106100275763ffffffff60e060020a600035041663d22057a9811461002c575b600080fd5b341561003757600080fd5b61004e600435600160a060020a0360243516610050565b005b816000806001548360405191825260208201526040908101905190819003902060008054919350600160a060020a03909116906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b15156100c857600080fd5b6102c65a03f115156100d957600080fd5b5050506040518051915050600160a060020a0381161580159061010e575033600160a060020a031681600160a060020a031614155b1561011857600080fd5b600054600154600160a060020a03909116906306ab592390878760405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b151561017e57600080fd5b6102c65a03f1151561018f57600080fd5b50505050505050505600a165627a7a723058206fb963cb168d5e3a51af12cd6bb23e324dbd32dd4954f43653ba27e66b68ea650029` |
||||||
|
|
||||||
|
// 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) { |
||||||
|
parsed, err := abi.JSON(strings.NewReader(FIFSRegistrarABI)) |
||||||
|
if err != nil { |
||||||
|
return common.Address{}, nil, nil, err |
||||||
|
} |
||||||
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(FIFSRegistrarBin), backend, ensAddr, node) |
||||||
|
if err != nil { |
||||||
|
return common.Address{}, nil, nil, err |
||||||
|
} |
||||||
|
return address, tx, &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}, FIFSRegistrarFilterer: FIFSRegistrarFilterer{contract: contract}}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// FIFSRegistrar is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type FIFSRegistrar struct { |
||||||
|
FIFSRegistrarCaller // Read-only binding to the contract
|
||||||
|
FIFSRegistrarTransactor // Write-only binding to the contract
|
||||||
|
FIFSRegistrarFilterer // Log filterer for contract events
|
||||||
|
} |
||||||
|
|
||||||
|
// FIFSRegistrarCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type FIFSRegistrarCaller struct { |
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
} |
||||||
|
|
||||||
|
// FIFSRegistrarTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type FIFSRegistrarTransactor struct { |
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
} |
||||||
|
|
||||||
|
// FIFSRegistrarFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
||||||
|
type FIFSRegistrarFilterer struct { |
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
} |
||||||
|
|
||||||
|
// FIFSRegistrarSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type FIFSRegistrarSession struct { |
||||||
|
Contract *FIFSRegistrar // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// FIFSRegistrarCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type FIFSRegistrarCallerSession struct { |
||||||
|
Contract *FIFSRegistrarCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// FIFSRegistrarTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type FIFSRegistrarTransactorSession struct { |
||||||
|
Contract *FIFSRegistrarTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// FIFSRegistrarRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type FIFSRegistrarRaw struct { |
||||||
|
Contract *FIFSRegistrar // Generic contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// FIFSRegistrarCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type FIFSRegistrarCallerRaw struct { |
||||||
|
Contract *FIFSRegistrarCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// FIFSRegistrarTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type FIFSRegistrarTransactorRaw struct { |
||||||
|
Contract *FIFSRegistrarTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// NewFIFSRegistrar creates a new instance of FIFSRegistrar, bound to a specific deployed contract.
|
||||||
|
func NewFIFSRegistrar(address common.Address, backend bind.ContractBackend) (*FIFSRegistrar, error) { |
||||||
|
contract, err := bindFIFSRegistrar(address, backend, backend, backend) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}, FIFSRegistrarFilterer: FIFSRegistrarFilterer{contract: contract}}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// NewFIFSRegistrarCaller creates a new read-only instance of FIFSRegistrar, bound to a specific deployed contract.
|
||||||
|
func NewFIFSRegistrarCaller(address common.Address, caller bind.ContractCaller) (*FIFSRegistrarCaller, error) { |
||||||
|
contract, err := bindFIFSRegistrar(address, caller, nil, nil) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &FIFSRegistrarCaller{contract: contract}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// NewFIFSRegistrarTransactor creates a new write-only instance of FIFSRegistrar, bound to a specific deployed contract.
|
||||||
|
func NewFIFSRegistrarTransactor(address common.Address, transactor bind.ContractTransactor) (*FIFSRegistrarTransactor, error) { |
||||||
|
contract, err := bindFIFSRegistrar(address, nil, transactor, nil) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &FIFSRegistrarTransactor{contract: contract}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// NewFIFSRegistrarFilterer creates a new log filterer instance of FIFSRegistrar, bound to a specific deployed contract.
|
||||||
|
func NewFIFSRegistrarFilterer(address common.Address, filterer bind.ContractFilterer) (*FIFSRegistrarFilterer, error) { |
||||||
|
contract, err := bindFIFSRegistrar(address, nil, nil, filterer) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &FIFSRegistrarFilterer{contract: contract}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// bindFIFSRegistrar binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindFIFSRegistrar(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { |
||||||
|
parsed, err := abi.JSON(strings.NewReader(FIFSRegistrarABI)) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil |
||||||
|
} |
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_FIFSRegistrar *FIFSRegistrarRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { |
||||||
|
return _FIFSRegistrar.Contract.FIFSRegistrarCaller.contract.Call(opts, result, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_FIFSRegistrar *FIFSRegistrarRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||||
|
return _FIFSRegistrar.Contract.FIFSRegistrarTransactor.contract.Transfer(opts) |
||||||
|
} |
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_FIFSRegistrar *FIFSRegistrarRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { |
||||||
|
return _FIFSRegistrar.Contract.FIFSRegistrarTransactor.contract.Transact(opts, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_FIFSRegistrar *FIFSRegistrarCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { |
||||||
|
return _FIFSRegistrar.Contract.contract.Call(opts, result, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_FIFSRegistrar *FIFSRegistrarTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||||
|
return _FIFSRegistrar.Contract.contract.Transfer(opts) |
||||||
|
} |
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_FIFSRegistrar *FIFSRegistrarTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { |
||||||
|
return _FIFSRegistrar.Contract.contract.Transact(opts, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// 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) |
||||||
|
} |
||||||
|
|
||||||
|
// 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) |
||||||
|
} |
||||||
|
|
||||||
|
// 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) |
||||||
|
} |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,249 +0,0 @@ |
|||||||
// 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/>. |
|
||||||
|
|
||||||
// ReleaseOracle is an Ethereum contract to store the current and previous |
|
||||||
// versions of the go-ethereum implementation. Its goal is to allow Geth to |
|
||||||
// check for new releases automatically without the need to consult a central |
|
||||||
// repository. |
|
||||||
// |
|
||||||
// The contract takes a vote based approach on both assigning authorised signers |
|
||||||
// as well as signing off on new Geth releases. |
|
||||||
// |
|
||||||
// Note, when a signer is demoted, the currently pending release is auto-nuked. |
|
||||||
// The reason is to prevent suprises where a demotion actually tilts the votes |
|
||||||
// in favor of one voter party and pushing out a new release as a consequence of |
|
||||||
// a simple demotion. |
|
||||||
contract ReleaseOracle { |
|
||||||
// Votes is an internal data structure to count votes on a specific proposal |
|
||||||
struct Votes { |
|
||||||
address[] pass; // List of signers voting to pass a proposal |
|
||||||
address[] fail; // List of signers voting to fail a proposal |
|
||||||
} |
|
||||||
|
|
||||||
// Version is the version details of a particular Geth release |
|
||||||
struct Version { |
|
||||||
uint32 major; // Major version component of the release |
|
||||||
uint32 minor; // Minor version component of the release |
|
||||||
uint32 patch; // Patch version component of the release |
|
||||||
bytes20 commit; // Git SHA1 commit hash of the release |
|
||||||
|
|
||||||
uint64 time; // Timestamp of the release approval |
|
||||||
Votes votes; // Votes that passed this release |
|
||||||
} |
|
||||||
|
|
||||||
// Oracle authorization details |
|
||||||
mapping(address => bool) authorised; // Set of accounts allowed to vote on updating the contract |
|
||||||
address[] voters; // List of addresses currently accepted as signers |
|
||||||
|
|
||||||
// Various proposals being voted on |
|
||||||
mapping(address => Votes) authProps; // Currently running user authorization proposals |
|
||||||
address[] authPend; // List of addresses being voted on (map indexes) |
|
||||||
|
|
||||||
Version verProp; // Currently proposed release being voted on |
|
||||||
Version[] releases; // All the positively voted releases |
|
||||||
|
|
||||||
// isSigner is a modifier to authorize contract transactions. |
|
||||||
modifier isSigner() { |
|
||||||
if (authorised[msg.sender]) { |
|
||||||
_ |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Constructor to assign the initial set of signers. |
|
||||||
function ReleaseOracle(address[] signers) { |
|
||||||
// If no signers were specified, assign the creator as the sole signer |
|
||||||
if (signers.length == 0) { |
|
||||||
authorised[msg.sender] = true; |
|
||||||
voters.push(msg.sender); |
|
||||||
return; |
|
||||||
} |
|
||||||
// Otherwise assign the individual signers one by one |
|
||||||
for (uint i = 0; i < signers.length; i++) { |
|
||||||
authorised[signers[i]] = true; |
|
||||||
voters.push(signers[i]); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// signers is an accessor method to retrieve all the signers (public accessor |
|
||||||
// generates an indexed one, not a retrieve-all version). |
|
||||||
function signers() constant returns(address[]) { |
|
||||||
return voters; |
|
||||||
} |
|
||||||
|
|
||||||
// authProposals retrieves the list of addresses that authorization proposals |
|
||||||
// are currently being voted on. |
|
||||||
function authProposals() constant returns(address[]) { |
|
||||||
return authPend; |
|
||||||
} |
|
||||||
|
|
||||||
// authVotes retrieves the current authorization votes for a particular user |
|
||||||
// to promote him into the list of signers, or demote him from there. |
|
||||||
function authVotes(address user) constant returns(address[] promote, address[] demote) { |
|
||||||
return (authProps[user].pass, authProps[user].fail); |
|
||||||
} |
|
||||||
|
|
||||||
// currentVersion retrieves the semantic version, commit hash and release time |
|
||||||
// of the currently votec active release. |
|
||||||
function currentVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, uint time) { |
|
||||||
if (releases.length == 0) { |
|
||||||
return (0, 0, 0, 0, 0); |
|
||||||
} |
|
||||||
var release = releases[releases.length - 1]; |
|
||||||
|
|
||||||
return (release.major, release.minor, release.patch, release.commit, release.time); |
|
||||||
} |
|
||||||
|
|
||||||
// proposedVersion retrieves the semantic version, commit hash and the current |
|
||||||
// votes for the next proposed release. |
|
||||||
function proposedVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, address[] pass, address[] fail) { |
|
||||||
return (verProp.major, verProp.minor, verProp.patch, verProp.commit, verProp.votes.pass, verProp.votes.fail); |
|
||||||
} |
|
||||||
|
|
||||||
// promote pitches in on a voting campaign to promote a new user to a signer |
|
||||||
// position. |
|
||||||
function promote(address user) { |
|
||||||
updateSigner(user, true); |
|
||||||
} |
|
||||||
|
|
||||||
// demote pitches in on a voting campaign to demote an authorised user from |
|
||||||
// its signer position. |
|
||||||
function demote(address user) { |
|
||||||
updateSigner(user, false); |
|
||||||
} |
|
||||||
|
|
||||||
// release votes for a particular version to be included as the next release. |
|
||||||
function release(uint32 major, uint32 minor, uint32 patch, bytes20 commit) { |
|
||||||
updateRelease(major, minor, patch, commit, true); |
|
||||||
} |
|
||||||
|
|
||||||
// nuke votes for the currently proposed version to not be included as the next |
|
||||||
// release. Nuking doesn't require a specific version number for simplicity. |
|
||||||
function nuke() { |
|
||||||
updateRelease(0, 0, 0, 0, false); |
|
||||||
} |
|
||||||
|
|
||||||
// updateSigner marks a vote for changing the status of an Ethereum user, either |
|
||||||
// for or against the user being an authorised signer. |
|
||||||
function updateSigner(address user, bool authorize) internal isSigner { |
|
||||||
// Gather the current votes and ensure we don't double vote |
|
||||||
Votes votes = authProps[user]; |
|
||||||
for (uint i = 0; i < votes.pass.length; i++) { |
|
||||||
if (votes.pass[i] == msg.sender) { |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
for (i = 0; i < votes.fail.length; i++) { |
|
||||||
if (votes.fail[i] == msg.sender) { |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
// If no authorization proposal is open, add the user to the index for later lookups |
|
||||||
if (votes.pass.length == 0 && votes.fail.length == 0) { |
|
||||||
authPend.push(user); |
|
||||||
} |
|
||||||
// Cast the vote and return if the proposal cannot be resolved yet |
|
||||||
if (authorize) { |
|
||||||
votes.pass.push(msg.sender); |
|
||||||
if (votes.pass.length <= voters.length / 2) { |
|
||||||
return; |
|
||||||
} |
|
||||||
} else { |
|
||||||
votes.fail.push(msg.sender); |
|
||||||
if (votes.fail.length <= voters.length / 2) { |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
// Proposal resolved in our favor, execute whatever we voted on |
|
||||||
if (authorize && !authorised[user]) { |
|
||||||
authorised[user] = true; |
|
||||||
voters.push(user); |
|
||||||
} else if (!authorize && authorised[user]) { |
|
||||||
authorised[user] = false; |
|
||||||
|
|
||||||
for (i = 0; i < voters.length; i++) { |
|
||||||
if (voters[i] == user) { |
|
||||||
voters[i] = voters[voters.length - 1]; |
|
||||||
voters.length--; |
|
||||||
|
|
||||||
delete verProp; // Nuke any version proposal (no surprise releases!) |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
// Finally delete the resolved proposal, index and garbage collect |
|
||||||
delete authProps[user]; |
|
||||||
|
|
||||||
for (i = 0; i < authPend.length; i++) { |
|
||||||
if (authPend[i] == user) { |
|
||||||
authPend[i] = authPend[authPend.length - 1]; |
|
||||||
authPend.length--; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// updateRelease votes for a particular version to be included as the next release, |
|
||||||
// or for the currently proposed release to be nuked out. |
|
||||||
function updateRelease(uint32 major, uint32 minor, uint32 patch, bytes20 commit, bool release) internal isSigner { |
|
||||||
// Skip nuke votes if no proposal is pending |
|
||||||
if (!release && verProp.votes.pass.length == 0) { |
|
||||||
return; |
|
||||||
} |
|
||||||
// Mark a new release if no proposal is pending |
|
||||||
if (verProp.votes.pass.length == 0) { |
|
||||||
verProp.major = major; |
|
||||||
verProp.minor = minor; |
|
||||||
verProp.patch = patch; |
|
||||||
verProp.commit = commit; |
|
||||||
} |
|
||||||
// Make sure positive votes match the current proposal |
|
||||||
if (release && (verProp.major != major || verProp.minor != minor || verProp.patch != patch || verProp.commit != commit)) { |
|
||||||
return; |
|
||||||
} |
|
||||||
// Gather the current votes and ensure we don't double vote |
|
||||||
Votes votes = verProp.votes; |
|
||||||
for (uint i = 0; i < votes.pass.length; i++) { |
|
||||||
if (votes.pass[i] == msg.sender) { |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
for (i = 0; i < votes.fail.length; i++) { |
|
||||||
if (votes.fail[i] == msg.sender) { |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
// Cast the vote and return if the proposal cannot be resolved yet |
|
||||||
if (release) { |
|
||||||
votes.pass.push(msg.sender); |
|
||||||
if (votes.pass.length <= voters.length / 2) { |
|
||||||
return; |
|
||||||
} |
|
||||||
} else { |
|
||||||
votes.fail.push(msg.sender); |
|
||||||
if (votes.fail.length <= voters.length / 2) { |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
// Proposal resolved in our favor, execute whatever we voted on |
|
||||||
if (release) { |
|
||||||
verProp.time = uint64(now); |
|
||||||
releases.push(verProp); |
|
||||||
delete verProp; |
|
||||||
} else { |
|
||||||
delete verProp; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue