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