From 945798f913d5cabd79635f45045b680b02396bf9 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Wed, 31 Dec 2014 15:39:33 +0100 Subject: [PATCH 01/15] Add new key_store interface and two new key stores * Add new generic key_store interface * Add new plaintext key store storing unprotected keys on disk * Add new encrypted key store storing encrypted keys on disk * Add new entropy mixing function using OS and go runtime sources --- .gitignore | 5 + crypto/key.go | 205 ++++++++++++++++++++ crypto/key_store_passphrase_encryped.go | 244 ++++++++++++++++++++++++ crypto/key_store_plaintext_file.go | 113 +++++++++++ crypto/key_store_test.go | 118 ++++++++++++ 5 files changed, 685 insertions(+) create mode 100644 crypto/key.go create mode 100644 crypto/key_store_passphrase_encryped.go create mode 100644 crypto/key_store_plaintext_file.go create mode 100644 crypto/key_store_test.go diff --git a/.gitignore b/.gitignore index fea7df6c2d..f84fe60409 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,8 @@ .DS_Store */**/.DS_Store .ethtest + +#* +.#* +*# +*~ diff --git a/crypto/key.go b/crypto/key.go new file mode 100644 index 0000000000..d4845ee226 --- /dev/null +++ b/crypto/key.go @@ -0,0 +1,205 @@ +/* + 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 Lesser 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 Lesser General Public License + along with go-ethereum. If not, see . +*/ +/** + * @authors + * Gustav Simonsson + * @date 2015 + * + */ + +package crypto + +import ( + "bytes" + "code.google.com/p/go-uuid/uuid" + "crypto/ecdsa" + "crypto/elliptic" + crand "crypto/rand" + "encoding/binary" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "runtime" + "strings" + "time" +) + +type Key struct { + Id *uuid.UUID // Version 4 "random" for unique id not derived from key data + Flags [4]byte // RFU + // we only store privkey as pubkey/address can be derived from it + // privkey in this struct is always in plaintext + PrivateKey *ecdsa.PrivateKey +} + +type KeyPlainJSON struct { + Id string + Flags string + PrivateKey string +} + +type CipherJSON struct { + Salt string + IV string + CipherText string +} + +type KeyProtectedJSON struct { + Id string + Flags string + Crypto CipherJSON +} + +func (k *Key) Address() []byte { + pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey) + return Sha3(pubBytes)[12:] +} + +func (k *Key) MarshalJSON() (j []byte, err error) { + stringStruct := KeyPlainJSON{ + k.Id.String(), + hex.EncodeToString(k.Flags[:]), + hex.EncodeToString(FromECDSA(k.PrivateKey)), + } + j, _ = json.Marshal(stringStruct) + return +} + +func (k *Key) UnmarshalJSON(j []byte) (err error) { + keyJSON := new(KeyPlainJSON) + err = json.Unmarshal(j, &keyJSON) + if err != nil { + return + } + + u := new(uuid.UUID) + *u = uuid.Parse(keyJSON.Id) + if *u == nil { + err = errors.New("UUID parsing failed") + return + } + k.Id = u + + flagsBytes, err := hex.DecodeString(keyJSON.Flags) + if err != nil { + return + } + + PrivateKeyBytes, err := hex.DecodeString(keyJSON.PrivateKey) + if err != nil { + return + } + + copy(k.Flags[:], flagsBytes[0:4]) + k.PrivateKey = ToECDSA(PrivateKeyBytes) + + return +} + +func NewKey() *Key { + randBytes := GetEntropyCSPRNG(32) + reader := bytes.NewReader(randBytes) + _, x, y, err := elliptic.GenerateKey(S256(), reader) + if err != nil { + panic("key generation: elliptic.GenerateKey failed: " + err.Error()) + } + privateKeyMarshalled := elliptic.Marshal(S256(), x, y) + privateKeyECDSA := ToECDSA(privateKeyMarshalled) + + key := new(Key) + id := uuid.NewRandom() + key.Id = &id + // flags := new([4]byte) + // key.Flags = flags + key.PrivateKey = privateKeyECDSA + return key +} + +// plain crypto/rand. this is /dev/urandom on Unix-like systems. +func GetEntropyCSPRNG(n int) []byte { + mainBuff := make([]byte, n) + _, err := io.ReadFull(crand.Reader, mainBuff) + if err != nil { + panic("key generation: reading from crypto/rand failed: " + err.Error()) + } + return mainBuff +} + +// TODO: verify. Do not use until properly discussed. +// we start with crypt/rand, then mix in additional sources of entropy. +// These sources are from three types: OS, go runtime and ethereum client state. +func GetEntropyTinFoilHat() []byte { + startTime := time.Now().UnixNano() + // for each source, we XOR in it's SHA3 hash. + mainBuff := GetEntropyCSPRNG(32) + // 1. OS entropy sources + startTimeBytes := make([]byte, 32) + binary.PutVarint(startTimeBytes, startTime) + startTimeHash := Sha3(startTimeBytes) + mix32Byte(mainBuff, startTimeHash) + + pid := os.Getpid() + pidBytes := make([]byte, 32) + binary.PutUvarint(pidBytes, uint64(pid)) + pidHash := Sha3(pidBytes) + mix32Byte(mainBuff, pidHash) + + osEnv := os.Environ() + osEnvBytes := []byte(strings.Join(osEnv, "")) + osEnvHash := Sha3(osEnvBytes) + mix32Byte(mainBuff, osEnvHash) + + // not all OS have hostname in env variables + osHostName, err := os.Hostname() + if err != nil { + osHostNameBytes := []byte(osHostName) + osHostNameHash := Sha3(osHostNameBytes) + mix32Byte(mainBuff, osHostNameHash) + } + + // 2. go runtime entropy sources + memStats := new(runtime.MemStats) + runtime.ReadMemStats(memStats) + memStatsBytes := []byte(fmt.Sprintf("%v", memStats)) + memStatsHash := Sha3(memStatsBytes) + mix32Byte(mainBuff, memStatsHash) + + // 3. Mix in ethereum / client state + // TODO: list of network peers structs (IP, port, etc) + // TODO: merkle patricia tree root hash for world state and tx list + + // 4. Yo dawg we heard you like entropy so we'll grab some entropy from how + // long it took to grab the above entropy. And a yield, for good measure. + runtime.Gosched() + diffTime := time.Now().UnixNano() - startTime + diffTimeBytes := make([]byte, 32) + binary.PutVarint(diffTimeBytes, diffTime) + diffTimeHash := Sha3(diffTimeBytes) + mix32Byte(mainBuff, diffTimeHash) + + return mainBuff +} + +func mix32Byte(buff []byte, mixBuff []byte) []byte { + for i := 0; i < 32; i++ { + buff[i] ^= mixBuff[i] + } + return buff +} diff --git a/crypto/key_store_passphrase_encryped.go b/crypto/key_store_passphrase_encryped.go new file mode 100644 index 0000000000..b17536c3f4 --- /dev/null +++ b/crypto/key_store_passphrase_encryped.go @@ -0,0 +1,244 @@ +/* + 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 Lesser 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 Lesser General Public License + along with go-ethereum. If not, see . +*/ +/** + * @authors + * Gustav Simonsson + * @date 2015 + * + */ +/* + +This key store behaves as KeyStorePlaintextFile with the difference that +the private key is encrypted and encoded as a JSON object within the +key JSON object. + +Cryptography: + +1. Encryption key is scrypt derived key from user passphrase. Scrypt parameters + (work factors) [1][2] are defined as constants below. +2. Scrypt salt is 32 random bytes from CSPRNG. It is appended to ciphertext. +3. Checksum is SHA3 of the private key bytes. +4. Plaintext is concatenation of private key bytes and checksum. +5. Encryption algo is AES 256 CBC [3][4] +6. CBC IV is 16 random bytes from CSPRNG. It is appended to ciphertext. +7. Plaintext padding is PKCS #7 [5][6] + +Encoding: + +1. On disk, ciphertext, salt and IV are encoded as a JSON object. + cat a key file to see the structure. +2. byte arrays are ASCII HEX encoded as JSON strings. +3. The EC private key bytes are in uncompressed form [7]. + They are a big-endian byte slice of the absolute value of D [8][9]. +4. The checksum is the last 32 bytes of the plaintext byte array and the + private key is the preceeding bytes. + +References: + +1. http://www.tarsnap.com/scrypt/scrypt-slides.pdf +2. http://stackoverflow.com/questions/11126315/what-are-optimal-scrypt-work-factors +3. http://en.wikipedia.org/wiki/Advanced_Encryption_Standard +4. http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 +5. https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes +6. http://tools.ietf.org/html/rfc2315 +7. http://bitcoin.stackexchange.com/questions/3059/what-is-a-compressed-bitcoin-key +8. http://golang.org/pkg/crypto/ecdsa/#PrivateKey +9. https://golang.org/pkg/math/big/#Int.Bytes + +*/ + +package crypto + +import ( + "bytes" + "code.google.com/p/go-uuid/uuid" + "code.google.com/p/go.crypto/scrypt" + "crypto/aes" + "crypto/cipher" + "encoding/hex" + "encoding/json" + "errors" + "os" + "path" +) + +const scryptN int = 262144 // 2^18 +const scryptr int = 8 +const scryptp int = 1 +const scryptdkLen int = 32 + +type KeyStorePassphrase struct { + keysDirPath string +} + +func (ks KeyStorePassphrase) GenerateNewKey(auth string) (key *Key, err error) { + key, err = GenerateNewKeyDefault(ks, auth) + return +} + +func (ks KeyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { + keyBytes, flags, err := DecryptKey(ks, keyId, auth) + key = new(Key) + key.Id = keyId + copy(key.Flags[:], flags[0:4]) + key.PrivateKey = ToECDSA(keyBytes) + return +} + +func (ks KeyStorePassphrase) StoreKey(key *Key, auth string) (err error) { + authArray := []byte(auth) + salt := GetEntropyCSPRNG(32) + derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) + if err != nil { + return + } + + keyBytes := FromECDSA(key.PrivateKey) + keyBytesHash := Sha3(keyBytes) + toEncrypt := PKCS7Pad(append(keyBytes, keyBytesHash...)) + + AES256Block, err := aes.NewCipher(derivedKey) + if err != nil { + return + } + + iv := GetEntropyCSPRNG(aes.BlockSize) // 16 + AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv) + cipherText := make([]byte, len(toEncrypt)) + AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt) + + cipherStruct := CipherJSON{ + hex.EncodeToString(salt), + hex.EncodeToString(iv), + hex.EncodeToString(cipherText), + } + keyStruct := KeyProtectedJSON{ + key.Id.String(), + hex.EncodeToString(key.Flags[:]), + cipherStruct, + } + keyJSON, err := json.Marshal(keyStruct) + if err != nil { + return + } + + err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) + return +} + +func (ks KeyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error) { + // only delete if correct passphrase is given + _, _, err = DecryptKey(ks, keyId, auth) + if err != nil { + return + } + + keyDirPath := path.Join(ks.keysDirPath, keyId.String()) + err = os.RemoveAll(keyDirPath) + return +} + +func DecryptKey(ks KeyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, flags []byte, err error) { + fileContent, err := GetKeyFile(ks.keysDirPath, keyId) + if err != nil { + return + } + + keyProtected := new(KeyProtectedJSON) + err = json.Unmarshal(fileContent, keyProtected) + + flags, err = hex.DecodeString(keyProtected.Flags) + if err != nil { + return + } + + salt, err := hex.DecodeString(keyProtected.Crypto.Salt) + if err != nil { + return + } + + iv, err := hex.DecodeString(keyProtected.Crypto.IV) + if err != nil { + return + } + + cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) + if err != nil { + return + } + + authArray := []byte(auth) + derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) + if err != nil { + return + } + + AES256Block, err := aes.NewCipher(derivedKey) + if err != nil { + return + } + + AES256CBCDecrypter := cipher.NewCBCDecrypter(AES256Block, iv) + paddedPlainText := make([]byte, len(cipherText)) + AES256CBCDecrypter.CryptBlocks(paddedPlainText, cipherText) + + plainText := PKCS7Unpad(paddedPlainText) + if plainText == nil { + err = errors.New("Decryption failed: PKCS7Unpad failed after decryption") + return + } + + keyBytes = plainText[:len(plainText)-32] + keyBytesHash := plainText[len(plainText)-32:] + if !bytes.Equal(Sha3(keyBytes), keyBytesHash) { + err = errors.New("Decryption failed: checksum mismatch") + return + } + return keyBytes, flags, err +} + +// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes +func PKCS7Pad(in []byte) []byte { + padding := 16 - (len(in) % 16) + if padding == 0 { + padding = 16 + } + for i := 0; i < padding; i++ { + in = append(in, byte(padding)) + } + return in +} + +func PKCS7Unpad(in []byte) []byte { + if len(in) == 0 { + return nil + } + + padding := in[len(in)-1] + if int(padding) > len(in) || padding > aes.BlockSize { + return nil + } else if padding == 0 { + return nil + } + + for i := len(in) - 1; i > len(in)-int(padding)-1; i-- { + if in[i] != padding { + return nil + } + } + return in[:len(in)-int(padding)] +} diff --git a/crypto/key_store_plaintext_file.go b/crypto/key_store_plaintext_file.go new file mode 100644 index 0000000000..7ca5e56791 --- /dev/null +++ b/crypto/key_store_plaintext_file.go @@ -0,0 +1,113 @@ +/* + 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 Lesser 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 Lesser General Public License + along with go-ethereum. If not, see . +*/ +/** + * @authors + * Gustav Simonsson + * @date 2015 + * + */ + +package crypto + +import ( + "code.google.com/p/go-uuid/uuid" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "os/user" + "path" +) + +// TODO: rename to KeyStore when replacing existing KeyStore +type KeyStore2 interface { + GenerateNewKey(string) (*Key, error) // create and store new key, optionally using auth string + GetKey(*uuid.UUID, string) (*Key, error) // key from id and auth string + StoreKey(*Key, string) error // store key optionally using auth string + DeleteKey(*uuid.UUID, string) error // delete key by id and auth string +} + +type KeyStorePlaintext struct { + keysDirPath string +} + +// TODO: copied from cmd/ethereum/flags.go +func DefaultDataDir() string { + usr, _ := user.Current() + return path.Join(usr.HomeDir, ".ethereum") +} + +func (ks KeyStorePlaintext) GenerateNewKey(auth string) (key *Key, err error) { + key, err = GenerateNewKeyDefault(ks, auth) + return +} + +func GenerateNewKeyDefault(ks KeyStore2, auth string) (key *Key, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("GenerateNewKey error: %v", r) + } + }() + key = NewKey() + err = ks.StoreKey(key, auth) + return +} + +func (ks KeyStorePlaintext) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { + fileContent, err := GetKeyFile(ks.keysDirPath, keyId) + if err != nil { + return + } + + key = new(Key) + err = json.Unmarshal(fileContent, key) + return +} + +func (ks KeyStorePlaintext) StoreKey(key *Key, auth string) (err error) { + keyJSON, err := json.Marshal(key) + if err != nil { + return + } + err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) + return +} + +func (ks KeyStorePlaintext) DeleteKey(keyId *uuid.UUID, auth string) (err error) { + keyDirPath := path.Join(ks.keysDirPath, keyId.String()) + err = os.RemoveAll(keyDirPath) + return +} + +func GetKeyFile(keysDirPath string, keyId *uuid.UUID) (fileContent []byte, err error) { + idString := keyId.String() + keyDirPath := path.Join(keysDirPath, idString) + keyFilePath := path.Join(keyDirPath, idString) + fileContent, err = ioutil.ReadFile(keyFilePath) + return +} + +func WriteKeyFile(idString string, keysDirPath string, content []byte) (err error) { + keyDirPath := path.Join(keysDirPath, idString) + keyFilePath := path.Join(keyDirPath, idString) + err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user + if err != nil { + return + } + err = ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user + return +} diff --git a/crypto/key_store_test.go b/crypto/key_store_test.go new file mode 100644 index 0000000000..4127354447 --- /dev/null +++ b/crypto/key_store_test.go @@ -0,0 +1,118 @@ +package crypto + +import ( + "fmt" + "reflect" + "testing" +) + +func TestKeyStorePlaintext(t *testing.T) { + ks := new(KeyStorePlaintext) + ks.keysDirPath = DefaultDataDir() + pass := "" // not used but required by API + k1, err := ks.GenerateNewKey(pass) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + k2 := new(Key) + k2, err = ks.GetKey(k1.Id, pass) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !reflect.DeepEqual(k1.Id, k2.Id) { + fmt.Println("key Id mismatch") + t.FailNow() + } + + if k1.Flags != k2.Flags { + fmt.Println("key Flags mismatch") + t.FailNow() + } + + if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { + fmt.Println("key PrivateKey mismatch") + t.FailNow() + } + + err = ks.DeleteKey(k2.Id, pass) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } +} + +func TestKeyStorePassphrase(t *testing.T) { + ks := new(KeyStorePassphrase) + ks.keysDirPath = DefaultDataDir() + pass := "foo" + k1, err := ks.GenerateNewKey(pass) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + k2 := new(Key) + k2, err = ks.GetKey(k1.Id, pass) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !reflect.DeepEqual(k1.Id, k2.Id) { + fmt.Println("key Id mismatch") + t.FailNow() + } + + if k1.Flags != k2.Flags { + fmt.Println("key Flags mismatch") + t.FailNow() + } + + if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { + fmt.Println("key PrivateKey mismatch") + t.FailNow() + } + + err = ks.DeleteKey(k2.Id, pass) // also to clean up created files + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } +} + +func TestKeyStorePassphraseDecryptionFail(t *testing.T) { + ks := new(KeyStorePassphrase) + ks.keysDirPath = DefaultDataDir() + pass := "foo" + k1, err := ks.GenerateNewKey(pass) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + _, err = ks.GetKey(k1.Id, "bar") // wrong passphrase + // fmt.Println(err.Error()) + if err == nil { + t.FailNow() + } + + err = ks.DeleteKey(k1.Id, "bar") // wrong passphrase + if err == nil { + fmt.Println(err.Error()) + t.FailNow() + } + + err = ks.DeleteKey(k1.Id, pass) // to clean up + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } +} + +func TestKeyMixedEntropy(t *testing.T) { + GetEntropyTinFoilHat() +} From a1c2749380523178f87ae3fdfb02bc6641362924 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Wed, 7 Jan 2015 16:06:26 +0100 Subject: [PATCH 02/15] Address pull request comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Simplify scrypt constants with const block * Add key store constructors and make their types private * Simplify key store and file namings to be less Java Enterpriseā„¢ * Change test error logging to use t.Error(err) * Reduce number of naked returns (just like my ex-gf) * Simplify file reading path code --- crypto/key.go | 22 +++--- ...se_encryped.go => key_store_passphrase.go} | 73 +++++++++++-------- ...e_plaintext_file.go => key_store_plain.go} | 49 +++++++------ crypto/key_store_test.go | 31 ++++---- 4 files changed, 91 insertions(+), 84 deletions(-) rename crypto/{key_store_passphrase_encryped.go => key_store_passphrase.go} (83%) rename crypto/{key_store_plaintext_file.go => key_store_plain.go} (71%) diff --git a/crypto/key.go b/crypto/key.go index d4845ee226..1a8b770e00 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -49,7 +49,7 @@ type Key struct { PrivateKey *ecdsa.PrivateKey } -type KeyPlainJSON struct { +type PlainKeyJSON struct { Id string Flags string PrivateKey string @@ -61,7 +61,7 @@ type CipherJSON struct { CipherText string } -type KeyProtectedJSON struct { +type EncryptedKeyJSON struct { Id string Flags string Crypto CipherJSON @@ -73,44 +73,44 @@ func (k *Key) Address() []byte { } func (k *Key) MarshalJSON() (j []byte, err error) { - stringStruct := KeyPlainJSON{ + stringStruct := PlainKeyJSON{ k.Id.String(), hex.EncodeToString(k.Flags[:]), hex.EncodeToString(FromECDSA(k.PrivateKey)), } - j, _ = json.Marshal(stringStruct) - return + j, err = json.Marshal(stringStruct) + return j, err } func (k *Key) UnmarshalJSON(j []byte) (err error) { - keyJSON := new(KeyPlainJSON) + keyJSON := new(PlainKeyJSON) err = json.Unmarshal(j, &keyJSON) if err != nil { - return + return err } u := new(uuid.UUID) *u = uuid.Parse(keyJSON.Id) if *u == nil { err = errors.New("UUID parsing failed") - return + return err } k.Id = u flagsBytes, err := hex.DecodeString(keyJSON.Flags) if err != nil { - return + return err } PrivateKeyBytes, err := hex.DecodeString(keyJSON.PrivateKey) if err != nil { - return + return err } copy(k.Flags[:], flagsBytes[0:4]) k.PrivateKey = ToECDSA(PrivateKeyBytes) - return + return err } func NewKey() *Key { diff --git a/crypto/key_store_passphrase_encryped.go b/crypto/key_store_passphrase.go similarity index 83% rename from crypto/key_store_passphrase_encryped.go rename to crypto/key_store_passphrase.go index b17536c3f4..eaf73422f6 100644 --- a/crypto/key_store_passphrase_encryped.go +++ b/crypto/key_store_passphrase.go @@ -76,35 +76,46 @@ import ( "path" ) -const scryptN int = 262144 // 2^18 -const scryptr int = 8 -const scryptp int = 1 -const scryptdkLen int = 32 +const ( + // 2^18 / 8 / 1 uses 256MB memory and approx 1s CPU time on a modern CPU. + scryptN = 1 << 18 + scryptr = 8 + scryptp = 1 + scryptdkLen = 32 +) -type KeyStorePassphrase struct { +type keyStorePassphrase struct { keysDirPath string } -func (ks KeyStorePassphrase) GenerateNewKey(auth string) (key *Key, err error) { - key, err = GenerateNewKeyDefault(ks, auth) - return +func NewKeyStorePassphrase(path string) KeyStore2 { + ks := new(keyStorePassphrase) + ks.keysDirPath = path + return ks } -func (ks KeyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { +func (ks keyStorePassphrase) GenerateNewKey(auth string) (key *Key, err error) { + return GenerateNewKeyDefault(ks, auth) +} + +func (ks keyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { keyBytes, flags, err := DecryptKey(ks, keyId, auth) + if err != nil { + return nil, err + } key = new(Key) key.Id = keyId copy(key.Flags[:], flags[0:4]) key.PrivateKey = ToECDSA(keyBytes) - return + return key, err } -func (ks KeyStorePassphrase) StoreKey(key *Key, auth string) (err error) { +func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) { authArray := []byte(auth) salt := GetEntropyCSPRNG(32) derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) if err != nil { - return + return err } keyBytes := FromECDSA(key.PrivateKey) @@ -113,7 +124,7 @@ func (ks KeyStorePassphrase) StoreKey(key *Key, auth string) (err error) { AES256Block, err := aes.NewCipher(derivedKey) if err != nil { - return + return err } iv := GetEntropyCSPRNG(aes.BlockSize) // 16 @@ -126,70 +137,68 @@ func (ks KeyStorePassphrase) StoreKey(key *Key, auth string) (err error) { hex.EncodeToString(iv), hex.EncodeToString(cipherText), } - keyStruct := KeyProtectedJSON{ + keyStruct := EncryptedKeyJSON{ key.Id.String(), hex.EncodeToString(key.Flags[:]), cipherStruct, } keyJSON, err := json.Marshal(keyStruct) if err != nil { - return + return err } - err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) - return + return WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) } -func (ks KeyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error) { +func (ks keyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error) { // only delete if correct passphrase is given _, _, err = DecryptKey(ks, keyId, auth) if err != nil { - return + return err } keyDirPath := path.Join(ks.keysDirPath, keyId.String()) - err = os.RemoveAll(keyDirPath) - return + return os.RemoveAll(keyDirPath) } -func DecryptKey(ks KeyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, flags []byte, err error) { +func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, flags []byte, err error) { fileContent, err := GetKeyFile(ks.keysDirPath, keyId) if err != nil { - return + return nil, nil, err } - keyProtected := new(KeyProtectedJSON) + keyProtected := new(EncryptedKeyJSON) err = json.Unmarshal(fileContent, keyProtected) flags, err = hex.DecodeString(keyProtected.Flags) if err != nil { - return + return nil, nil, err } salt, err := hex.DecodeString(keyProtected.Crypto.Salt) if err != nil { - return + return nil, nil, err } iv, err := hex.DecodeString(keyProtected.Crypto.IV) if err != nil { - return + return nil, nil, err } cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) if err != nil { - return + return nil, nil, err } authArray := []byte(auth) derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) if err != nil { - return + return nil, nil, err } AES256Block, err := aes.NewCipher(derivedKey) if err != nil { - return + return nil, nil, err } AES256CBCDecrypter := cipher.NewCBCDecrypter(AES256Block, iv) @@ -199,14 +208,14 @@ func DecryptKey(ks KeyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes plainText := PKCS7Unpad(paddedPlainText) if plainText == nil { err = errors.New("Decryption failed: PKCS7Unpad failed after decryption") - return + return nil, nil, err } keyBytes = plainText[:len(plainText)-32] keyBytesHash := plainText[len(plainText)-32:] if !bytes.Equal(Sha3(keyBytes), keyBytesHash) { err = errors.New("Decryption failed: checksum mismatch") - return + return nil, nil, err } return keyBytes, flags, err } diff --git a/crypto/key_store_plaintext_file.go b/crypto/key_store_plain.go similarity index 71% rename from crypto/key_store_plaintext_file.go rename to crypto/key_store_plain.go index 7ca5e56791..00d9767b68 100644 --- a/crypto/key_store_plaintext_file.go +++ b/crypto/key_store_plain.go @@ -41,7 +41,7 @@ type KeyStore2 interface { DeleteKey(*uuid.UUID, string) error // delete key by id and auth string } -type KeyStorePlaintext struct { +type keyStorePlain struct { keysDirPath string } @@ -51,9 +51,14 @@ func DefaultDataDir() string { return path.Join(usr.HomeDir, ".ethereum") } -func (ks KeyStorePlaintext) GenerateNewKey(auth string) (key *Key, err error) { - key, err = GenerateNewKeyDefault(ks, auth) - return +func NewKeyStorePlain(path string) KeyStore2 { + ks := new(keyStorePlain) + ks.keysDirPath = path + return ks +} + +func (ks keyStorePlain) GenerateNewKey(auth string) (key *Key, err error) { + return GenerateNewKeyDefault(ks, auth) } func GenerateNewKeyDefault(ks KeyStore2, auth string) (key *Key, err error) { @@ -64,50 +69,46 @@ func GenerateNewKeyDefault(ks KeyStore2, auth string) (key *Key, err error) { }() key = NewKey() err = ks.StoreKey(key, auth) - return + return key, err } -func (ks KeyStorePlaintext) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { +func (ks keyStorePlain) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { fileContent, err := GetKeyFile(ks.keysDirPath, keyId) if err != nil { - return + return nil, err } key = new(Key) err = json.Unmarshal(fileContent, key) - return + return key, err } -func (ks KeyStorePlaintext) StoreKey(key *Key, auth string) (err error) { +func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) { keyJSON, err := json.Marshal(key) if err != nil { - return + return err } err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) - return + return err } -func (ks KeyStorePlaintext) DeleteKey(keyId *uuid.UUID, auth string) (err error) { +func (ks keyStorePlain) DeleteKey(keyId *uuid.UUID, auth string) (err error) { keyDirPath := path.Join(ks.keysDirPath, keyId.String()) err = os.RemoveAll(keyDirPath) - return + return err } func GetKeyFile(keysDirPath string, keyId *uuid.UUID) (fileContent []byte, err error) { - idString := keyId.String() - keyDirPath := path.Join(keysDirPath, idString) - keyFilePath := path.Join(keyDirPath, idString) - fileContent, err = ioutil.ReadFile(keyFilePath) - return + id := keyId.String() + return ioutil.ReadFile(path.Join(keysDirPath, id, id)) } -func WriteKeyFile(idString string, keysDirPath string, content []byte) (err error) { - keyDirPath := path.Join(keysDirPath, idString) - keyFilePath := path.Join(keyDirPath, idString) +func WriteKeyFile(id string, keysDirPath string, content []byte) (err error) { + keyDirPath := path.Join(keysDirPath, id) + keyFilePath := path.Join(keyDirPath, id) err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user if err != nil { - return + return err } - err = ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user - return + return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user } diff --git a/crypto/key_store_test.go b/crypto/key_store_test.go index 4127354447..8469d23696 100644 --- a/crypto/key_store_test.go +++ b/crypto/key_store_test.go @@ -6,20 +6,19 @@ import ( "testing" ) -func TestKeyStorePlaintext(t *testing.T) { - ks := new(KeyStorePlaintext) - ks.keysDirPath = DefaultDataDir() +func TestKeyStorePlain(t *testing.T) { + ks := NewKeyStorePlain(DefaultDataDir()) pass := "" // not used but required by API k1, err := ks.GenerateNewKey(pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } k2 := new(Key) k2, err = ks.GetKey(k1.Id, pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } @@ -40,25 +39,24 @@ func TestKeyStorePlaintext(t *testing.T) { err = ks.DeleteKey(k2.Id, pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } } func TestKeyStorePassphrase(t *testing.T) { - ks := new(KeyStorePassphrase) - ks.keysDirPath = DefaultDataDir() + ks := NewKeyStorePassphrase(DefaultDataDir()) pass := "foo" k1, err := ks.GenerateNewKey(pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } k2 := new(Key) k2, err = ks.GetKey(k1.Id, pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } @@ -79,36 +77,35 @@ func TestKeyStorePassphrase(t *testing.T) { err = ks.DeleteKey(k2.Id, pass) // also to clean up created files if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } } func TestKeyStorePassphraseDecryptionFail(t *testing.T) { - ks := new(KeyStorePassphrase) - ks.keysDirPath = DefaultDataDir() + ks := NewKeyStorePassphrase(DefaultDataDir()) pass := "foo" k1, err := ks.GenerateNewKey(pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } _, err = ks.GetKey(k1.Id, "bar") // wrong passphrase - // fmt.Println(err.Error()) + // t.Error(err) if err == nil { t.FailNow() } err = ks.DeleteKey(k1.Id, "bar") // wrong passphrase if err == nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } err = ks.DeleteKey(k1.Id, pass) // to clean up if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } } From 47d3b3dd58172c2e7c1f72fb072bd9385aff8205 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Thu, 15 Jan 2015 17:45:45 +0100 Subject: [PATCH 03/15] Address pull request comments * Remove flags field from key struct * Change JSON struct fields from string to []byte * Change GenerateNewKey API to take io.Reader for random source * Remove mixing entropy source function * Use testing Fatal in tests --- crypto/key.go | 150 ++++++--------------------------- crypto/key_store_passphrase.go | 75 ++++++++--------- crypto/key_store_plain.go | 12 +-- crypto/key_store_test.go | 66 ++++----------- 4 files changed, 88 insertions(+), 215 deletions(-) diff --git a/crypto/key.go b/crypto/key.go index 1a8b770e00..0e7c04275a 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -28,43 +28,31 @@ import ( "code.google.com/p/go-uuid/uuid" "crypto/ecdsa" "crypto/elliptic" - crand "crypto/rand" - "encoding/binary" - "encoding/hex" "encoding/json" - "errors" - "fmt" "io" - "os" - "runtime" - "strings" - "time" ) type Key struct { - Id *uuid.UUID // Version 4 "random" for unique id not derived from key data - Flags [4]byte // RFU + Id *uuid.UUID // Version 4 "random" for unique id not derived from key data // we only store privkey as pubkey/address can be derived from it // privkey in this struct is always in plaintext PrivateKey *ecdsa.PrivateKey } -type PlainKeyJSON struct { - Id string - Flags string - PrivateKey string +type plainKeyJSON struct { + Id []byte + PrivateKey []byte } -type CipherJSON struct { - Salt string - IV string - CipherText string +type cipherJSON struct { + Salt []byte + IV []byte + CipherText []byte } -type EncryptedKeyJSON struct { - Id string - Flags string - Crypto CipherJSON +type encryptedKeyJSON struct { + Id []byte + Crypto cipherJSON } func (k *Key) Address() []byte { @@ -73,48 +61,40 @@ func (k *Key) Address() []byte { } func (k *Key) MarshalJSON() (j []byte, err error) { - stringStruct := PlainKeyJSON{ - k.Id.String(), - hex.EncodeToString(k.Flags[:]), - hex.EncodeToString(FromECDSA(k.PrivateKey)), + jStruct := plainKeyJSON{ + *k.Id, + FromECDSA(k.PrivateKey), } - j, err = json.Marshal(stringStruct) + j, err = json.Marshal(jStruct) return j, err } func (k *Key) UnmarshalJSON(j []byte) (err error) { - keyJSON := new(PlainKeyJSON) + keyJSON := new(plainKeyJSON) err = json.Unmarshal(j, &keyJSON) if err != nil { return err } u := new(uuid.UUID) - *u = uuid.Parse(keyJSON.Id) - if *u == nil { - err = errors.New("UUID parsing failed") - return err - } + *u = keyJSON.Id k.Id = u - flagsBytes, err := hex.DecodeString(keyJSON.Flags) - if err != nil { - return err - } - - PrivateKeyBytes, err := hex.DecodeString(keyJSON.PrivateKey) - if err != nil { - return err - } - - copy(k.Flags[:], flagsBytes[0:4]) - k.PrivateKey = ToECDSA(PrivateKeyBytes) + k.PrivateKey = ToECDSA(keyJSON.PrivateKey) return err } -func NewKey() *Key { - randBytes := GetEntropyCSPRNG(32) +func NewKey(rand io.Reader) *Key { + randBytes := make([]byte, 32) + n, err := rand.Read(randBytes) + if err != nil { + panic("key generation: could not read from random source: " + err.Error()) + } else { + if n != 32 { + panic("key generation: read less than required bytes from random source: " + err.Error()) + } + } reader := bytes.NewReader(randBytes) _, x, y, err := elliptic.GenerateKey(S256(), reader) if err != nil { @@ -126,80 +106,6 @@ func NewKey() *Key { key := new(Key) id := uuid.NewRandom() key.Id = &id - // flags := new([4]byte) - // key.Flags = flags key.PrivateKey = privateKeyECDSA return key } - -// plain crypto/rand. this is /dev/urandom on Unix-like systems. -func GetEntropyCSPRNG(n int) []byte { - mainBuff := make([]byte, n) - _, err := io.ReadFull(crand.Reader, mainBuff) - if err != nil { - panic("key generation: reading from crypto/rand failed: " + err.Error()) - } - return mainBuff -} - -// TODO: verify. Do not use until properly discussed. -// we start with crypt/rand, then mix in additional sources of entropy. -// These sources are from three types: OS, go runtime and ethereum client state. -func GetEntropyTinFoilHat() []byte { - startTime := time.Now().UnixNano() - // for each source, we XOR in it's SHA3 hash. - mainBuff := GetEntropyCSPRNG(32) - // 1. OS entropy sources - startTimeBytes := make([]byte, 32) - binary.PutVarint(startTimeBytes, startTime) - startTimeHash := Sha3(startTimeBytes) - mix32Byte(mainBuff, startTimeHash) - - pid := os.Getpid() - pidBytes := make([]byte, 32) - binary.PutUvarint(pidBytes, uint64(pid)) - pidHash := Sha3(pidBytes) - mix32Byte(mainBuff, pidHash) - - osEnv := os.Environ() - osEnvBytes := []byte(strings.Join(osEnv, "")) - osEnvHash := Sha3(osEnvBytes) - mix32Byte(mainBuff, osEnvHash) - - // not all OS have hostname in env variables - osHostName, err := os.Hostname() - if err != nil { - osHostNameBytes := []byte(osHostName) - osHostNameHash := Sha3(osHostNameBytes) - mix32Byte(mainBuff, osHostNameHash) - } - - // 2. go runtime entropy sources - memStats := new(runtime.MemStats) - runtime.ReadMemStats(memStats) - memStatsBytes := []byte(fmt.Sprintf("%v", memStats)) - memStatsHash := Sha3(memStatsBytes) - mix32Byte(mainBuff, memStatsHash) - - // 3. Mix in ethereum / client state - // TODO: list of network peers structs (IP, port, etc) - // TODO: merkle patricia tree root hash for world state and tx list - - // 4. Yo dawg we heard you like entropy so we'll grab some entropy from how - // long it took to grab the above entropy. And a yield, for good measure. - runtime.Gosched() - diffTime := time.Now().UnixNano() - startTime - diffTimeBytes := make([]byte, 32) - binary.PutVarint(diffTimeBytes, diffTime) - diffTimeHash := Sha3(diffTimeBytes) - mix32Byte(mainBuff, diffTimeHash) - - return mainBuff -} - -func mix32Byte(buff []byte, mixBuff []byte) []byte { - for i := 0; i < 32; i++ { - buff[i] ^= mixBuff[i] - } - return buff -} diff --git a/crypto/key_store_passphrase.go b/crypto/key_store_passphrase.go index eaf73422f6..1e7c50f965 100644 --- a/crypto/key_store_passphrase.go +++ b/crypto/key_store_passphrase.go @@ -69,9 +69,10 @@ import ( "code.google.com/p/go.crypto/scrypt" "crypto/aes" "crypto/cipher" - "encoding/hex" + crand "crypto/rand" "encoding/json" "errors" + "io" "os" "path" ) @@ -94,25 +95,24 @@ func NewKeyStorePassphrase(path string) KeyStore2 { return ks } -func (ks keyStorePassphrase) GenerateNewKey(auth string) (key *Key, err error) { - return GenerateNewKeyDefault(ks, auth) +func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) { + return GenerateNewKeyDefault(ks, rand, auth) } func (ks keyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { - keyBytes, flags, err := DecryptKey(ks, keyId, auth) + keyBytes, err := DecryptKey(ks, keyId, auth) if err != nil { return nil, err } key = new(Key) key.Id = keyId - copy(key.Flags[:], flags[0:4]) key.PrivateKey = ToECDSA(keyBytes) return key, err } func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) { authArray := []byte(auth) - salt := GetEntropyCSPRNG(32) + salt := getEntropyCSPRNG(32) derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) if err != nil { return err @@ -127,19 +127,18 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) { return err } - iv := GetEntropyCSPRNG(aes.BlockSize) // 16 + iv := getEntropyCSPRNG(aes.BlockSize) // 16 AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv) cipherText := make([]byte, len(toEncrypt)) AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt) - cipherStruct := CipherJSON{ - hex.EncodeToString(salt), - hex.EncodeToString(iv), - hex.EncodeToString(cipherText), + cipherStruct := cipherJSON{ + salt, + iv, + cipherText, } - keyStruct := EncryptedKeyJSON{ - key.Id.String(), - hex.EncodeToString(key.Flags[:]), + keyStruct := encryptedKeyJSON{ + *key.Id, cipherStruct, } keyJSON, err := json.Marshal(keyStruct) @@ -152,7 +151,7 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) { func (ks keyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error) { // only delete if correct passphrase is given - _, _, err = DecryptKey(ks, keyId, auth) + _, err = DecryptKey(ks, keyId, auth) if err != nil { return err } @@ -161,44 +160,30 @@ func (ks keyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error return os.RemoveAll(keyDirPath) } -func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, flags []byte, err error) { +func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, err error) { fileContent, err := GetKeyFile(ks.keysDirPath, keyId) if err != nil { - return nil, nil, err + return nil, err } - keyProtected := new(EncryptedKeyJSON) + keyProtected := new(encryptedKeyJSON) err = json.Unmarshal(fileContent, keyProtected) - flags, err = hex.DecodeString(keyProtected.Flags) - if err != nil { - return nil, nil, err - } + salt := keyProtected.Crypto.Salt - salt, err := hex.DecodeString(keyProtected.Crypto.Salt) - if err != nil { - return nil, nil, err - } + iv := keyProtected.Crypto.IV - iv, err := hex.DecodeString(keyProtected.Crypto.IV) - if err != nil { - return nil, nil, err - } - - cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) - if err != nil { - return nil, nil, err - } + cipherText := keyProtected.Crypto.CipherText authArray := []byte(auth) derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) if err != nil { - return nil, nil, err + return nil, err } AES256Block, err := aes.NewCipher(derivedKey) if err != nil { - return nil, nil, err + return nil, err } AES256CBCDecrypter := cipher.NewCBCDecrypter(AES256Block, iv) @@ -208,16 +193,26 @@ func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes plainText := PKCS7Unpad(paddedPlainText) if plainText == nil { err = errors.New("Decryption failed: PKCS7Unpad failed after decryption") - return nil, nil, err + return nil, err } keyBytes = plainText[:len(plainText)-32] keyBytesHash := plainText[len(plainText)-32:] if !bytes.Equal(Sha3(keyBytes), keyBytesHash) { err = errors.New("Decryption failed: checksum mismatch") - return nil, nil, err + return nil, err } - return keyBytes, flags, err + return keyBytes, err +} + +// plain crypto/rand. this is /dev/urandom on Unix-like systems. +func getEntropyCSPRNG(n int) []byte { + mainBuff := make([]byte, n) + _, err := io.ReadFull(crand.Reader, mainBuff) + if err != nil { + panic("key generation: reading from crypto/rand failed: " + err.Error()) + } + return mainBuff } // From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes diff --git a/crypto/key_store_plain.go b/crypto/key_store_plain.go index 00d9767b68..2aa813f5e8 100644 --- a/crypto/key_store_plain.go +++ b/crypto/key_store_plain.go @@ -27,6 +27,7 @@ import ( "code.google.com/p/go-uuid/uuid" "encoding/json" "fmt" + "io" "io/ioutil" "os" "os/user" @@ -35,7 +36,8 @@ import ( // TODO: rename to KeyStore when replacing existing KeyStore type KeyStore2 interface { - GenerateNewKey(string) (*Key, error) // create and store new key, optionally using auth string + // create new key using io.Reader entropy source and optionally using auth string + GenerateNewKey(io.Reader, string) (*Key, error) GetKey(*uuid.UUID, string) (*Key, error) // key from id and auth string StoreKey(*Key, string) error // store key optionally using auth string DeleteKey(*uuid.UUID, string) error // delete key by id and auth string @@ -57,17 +59,17 @@ func NewKeyStorePlain(path string) KeyStore2 { return ks } -func (ks keyStorePlain) GenerateNewKey(auth string) (key *Key, err error) { - return GenerateNewKeyDefault(ks, auth) +func (ks keyStorePlain) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) { + return GenerateNewKeyDefault(ks, rand, auth) } -func GenerateNewKeyDefault(ks KeyStore2, auth string) (key *Key, err error) { +func GenerateNewKeyDefault(ks KeyStore2, rand io.Reader, auth string) (key *Key, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("GenerateNewKey error: %v", r) } }() - key = NewKey() + key = NewKey(rand) err = ks.StoreKey(key, auth) return key, err } diff --git a/crypto/key_store_test.go b/crypto/key_store_test.go index 8469d23696..16c0e476d4 100644 --- a/crypto/key_store_test.go +++ b/crypto/key_store_test.go @@ -1,7 +1,7 @@ package crypto import ( - "fmt" + crand "crypto/rand" "reflect" "testing" ) @@ -9,107 +9,77 @@ import ( func TestKeyStorePlain(t *testing.T) { ks := NewKeyStorePlain(DefaultDataDir()) pass := "" // not used but required by API - k1, err := ks.GenerateNewKey(pass) + k1, err := ks.GenerateNewKey(crand.Reader, pass) if err != nil { - t.Error(err) - t.FailNow() + t.Fatal(err) } k2 := new(Key) k2, err = ks.GetKey(k1.Id, pass) if err != nil { - t.Error(err) - t.FailNow() + t.Fatal(err) } if !reflect.DeepEqual(k1.Id, k2.Id) { - fmt.Println("key Id mismatch") - t.FailNow() - } - - if k1.Flags != k2.Flags { - fmt.Println("key Flags mismatch") - t.FailNow() + t.Fatal(err) } if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { - fmt.Println("key PrivateKey mismatch") - t.FailNow() + t.Fatal(err) } err = ks.DeleteKey(k2.Id, pass) if err != nil { - t.Error(err) - t.FailNow() + t.Fatal(err) } } func TestKeyStorePassphrase(t *testing.T) { ks := NewKeyStorePassphrase(DefaultDataDir()) pass := "foo" - k1, err := ks.GenerateNewKey(pass) + k1, err := ks.GenerateNewKey(crand.Reader, pass) if err != nil { - t.Error(err) - t.FailNow() + t.Fatal(err) } - k2 := new(Key) k2, err = ks.GetKey(k1.Id, pass) if err != nil { - t.Error(err) - t.FailNow() + t.Fatal(err) } - if !reflect.DeepEqual(k1.Id, k2.Id) { - fmt.Println("key Id mismatch") - t.FailNow() - } - - if k1.Flags != k2.Flags { - fmt.Println("key Flags mismatch") - t.FailNow() + t.Fatal(err) } if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { - fmt.Println("key PrivateKey mismatch") - t.FailNow() + t.Fatal(err) } err = ks.DeleteKey(k2.Id, pass) // also to clean up created files if err != nil { - t.Error(err) - t.FailNow() + t.Fatal(err) } } func TestKeyStorePassphraseDecryptionFail(t *testing.T) { ks := NewKeyStorePassphrase(DefaultDataDir()) pass := "foo" - k1, err := ks.GenerateNewKey(pass) + k1, err := ks.GenerateNewKey(crand.Reader, pass) if err != nil { - t.Error(err) - t.FailNow() + t.Fatal(err) } _, err = ks.GetKey(k1.Id, "bar") // wrong passphrase - // t.Error(err) if err == nil { - t.FailNow() + t.Fatal(err) } err = ks.DeleteKey(k1.Id, "bar") // wrong passphrase if err == nil { - t.Error(err) - t.FailNow() + t.Fatal(err) } err = ks.DeleteKey(k1.Id, pass) // to clean up if err != nil { - t.Error(err) - t.FailNow() + t.Fatal(err) } } - -func TestKeyMixedEntropy(t *testing.T) { - GetEntropyTinFoilHat() -} From 9caf32befebb41fbb013b299caf2fff5fe840430 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Thu, 15 Jan 2015 19:58:38 +0100 Subject: [PATCH 04/15] Update code comments --- crypto/key_store_passphrase.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crypto/key_store_passphrase.go b/crypto/key_store_passphrase.go index 1e7c50f965..c7f740ae36 100644 --- a/crypto/key_store_passphrase.go +++ b/crypto/key_store_passphrase.go @@ -22,9 +22,8 @@ */ /* -This key store behaves as KeyStorePlaintextFile with the difference that -the private key is encrypted and encoded as a JSON object within the -key JSON object. +This key store behaves as KeyStorePlain with the difference that +the private key is encrypted and on disk uses another JSON encoding. Cryptography: @@ -39,9 +38,9 @@ Cryptography: Encoding: -1. On disk, ciphertext, salt and IV are encoded as a JSON object. +1. On disk, ciphertext, salt and IV are encoded in a nested JSON object. cat a key file to see the structure. -2. byte arrays are ASCII HEX encoded as JSON strings. +2. byte arrays are base64 JSON strings. 3. The EC private key bytes are in uncompressed form [7]. They are a big-endian byte slice of the absolute value of D [8][9]. 4. The checksum is the last 32 bytes of the plaintext byte array and the @@ -205,7 +204,6 @@ func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes return keyBytes, err } -// plain crypto/rand. this is /dev/urandom on Unix-like systems. func getEntropyCSPRNG(n int) []byte { mainBuff := make([]byte, n) _, err := io.ReadFull(crand.Reader, mainBuff) From 89c69a1d257daf973690a7fb96d2aa2b9d0849ec Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 Jan 2015 11:18:34 +0100 Subject: [PATCH 05/15] VmDebug => StdVm --- vm/common.go | 5 +- vm/vm.go | 969 +++++++++++++++++++++++++++++++++++++++++++++++-- vm/vm_debug.go | 961 ------------------------------------------------ vm/vm_jit.go | 8 +- 4 files changed, 952 insertions(+), 991 deletions(-) delete mode 100644 vm/vm_debug.go diff --git a/vm/common.go b/vm/common.go index ed250dab1b..ff187001f8 100644 --- a/vm/common.go +++ b/vm/common.go @@ -9,11 +9,10 @@ import ( var vmlogger = logger.NewLogger("VM") -type Type int +type Type byte const ( - StandardVmTy Type = iota - DebugVmTy + StdVmTy Type = iota JitVmTy MaxVmTy diff --git a/vm/vm.go b/vm/vm.go index b795bb86e6..6aa39797ec 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -1,38 +1,961 @@ package vm -import "math/big" +import ( + "fmt" + "math/big" -// BIG FAT WARNING. THIS VM IS NOT YET IS USE! -// I want to get all VM tests pass first before updating this VM + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" +) type Vm struct { - env Environment - err error - depth int + env Environment + + logTy byte + logStr string + + err error + + // Debugging + Dbg Debugger + + BreakPoints []int64 + Stepping bool + Fn string + + Recoverable bool } -func New(env Environment, typ Type) VirtualMachine { - switch typ { - case DebugVmTy: - return NewDebugVm(env) - case JitVmTy: - return NewJitVm(env) - default: - return &Vm{env: env} +func NewVm(env Environment) *Vm { + lt := LogTyPretty + if ethutil.Config.Diff { + lt = LogTyDiff + } + + return &Vm{env: env, logTy: lt, Recoverable: true} +} + +func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) { + self.env.SetDepth(self.env.Depth() + 1) + + msg := self.env.State().Manifest().AddMessage(&state.Message{ + To: me.Address(), From: caller.Address(), + Input: callData, + Origin: self.env.Origin(), + Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(), + Value: value, + }) + context := NewContext(caller, me, code, gas, price) + + vmlogger.Debugf("(%d) (%x) %x (code=%d) gas: %v (d) %x\n", self.env.Depth(), caller.Address()[:4], context.Address(), len(code), context.Gas, callData) + + if self.Recoverable { + // Recover from any require exception + defer func() { + if r := recover(); r != nil { + self.Printf(" %v", r).Endl() + + context.UseGas(context.Gas) + + ret = context.Return(nil) + + err = fmt.Errorf("%v", r) + + } + }() + } + + if p := Precompiled[string(me.Address())]; p != nil { + return self.RunPrecompiled(p, callData, context) + } + + var ( + op OpCode + + destinations = analyseJumpDests(context.Code) + mem = NewMemory() + stack = NewStack() + pc uint64 = 0 + step = 0 + prevStep = 0 + statedb = self.env.State() + + jump = func(from uint64, to *big.Int) { + p := to.Uint64() + + nop := context.GetOp(p) + if !destinations.Has(p) { + panic(fmt.Sprintf("invalid jump destination (%v) %v", nop, p)) + } + + self.Printf(" ~> %v", to) + pc = to.Uint64() + + self.Endl() + } + ) + + // Don't bother with the execution if there's no code. + if len(code) == 0 { + return context.Return(nil), nil + } + + for { + prevStep = step + // The base for all big integer arithmetic + base := new(big.Int) + + step++ + // Get the memory location of pc + op = context.GetOp(pc) + + self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.Len()) + + newMemSize, gas := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) + + self.Printf("(g) %-3v (%v)", gas, context.Gas) + + if !context.UseGas(gas) { + self.Endl() + + tmp := new(big.Int).Set(context.Gas) + + context.UseGas(context.Gas) + + return context.Return(nil), OOG(gas, tmp) + } + + mem.Resize(newMemSize.Uint64()) + + switch op { + // 0x20 range + case ADD: + x, y := stack.Popn() + self.Printf(" %v + %v", y, x) + + base.Add(y, x) + + U256(base) + + self.Printf(" = %v", base) + // Pop result back on the stack + stack.Push(base) + case SUB: + x, y := stack.Popn() + self.Printf(" %v - %v", y, x) + + base.Sub(y, x) + + U256(base) + + self.Printf(" = %v", base) + // Pop result back on the stack + stack.Push(base) + case MUL: + x, y := stack.Popn() + self.Printf(" %v * %v", y, x) + + base.Mul(y, x) + + U256(base) + + self.Printf(" = %v", base) + // Pop result back on the stack + stack.Push(base) + case DIV: + x, y := stack.Pop(), stack.Pop() + self.Printf(" %v / %v", x, y) + + if y.Cmp(ethutil.Big0) != 0 { + base.Div(x, y) + } + + U256(base) + + self.Printf(" = %v", base) + // Pop result back on the stack + stack.Push(base) + case SDIV: + x, y := S256(stack.Pop()), S256(stack.Pop()) + + self.Printf(" %v / %v", x, y) + + if y.Cmp(ethutil.Big0) == 0 { + base.Set(ethutil.Big0) + } else { + n := new(big.Int) + if new(big.Int).Mul(x, y).Cmp(ethutil.Big0) < 0 { + n.SetInt64(-1) + } else { + n.SetInt64(1) + } + + base.Div(x.Abs(x), y.Abs(y)).Mul(base, n) + + U256(base) + } + + self.Printf(" = %v", base) + stack.Push(base) + case MOD: + x, y := stack.Pop(), stack.Pop() + + self.Printf(" %v %% %v", x, y) + + if y.Cmp(ethutil.Big0) == 0 { + base.Set(ethutil.Big0) + } else { + base.Mod(x, y) + } + + U256(base) + + self.Printf(" = %v", base) + stack.Push(base) + case SMOD: + x, y := S256(stack.Pop()), S256(stack.Pop()) + + self.Printf(" %v %% %v", x, y) + + if y.Cmp(ethutil.Big0) == 0 { + base.Set(ethutil.Big0) + } else { + n := new(big.Int) + if x.Cmp(ethutil.Big0) < 0 { + n.SetInt64(-1) + } else { + n.SetInt64(1) + } + + base.Mod(x.Abs(x), y.Abs(y)).Mul(base, n) + + U256(base) + } + + self.Printf(" = %v", base) + stack.Push(base) + + case EXP: + x, y := stack.Popn() + + self.Printf(" %v ** %v", y, x) + + base.Exp(y, x, Pow256) + + U256(base) + + self.Printf(" = %v", base) + + stack.Push(base) + case SIGNEXTEND: + back := stack.Pop().Uint64() + if back < 31 { + bit := uint(back*8 + 7) + num := stack.Pop() + mask := new(big.Int).Lsh(ethutil.Big1, bit) + mask.Sub(mask, ethutil.Big1) + if ethutil.BitTest(num, int(bit)) { + num.Or(num, mask.Not(mask)) + } else { + num.And(num, mask) + } + + num = U256(num) + + self.Printf(" = %v", num) + + stack.Push(num) + } + case NOT: + base.Sub(Pow256, stack.Pop()).Sub(base, ethutil.Big1) + + // Not needed + //base = U256(base) + + stack.Push(base) + case LT: + x, y := stack.Popn() + self.Printf(" %v < %v", y, x) + // x < y + if y.Cmp(x) < 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + case GT: + x, y := stack.Popn() + self.Printf(" %v > %v", y, x) + + // x > y + if y.Cmp(x) > 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + + case SLT: + y, x := S256(stack.Pop()), S256(stack.Pop()) + self.Printf(" %v < %v", y, x) + // x < y + if y.Cmp(S256(x)) < 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + case SGT: + y, x := S256(stack.Pop()), S256(stack.Pop()) + self.Printf(" %v > %v", y, x) + + // x > y + if y.Cmp(x) > 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + + case EQ: + x, y := stack.Popn() + self.Printf(" %v == %v", y, x) + + // x == y + if x.Cmp(y) == 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + case ISZERO: + x := stack.Pop() + if x.Cmp(ethutil.BigFalse) > 0 { + stack.Push(ethutil.BigFalse) + } else { + stack.Push(ethutil.BigTrue) + } + + // 0x10 range + case AND: + x, y := stack.Popn() + self.Printf(" %v & %v", y, x) + + stack.Push(base.And(y, x)) + case OR: + x, y := stack.Popn() + self.Printf(" %v | %v", y, x) + + stack.Push(base.Or(y, x)) + case XOR: + x, y := stack.Popn() + self.Printf(" %v ^ %v", y, x) + + stack.Push(base.Xor(y, x)) + case BYTE: + val, th := stack.Popn() + + if th.Cmp(big.NewInt(32)) < 0 { + byt := big.NewInt(int64(ethutil.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) + + base.Set(byt) + } else { + base.Set(ethutil.BigFalse) + } + + self.Printf(" => 0x%x", base.Bytes()) + + stack.Push(base) + case ADDMOD: + + x := stack.Pop() + y := stack.Pop() + z := stack.Pop() + + add := new(big.Int).Add(x, y) + if len(z.Bytes()) > 0 { // NOT 0x0 + base.Mod(add, z) + + U256(base) + } + + self.Printf(" %v + %v %% %v = %v", x, y, z, base) + + stack.Push(base) + case MULMOD: + + x := stack.Pop() + y := stack.Pop() + z := stack.Pop() + + mul := new(big.Int).Mul(x, y) + if len(z.Bytes()) > 0 { // NOT 0x0 + base.Mod(mul, z) + + U256(base) + } + + self.Printf(" %v + %v %% %v = %v", x, y, z, base) + + stack.Push(base) + + // 0x20 range + case SHA3: + size, offset := stack.Popn() + data := crypto.Sha3(mem.Get(offset.Int64(), size.Int64())) + + stack.Push(ethutil.BigD(data)) + + self.Printf(" => %x", data) + // 0x30 range + case ADDRESS: + stack.Push(ethutil.BigD(context.Address())) + + self.Printf(" => %x", context.Address()) + case BALANCE: + + addr := stack.Pop().Bytes() + balance := statedb.GetBalance(addr) + + stack.Push(balance) + + self.Printf(" => %v (%x)", balance, addr) + case ORIGIN: + origin := self.env.Origin() + + stack.Push(ethutil.BigD(origin)) + + self.Printf(" => %x", origin) + case CALLER: + caller := context.caller.Address() + stack.Push(ethutil.BigD(caller)) + + self.Printf(" => %x", caller) + case CALLVALUE: + stack.Push(value) + + self.Printf(" => %v", value) + case CALLDATALOAD: + var ( + offset = stack.Pop() + data = make([]byte, 32) + lenData = big.NewInt(int64(len(callData))) + ) + + if lenData.Cmp(offset) >= 0 { + length := new(big.Int).Add(offset, ethutil.Big32) + length = ethutil.BigMin(length, lenData) + + copy(data, callData[offset.Int64():length.Int64()]) + } + + self.Printf(" => 0x%x", data) + + stack.Push(ethutil.BigD(data)) + case CALLDATASIZE: + l := int64(len(callData)) + stack.Push(big.NewInt(l)) + + self.Printf(" => %d", l) + case CALLDATACOPY: + var ( + size = uint64(len(callData)) + mOff = stack.Pop().Uint64() + cOff = stack.Pop().Uint64() + l = stack.Pop().Uint64() + ) + + if cOff > size { + cOff = 0 + l = 0 + } else if cOff+l > size { + l = 0 + } + + code := callData[cOff : cOff+l] + + mem.Set(mOff, l, code) + + self.Printf(" => [%v, %v, %v] %x", mOff, cOff, l, callData[cOff:cOff+l]) + case CODESIZE, EXTCODESIZE: + var code []byte + if op == EXTCODESIZE { + addr := stack.Pop().Bytes() + + code = statedb.GetCode(addr) + } else { + code = context.Code + } + + l := big.NewInt(int64(len(code))) + stack.Push(l) + + self.Printf(" => %d", l) + case CODECOPY, EXTCODECOPY: + var code []byte + if op == EXTCODECOPY { + code = statedb.GetCode(stack.Pop().Bytes()) + } else { + code = context.Code + } + context := NewContext(nil, nil, code, ethutil.Big0, ethutil.Big0) + var ( + mOff = stack.Pop().Uint64() + cOff = stack.Pop().Uint64() + l = stack.Pop().Uint64() + ) + codeCopy := context.GetCode(cOff, l) + + mem.Set(mOff, l, codeCopy) + + self.Printf(" => [%v, %v, %v] %x", mOff, cOff, l, codeCopy) + case GASPRICE: + stack.Push(context.Price) + + self.Printf(" => %v", context.Price) + + // 0x40 range + case BLOCKHASH: + num := stack.Pop() + + n := U256(new(big.Int).Sub(self.env.BlockNumber(), ethutil.Big257)) + if num.Cmp(n) > 0 && num.Cmp(self.env.BlockNumber()) < 0 { + stack.Push(ethutil.BigD(self.env.GetHash(num.Uint64()))) + } else { + stack.Push(ethutil.Big0) + } + + self.Printf(" => 0x%x", stack.Peek().Bytes()) + case COINBASE: + coinbase := self.env.Coinbase() + + stack.Push(ethutil.BigD(coinbase)) + + self.Printf(" => 0x%x", coinbase) + case TIMESTAMP: + time := self.env.Time() + + stack.Push(big.NewInt(time)) + + self.Printf(" => 0x%x", time) + case NUMBER: + number := self.env.BlockNumber() + + stack.Push(number) + + self.Printf(" => 0x%x", number.Bytes()) + case DIFFICULTY: + difficulty := self.env.Difficulty() + + stack.Push(difficulty) + + self.Printf(" => 0x%x", difficulty.Bytes()) + case GASLIMIT: + self.Printf(" => %v", self.env.GasLimit()) + + stack.Push(self.env.GasLimit()) + + // 0x50 range + case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: + a := uint64(op - PUSH1 + 1) + byts := context.GetRangeValue(pc+1, a) + // Push value to stack + stack.Push(ethutil.BigD(byts)) + pc += a + + step += int(op) - int(PUSH1) + 1 + + self.Printf(" => 0x%x", byts) + case POP: + stack.Pop() + case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: + n := int(op - DUP1 + 1) + stack.Dupn(n) + + self.Printf(" => [%d] 0x%x", n, stack.Peek().Bytes()) + case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: + n := int(op - SWAP1 + 2) + x, y := stack.Swapn(n) + + self.Printf(" => [%d] %x [0] %x", n, x.Bytes(), y.Bytes()) + case LOG0, LOG1, LOG2, LOG3, LOG4: + n := int(op - LOG0) + topics := make([][]byte, n) + mSize, mStart := stack.Popn() + for i := 0; i < n; i++ { + topics[i] = ethutil.LeftPadBytes(stack.Pop().Bytes(), 32) + } + + data := mem.Get(mStart.Int64(), mSize.Int64()) + log := &Log{context.Address(), topics, data} + self.env.AddLog(log) + + self.Printf(" => %v", log) + case MLOAD: + offset := stack.Pop() + val := ethutil.BigD(mem.Get(offset.Int64(), 32)) + stack.Push(val) + + self.Printf(" => 0x%x", val.Bytes()) + case MSTORE: // Store the value at stack top-1 in to memory at location stack top + // Pop value of the stack + val, mStart := stack.Popn() + mem.Set(mStart.Uint64(), 32, ethutil.BigToBytes(val, 256)) + + self.Printf(" => 0x%x", val) + case MSTORE8: + off := stack.Pop() + val := stack.Pop() + + mem.store[off.Int64()] = byte(val.Int64() & 0xff) + + self.Printf(" => [%v] 0x%x", off, val) + case SLOAD: + loc := stack.Pop() + val := ethutil.BigD(statedb.GetState(context.Address(), loc.Bytes())) + stack.Push(val) + + self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) + case SSTORE: + val, loc := stack.Popn() + statedb.SetState(context.Address(), loc.Bytes(), val) + + msg.AddStorageChange(loc.Bytes()) + + self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) + case JUMP: + jump(pc, stack.Pop()) + + continue + case JUMPI: + cond, pos := stack.Popn() + + if cond.Cmp(ethutil.BigTrue) >= 0 { + jump(pc, pos) + + continue + } + + case JUMPDEST: + case PC: + stack.Push(big.NewInt(int64(pc))) + case MSIZE: + stack.Push(big.NewInt(int64(mem.Len()))) + case GAS: + stack.Push(context.Gas) + // 0x60 range + case CREATE: + + var ( + value = stack.Pop() + size, offset = stack.Popn() + input = mem.Get(offset.Int64(), size.Int64()) + gas = new(big.Int).Set(context.Gas) + addr []byte + ) + + context.UseGas(context.Gas) + ret, suberr, ref := self.env.Create(context, nil, input, gas, price, value) + if suberr != nil { + stack.Push(ethutil.BigFalse) + + self.Printf(" (*) 0x0 %v", suberr) + } else { + + // gas < len(ret) * CreateDataGas == NO_CODE + dataGas := big.NewInt(int64(len(ret))) + dataGas.Mul(dataGas, GasCreateByte) + if context.UseGas(dataGas) { + ref.SetCode(ret) + msg.Output = ret + } + addr = ref.Address() + + stack.Push(ethutil.BigD(addr)) + + self.Printf(" (*) %x", addr) + } + + // Debug hook + if self.Dbg != nil { + self.Dbg.SetCode(context.Code) + } + case CALL, CALLCODE: + self.Endl() + + gas := stack.Pop() + // Pop gas and value of the stack. + value, addr := stack.Popn() + // Pop input size and offset + inSize, inOffset := stack.Popn() + // Pop return size and offset + retSize, retOffset := stack.Popn() + + // Get the arguments from the memory + args := mem.Get(inOffset.Int64(), inSize.Int64()) + + var ( + ret []byte + err error + ) + if op == CALLCODE { + ret, err = self.env.CallCode(context, addr.Bytes(), args, gas, price, value) + } else { + ret, err = self.env.Call(context, addr.Bytes(), args, gas, price, value) + } + + if err != nil { + stack.Push(ethutil.BigFalse) + + vmlogger.Debugln(err) + } else { + stack.Push(ethutil.BigTrue) + msg.Output = ret + + mem.Set(retOffset.Uint64(), retSize.Uint64(), ret) + } + self.Printf("resume %x (%v)", context.Address(), context.Gas) + + // Debug hook + if self.Dbg != nil { + self.Dbg.SetCode(context.Code) + } + + case RETURN: + size, offset := stack.Popn() + ret := mem.Get(offset.Int64(), size.Int64()) + + self.Printf(" => [%v, %v] (%d) 0x%x", offset, size, len(ret), ret).Endl() + + return context.Return(ret), nil + case SUICIDE: + receiver := statedb.GetOrNewStateObject(stack.Pop().Bytes()) + balance := statedb.GetBalance(context.Address()) + + self.Printf(" => (%x) %v", receiver.Address()[:4], balance) + + receiver.AddAmount(balance) + statedb.Delete(context.Address()) + + fallthrough + case STOP: // Stop the context + self.Endl() + + return context.Return(nil), nil + default: + vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op) + + panic(fmt.Errorf("Invalid opcode %x", op)) + } + + pc++ + + self.Endl() + + if self.Dbg != nil { + for _, instrNo := range self.Dbg.BreakPoints() { + if pc == uint64(instrNo) { + self.Stepping = true + + if !self.Dbg.BreakHook(prevStep, op, mem, stack, statedb.GetStateObject(context.Address())) { + return nil, nil + } + } else if self.Stepping { + if !self.Dbg.StepHook(prevStep, op, mem, stack, statedb.GetStateObject(context.Address())) { + return nil, nil + } + } + } + } + } } -func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, data []byte) (ret []byte, err error) { - panic("not implemented") +func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *Stack) (*big.Int, *big.Int) { + gas := new(big.Int) + addStepGasUsage := func(amount *big.Int) { + if amount.Cmp(ethutil.Big0) >= 0 { + gas.Add(gas, amount) + } + } + + addStepGasUsage(GasStep) + + var newMemSize *big.Int = ethutil.Big0 + var additionalGas *big.Int = new(big.Int) + // Stack Check, memory resize & gas phase + switch op { + // Stack checks only + case ISZERO, CALLDATALOAD, POP, JUMP, NOT: // 1 + stack.require(1) + case JUMPI, ADD, SUB, DIV, SDIV, MOD, SMOD, LT, GT, SLT, SGT, EQ, AND, OR, XOR, BYTE, SIGNEXTEND: // 2 + stack.require(2) + case ADDMOD, MULMOD: // 3 + stack.require(3) + case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: + n := int(op - SWAP1 + 2) + stack.require(n) + case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: + n := int(op - DUP1 + 1) + stack.require(n) + case LOG0, LOG1, LOG2, LOG3, LOG4: + n := int(op - LOG0) + stack.require(n + 2) + + gas.Set(GasLog) + addStepGasUsage(new(big.Int).Mul(big.NewInt(int64(n)), GasLog)) + + mSize, mStart := stack.Peekn() + addStepGasUsage(mSize) + + newMemSize = calcMemSize(mStart, mSize) + case EXP: + stack.require(2) + + gas.Set(big.NewInt(int64(len(stack.data[stack.Len()-2].Bytes()) + 1))) + // Gas only + case STOP: + gas.Set(ethutil.Big0) + case SUICIDE: + stack.require(1) + + gas.Set(ethutil.Big0) + case SLOAD: + stack.require(1) + + gas.Set(GasSLoad) + // Memory resize & Gas + case SSTORE: + stack.require(2) + + var mult *big.Int + y, x := stack.Peekn() + val := statedb.GetState(context.Address(), x.Bytes()) + if len(val) == 0 && len(y.Bytes()) > 0 { + // 0 => non 0 + mult = ethutil.Big3 + } else if len(val) > 0 && len(y.Bytes()) == 0 { + statedb.Refund(caller.Address(), GasSStoreRefund) + + mult = ethutil.Big0 + } else { + // non 0 => non 0 (or 0 => 0) + mult = ethutil.Big1 + } + gas.Set(new(big.Int).Mul(mult, GasSStore)) + case BALANCE: + stack.require(1) + gas.Set(GasBalance) + case MSTORE: + stack.require(2) + newMemSize = calcMemSize(stack.Peek(), u256(32)) + case MLOAD: + stack.require(1) + + newMemSize = calcMemSize(stack.Peek(), u256(32)) + case MSTORE8: + stack.require(2) + newMemSize = calcMemSize(stack.Peek(), u256(1)) + case RETURN: + stack.require(2) + + newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2]) + case SHA3: + stack.require(2) + gas.Set(GasSha) + newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2]) + additionalGas.Set(stack.data[stack.Len()-2]) + case CALLDATACOPY: + stack.require(2) + + newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3]) + additionalGas.Set(stack.data[stack.Len()-3]) + case CODECOPY: + stack.require(3) + + newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3]) + additionalGas.Set(stack.data[stack.Len()-3]) + case EXTCODECOPY: + stack.require(4) + + newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-4]) + additionalGas.Set(stack.data[stack.Len()-4]) + case CALL, CALLCODE: + stack.require(7) + gas.Set(GasCall) + addStepGasUsage(stack.data[stack.Len()-1]) + + x := calcMemSize(stack.data[stack.Len()-6], stack.data[stack.Len()-7]) + y := calcMemSize(stack.data[stack.Len()-4], stack.data[stack.Len()-5]) + + newMemSize = ethutil.BigMax(x, y) + case CREATE: + stack.require(3) + gas.Set(GasCreate) + + newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-3]) + } + + switch op { + case CALLDATACOPY, CODECOPY, EXTCODECOPY: + additionalGas.Add(additionalGas, u256(31)) + additionalGas.Div(additionalGas, u256(32)) + addStepGasUsage(additionalGas) + case SHA3: + additionalGas.Add(additionalGas, u256(31)) + additionalGas.Div(additionalGas, u256(32)) + additionalGas.Mul(additionalGas, GasSha3Byte) + addStepGasUsage(additionalGas) + } + + if newMemSize.Cmp(ethutil.Big0) > 0 { + newMemSize.Add(newMemSize, u256(31)) + newMemSize.Div(newMemSize, u256(32)) + newMemSize.Mul(newMemSize, u256(32)) + + if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { + memGasUsage := new(big.Int).Sub(newMemSize, u256(int64(mem.Len()))) + memGasUsage.Mul(GasMemory, memGasUsage) + memGasUsage.Div(memGasUsage, u256(32)) + + addStepGasUsage(memGasUsage) + } + + } + + return newMemSize, gas +} + +func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) { + gas := p.Gas(len(callData)) + if context.UseGas(gas) { + ret = p.Call(callData) + self.Printf("NATIVE_FUNC => %x", ret) + self.Endl() + + return context.Return(ret), nil + } else { + self.Printf("NATIVE_FUNC => failed").Endl() + + tmp := new(big.Int).Set(context.Gas) + + panic(OOG(gas, tmp).Error()) + } +} + +func (self *Vm) Printf(format string, v ...interface{}) VirtualMachine { + if self.logTy == LogTyPretty { + self.logStr += fmt.Sprintf(format, v...) + } + + return self +} + +func (self *Vm) Endl() VirtualMachine { + if self.logTy == LogTyPretty { + vmlogger.Debugln(self.logStr) + self.logStr = "" + } + + return self } func (self *Vm) Env() Environment { return self.env } - -func (self *Vm) Depth() int { - return self.depth -} - -func (self *Vm) Printf(format string, v ...interface{}) VirtualMachine { return self } -func (self *Vm) Endl() VirtualMachine { return self } diff --git a/vm/vm_debug.go b/vm/vm_debug.go deleted file mode 100644 index 44137568e0..0000000000 --- a/vm/vm_debug.go +++ /dev/null @@ -1,961 +0,0 @@ -package vm - -import ( - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethutil" - "github.com/ethereum/go-ethereum/state" -) - -type DebugVm struct { - env Environment - - logTy byte - logStr string - - err error - - // Debugging - Dbg Debugger - - BreakPoints []int64 - Stepping bool - Fn string - - Recoverable bool -} - -func NewDebugVm(env Environment) *DebugVm { - lt := LogTyPretty - if ethutil.Config.Diff { - lt = LogTyDiff - } - - return &DebugVm{env: env, logTy: lt, Recoverable: true} -} - -func (self *DebugVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) { - self.env.SetDepth(self.env.Depth() + 1) - - msg := self.env.State().Manifest().AddMessage(&state.Message{ - To: me.Address(), From: caller.Address(), - Input: callData, - Origin: self.env.Origin(), - Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(), - Value: value, - }) - context := NewContext(caller, me, code, gas, price) - - vmlogger.Debugf("(%d) (%x) %x (code=%d) gas: %v (d) %x\n", self.env.Depth(), caller.Address()[:4], context.Address(), len(code), context.Gas, callData) - - if self.Recoverable { - // Recover from any require exception - defer func() { - if r := recover(); r != nil { - self.Printf(" %v", r).Endl() - - context.UseGas(context.Gas) - - ret = context.Return(nil) - - err = fmt.Errorf("%v", r) - - } - }() - } - - if p := Precompiled[string(me.Address())]; p != nil { - return self.RunPrecompiled(p, callData, context) - } - - var ( - op OpCode - - destinations = analyseJumpDests(context.Code) - mem = NewMemory() - stack = NewStack() - pc uint64 = 0 - step = 0 - prevStep = 0 - statedb = self.env.State() - - jump = func(from uint64, to *big.Int) { - p := to.Uint64() - - nop := context.GetOp(p) - if !destinations.Has(p) { - panic(fmt.Sprintf("invalid jump destination (%v) %v", nop, p)) - } - - self.Printf(" ~> %v", to) - pc = to.Uint64() - - self.Endl() - } - ) - - // Don't bother with the execution if there's no code. - if len(code) == 0 { - return context.Return(nil), nil - } - - for { - prevStep = step - // The base for all big integer arithmetic - base := new(big.Int) - - step++ - // Get the memory location of pc - op = context.GetOp(pc) - - self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.Len()) - - newMemSize, gas := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) - - self.Printf("(g) %-3v (%v)", gas, context.Gas) - - if !context.UseGas(gas) { - self.Endl() - - tmp := new(big.Int).Set(context.Gas) - - context.UseGas(context.Gas) - - return context.Return(nil), OOG(gas, tmp) - } - - mem.Resize(newMemSize.Uint64()) - - switch op { - // 0x20 range - case ADD: - x, y := stack.Popn() - self.Printf(" %v + %v", y, x) - - base.Add(y, x) - - U256(base) - - self.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) - case SUB: - x, y := stack.Popn() - self.Printf(" %v - %v", y, x) - - base.Sub(y, x) - - U256(base) - - self.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) - case MUL: - x, y := stack.Popn() - self.Printf(" %v * %v", y, x) - - base.Mul(y, x) - - U256(base) - - self.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) - case DIV: - x, y := stack.Pop(), stack.Pop() - self.Printf(" %v / %v", x, y) - - if y.Cmp(ethutil.Big0) != 0 { - base.Div(x, y) - } - - U256(base) - - self.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) - case SDIV: - x, y := S256(stack.Pop()), S256(stack.Pop()) - - self.Printf(" %v / %v", x, y) - - if y.Cmp(ethutil.Big0) == 0 { - base.Set(ethutil.Big0) - } else { - n := new(big.Int) - if new(big.Int).Mul(x, y).Cmp(ethutil.Big0) < 0 { - n.SetInt64(-1) - } else { - n.SetInt64(1) - } - - base.Div(x.Abs(x), y.Abs(y)).Mul(base, n) - - U256(base) - } - - self.Printf(" = %v", base) - stack.Push(base) - case MOD: - x, y := stack.Pop(), stack.Pop() - - self.Printf(" %v %% %v", x, y) - - if y.Cmp(ethutil.Big0) == 0 { - base.Set(ethutil.Big0) - } else { - base.Mod(x, y) - } - - U256(base) - - self.Printf(" = %v", base) - stack.Push(base) - case SMOD: - x, y := S256(stack.Pop()), S256(stack.Pop()) - - self.Printf(" %v %% %v", x, y) - - if y.Cmp(ethutil.Big0) == 0 { - base.Set(ethutil.Big0) - } else { - n := new(big.Int) - if x.Cmp(ethutil.Big0) < 0 { - n.SetInt64(-1) - } else { - n.SetInt64(1) - } - - base.Mod(x.Abs(x), y.Abs(y)).Mul(base, n) - - U256(base) - } - - self.Printf(" = %v", base) - stack.Push(base) - - case EXP: - x, y := stack.Popn() - - self.Printf(" %v ** %v", y, x) - - base.Exp(y, x, Pow256) - - U256(base) - - self.Printf(" = %v", base) - - stack.Push(base) - case SIGNEXTEND: - back := stack.Pop().Uint64() - if back < 31 { - bit := uint(back*8 + 7) - num := stack.Pop() - mask := new(big.Int).Lsh(ethutil.Big1, bit) - mask.Sub(mask, ethutil.Big1) - if ethutil.BitTest(num, int(bit)) { - num.Or(num, mask.Not(mask)) - } else { - num.And(num, mask) - } - - num = U256(num) - - self.Printf(" = %v", num) - - stack.Push(num) - } - case NOT: - base.Sub(Pow256, stack.Pop()).Sub(base, ethutil.Big1) - - // Not needed - //base = U256(base) - - stack.Push(base) - case LT: - x, y := stack.Popn() - self.Printf(" %v < %v", y, x) - // x < y - if y.Cmp(x) < 0 { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } - case GT: - x, y := stack.Popn() - self.Printf(" %v > %v", y, x) - - // x > y - if y.Cmp(x) > 0 { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } - - case SLT: - y, x := S256(stack.Pop()), S256(stack.Pop()) - self.Printf(" %v < %v", y, x) - // x < y - if y.Cmp(S256(x)) < 0 { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } - case SGT: - y, x := S256(stack.Pop()), S256(stack.Pop()) - self.Printf(" %v > %v", y, x) - - // x > y - if y.Cmp(x) > 0 { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } - - case EQ: - x, y := stack.Popn() - self.Printf(" %v == %v", y, x) - - // x == y - if x.Cmp(y) == 0 { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } - case ISZERO: - x := stack.Pop() - if x.Cmp(ethutil.BigFalse) > 0 { - stack.Push(ethutil.BigFalse) - } else { - stack.Push(ethutil.BigTrue) - } - - // 0x10 range - case AND: - x, y := stack.Popn() - self.Printf(" %v & %v", y, x) - - stack.Push(base.And(y, x)) - case OR: - x, y := stack.Popn() - self.Printf(" %v | %v", y, x) - - stack.Push(base.Or(y, x)) - case XOR: - x, y := stack.Popn() - self.Printf(" %v ^ %v", y, x) - - stack.Push(base.Xor(y, x)) - case BYTE: - val, th := stack.Popn() - - if th.Cmp(big.NewInt(32)) < 0 { - byt := big.NewInt(int64(ethutil.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) - - base.Set(byt) - } else { - base.Set(ethutil.BigFalse) - } - - self.Printf(" => 0x%x", base.Bytes()) - - stack.Push(base) - case ADDMOD: - - x := stack.Pop() - y := stack.Pop() - z := stack.Pop() - - add := new(big.Int).Add(x, y) - if len(z.Bytes()) > 0 { // NOT 0x0 - base.Mod(add, z) - - U256(base) - } - - self.Printf(" %v + %v %% %v = %v", x, y, z, base) - - stack.Push(base) - case MULMOD: - - x := stack.Pop() - y := stack.Pop() - z := stack.Pop() - - mul := new(big.Int).Mul(x, y) - if len(z.Bytes()) > 0 { // NOT 0x0 - base.Mod(mul, z) - - U256(base) - } - - self.Printf(" %v + %v %% %v = %v", x, y, z, base) - - stack.Push(base) - - // 0x20 range - case SHA3: - size, offset := stack.Popn() - data := crypto.Sha3(mem.Get(offset.Int64(), size.Int64())) - - stack.Push(ethutil.BigD(data)) - - self.Printf(" => %x", data) - // 0x30 range - case ADDRESS: - stack.Push(ethutil.BigD(context.Address())) - - self.Printf(" => %x", context.Address()) - case BALANCE: - - addr := stack.Pop().Bytes() - balance := statedb.GetBalance(addr) - - stack.Push(balance) - - self.Printf(" => %v (%x)", balance, addr) - case ORIGIN: - origin := self.env.Origin() - - stack.Push(ethutil.BigD(origin)) - - self.Printf(" => %x", origin) - case CALLER: - caller := context.caller.Address() - stack.Push(ethutil.BigD(caller)) - - self.Printf(" => %x", caller) - case CALLVALUE: - stack.Push(value) - - self.Printf(" => %v", value) - case CALLDATALOAD: - var ( - offset = stack.Pop() - data = make([]byte, 32) - lenData = big.NewInt(int64(len(callData))) - ) - - if lenData.Cmp(offset) >= 0 { - length := new(big.Int).Add(offset, ethutil.Big32) - length = ethutil.BigMin(length, lenData) - - copy(data, callData[offset.Int64():length.Int64()]) - } - - self.Printf(" => 0x%x", data) - - stack.Push(ethutil.BigD(data)) - case CALLDATASIZE: - l := int64(len(callData)) - stack.Push(big.NewInt(l)) - - self.Printf(" => %d", l) - case CALLDATACOPY: - var ( - size = uint64(len(callData)) - mOff = stack.Pop().Uint64() - cOff = stack.Pop().Uint64() - l = stack.Pop().Uint64() - ) - - if cOff > size { - cOff = 0 - l = 0 - } else if cOff+l > size { - l = 0 - } - - code := callData[cOff : cOff+l] - - mem.Set(mOff, l, code) - - self.Printf(" => [%v, %v, %v] %x", mOff, cOff, l, callData[cOff:cOff+l]) - case CODESIZE, EXTCODESIZE: - var code []byte - if op == EXTCODESIZE { - addr := stack.Pop().Bytes() - - code = statedb.GetCode(addr) - } else { - code = context.Code - } - - l := big.NewInt(int64(len(code))) - stack.Push(l) - - self.Printf(" => %d", l) - case CODECOPY, EXTCODECOPY: - var code []byte - if op == EXTCODECOPY { - code = statedb.GetCode(stack.Pop().Bytes()) - } else { - code = context.Code - } - context := NewContext(nil, nil, code, ethutil.Big0, ethutil.Big0) - var ( - mOff = stack.Pop().Uint64() - cOff = stack.Pop().Uint64() - l = stack.Pop().Uint64() - ) - codeCopy := context.GetCode(cOff, l) - - mem.Set(mOff, l, codeCopy) - - self.Printf(" => [%v, %v, %v] %x", mOff, cOff, l, codeCopy) - case GASPRICE: - stack.Push(context.Price) - - self.Printf(" => %v", context.Price) - - // 0x40 range - case BLOCKHASH: - num := stack.Pop() - - n := U256(new(big.Int).Sub(self.env.BlockNumber(), ethutil.Big257)) - if num.Cmp(n) > 0 && num.Cmp(self.env.BlockNumber()) < 0 { - stack.Push(ethutil.BigD(self.env.GetHash(num.Uint64()))) - } else { - stack.Push(ethutil.Big0) - } - - self.Printf(" => 0x%x", stack.Peek().Bytes()) - case COINBASE: - coinbase := self.env.Coinbase() - - stack.Push(ethutil.BigD(coinbase)) - - self.Printf(" => 0x%x", coinbase) - case TIMESTAMP: - time := self.env.Time() - - stack.Push(big.NewInt(time)) - - self.Printf(" => 0x%x", time) - case NUMBER: - number := self.env.BlockNumber() - - stack.Push(number) - - self.Printf(" => 0x%x", number.Bytes()) - case DIFFICULTY: - difficulty := self.env.Difficulty() - - stack.Push(difficulty) - - self.Printf(" => 0x%x", difficulty.Bytes()) - case GASLIMIT: - self.Printf(" => %v", self.env.GasLimit()) - - stack.Push(self.env.GasLimit()) - - // 0x50 range - case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: - a := uint64(op - PUSH1 + 1) - byts := context.GetRangeValue(pc+1, a) - // Push value to stack - stack.Push(ethutil.BigD(byts)) - pc += a - - step += int(op) - int(PUSH1) + 1 - - self.Printf(" => 0x%x", byts) - case POP: - stack.Pop() - case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: - n := int(op - DUP1 + 1) - stack.Dupn(n) - - self.Printf(" => [%d] 0x%x", n, stack.Peek().Bytes()) - case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: - n := int(op - SWAP1 + 2) - x, y := stack.Swapn(n) - - self.Printf(" => [%d] %x [0] %x", n, x.Bytes(), y.Bytes()) - case LOG0, LOG1, LOG2, LOG3, LOG4: - n := int(op - LOG0) - topics := make([][]byte, n) - mSize, mStart := stack.Popn() - for i := 0; i < n; i++ { - topics[i] = ethutil.LeftPadBytes(stack.Pop().Bytes(), 32) - } - - data := mem.Get(mStart.Int64(), mSize.Int64()) - log := &Log{context.Address(), topics, data} - self.env.AddLog(log) - - self.Printf(" => %v", log) - case MLOAD: - offset := stack.Pop() - val := ethutil.BigD(mem.Get(offset.Int64(), 32)) - stack.Push(val) - - self.Printf(" => 0x%x", val.Bytes()) - case MSTORE: // Store the value at stack top-1 in to memory at location stack top - // Pop value of the stack - val, mStart := stack.Popn() - mem.Set(mStart.Uint64(), 32, ethutil.BigToBytes(val, 256)) - - self.Printf(" => 0x%x", val) - case MSTORE8: - off := stack.Pop() - val := stack.Pop() - - mem.store[off.Int64()] = byte(val.Int64() & 0xff) - - self.Printf(" => [%v] 0x%x", off, val) - case SLOAD: - loc := stack.Pop() - val := ethutil.BigD(statedb.GetState(context.Address(), loc.Bytes())) - stack.Push(val) - - self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) - case SSTORE: - val, loc := stack.Popn() - statedb.SetState(context.Address(), loc.Bytes(), val) - - msg.AddStorageChange(loc.Bytes()) - - self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) - case JUMP: - jump(pc, stack.Pop()) - - continue - case JUMPI: - cond, pos := stack.Popn() - - if cond.Cmp(ethutil.BigTrue) >= 0 { - jump(pc, pos) - - continue - } - - case JUMPDEST: - case PC: - stack.Push(big.NewInt(int64(pc))) - case MSIZE: - stack.Push(big.NewInt(int64(mem.Len()))) - case GAS: - stack.Push(context.Gas) - // 0x60 range - case CREATE: - - var ( - value = stack.Pop() - size, offset = stack.Popn() - input = mem.Get(offset.Int64(), size.Int64()) - gas = new(big.Int).Set(context.Gas) - addr []byte - ) - - context.UseGas(context.Gas) - ret, suberr, ref := self.env.Create(context, nil, input, gas, price, value) - if suberr != nil { - stack.Push(ethutil.BigFalse) - - self.Printf(" (*) 0x0 %v", suberr) - } else { - - // gas < len(ret) * CreateDataGas == NO_CODE - dataGas := big.NewInt(int64(len(ret))) - dataGas.Mul(dataGas, GasCreateByte) - if context.UseGas(dataGas) { - ref.SetCode(ret) - msg.Output = ret - } - addr = ref.Address() - - stack.Push(ethutil.BigD(addr)) - - self.Printf(" (*) %x", addr) - } - - // Debug hook - if self.Dbg != nil { - self.Dbg.SetCode(context.Code) - } - case CALL, CALLCODE: - self.Endl() - - gas := stack.Pop() - // Pop gas and value of the stack. - value, addr := stack.Popn() - // Pop input size and offset - inSize, inOffset := stack.Popn() - // Pop return size and offset - retSize, retOffset := stack.Popn() - - // Get the arguments from the memory - args := mem.Get(inOffset.Int64(), inSize.Int64()) - - var ( - ret []byte - err error - ) - if op == CALLCODE { - ret, err = self.env.CallCode(context, addr.Bytes(), args, gas, price, value) - } else { - ret, err = self.env.Call(context, addr.Bytes(), args, gas, price, value) - } - - if err != nil { - stack.Push(ethutil.BigFalse) - - vmlogger.Debugln(err) - } else { - stack.Push(ethutil.BigTrue) - msg.Output = ret - - mem.Set(retOffset.Uint64(), retSize.Uint64(), ret) - } - self.Printf("resume %x (%v)", context.Address(), context.Gas) - - // Debug hook - if self.Dbg != nil { - self.Dbg.SetCode(context.Code) - } - - case RETURN: - size, offset := stack.Popn() - ret := mem.Get(offset.Int64(), size.Int64()) - - self.Printf(" => [%v, %v] (%d) 0x%x", offset, size, len(ret), ret).Endl() - - return context.Return(ret), nil - case SUICIDE: - receiver := statedb.GetOrNewStateObject(stack.Pop().Bytes()) - balance := statedb.GetBalance(context.Address()) - - self.Printf(" => (%x) %v", receiver.Address()[:4], balance) - - receiver.AddAmount(balance) - statedb.Delete(context.Address()) - - fallthrough - case STOP: // Stop the context - self.Endl() - - return context.Return(nil), nil - default: - vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op) - - panic(fmt.Errorf("Invalid opcode %x", op)) - } - - pc++ - - self.Endl() - - if self.Dbg != nil { - for _, instrNo := range self.Dbg.BreakPoints() { - if pc == uint64(instrNo) { - self.Stepping = true - - if !self.Dbg.BreakHook(prevStep, op, mem, stack, statedb.GetStateObject(context.Address())) { - return nil, nil - } - } else if self.Stepping { - if !self.Dbg.StepHook(prevStep, op, mem, stack, statedb.GetStateObject(context.Address())) { - return nil, nil - } - } - } - } - - } -} - -func (self *DebugVm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *Stack) (*big.Int, *big.Int) { - gas := new(big.Int) - addStepGasUsage := func(amount *big.Int) { - if amount.Cmp(ethutil.Big0) >= 0 { - gas.Add(gas, amount) - } - } - - addStepGasUsage(GasStep) - - var newMemSize *big.Int = ethutil.Big0 - var additionalGas *big.Int = new(big.Int) - // Stack Check, memory resize & gas phase - switch op { - // Stack checks only - case ISZERO, CALLDATALOAD, POP, JUMP, NOT: // 1 - stack.require(1) - case JUMPI, ADD, SUB, DIV, SDIV, MOD, SMOD, LT, GT, SLT, SGT, EQ, AND, OR, XOR, BYTE, SIGNEXTEND: // 2 - stack.require(2) - case ADDMOD, MULMOD: // 3 - stack.require(3) - case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: - n := int(op - SWAP1 + 2) - stack.require(n) - case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: - n := int(op - DUP1 + 1) - stack.require(n) - case LOG0, LOG1, LOG2, LOG3, LOG4: - n := int(op - LOG0) - stack.require(n + 2) - - gas.Set(GasLog) - addStepGasUsage(new(big.Int).Mul(big.NewInt(int64(n)), GasLog)) - - mSize, mStart := stack.Peekn() - addStepGasUsage(mSize) - - newMemSize = calcMemSize(mStart, mSize) - case EXP: - stack.require(2) - - gas.Set(big.NewInt(int64(len(stack.data[stack.Len()-2].Bytes()) + 1))) - // Gas only - case STOP: - gas.Set(ethutil.Big0) - case SUICIDE: - stack.require(1) - - gas.Set(ethutil.Big0) - case SLOAD: - stack.require(1) - - gas.Set(GasSLoad) - // Memory resize & Gas - case SSTORE: - stack.require(2) - - var mult *big.Int - y, x := stack.Peekn() - val := statedb.GetState(context.Address(), x.Bytes()) - if len(val) == 0 && len(y.Bytes()) > 0 { - // 0 => non 0 - mult = ethutil.Big3 - } else if len(val) > 0 && len(y.Bytes()) == 0 { - statedb.Refund(caller.Address(), GasSStoreRefund) - - mult = ethutil.Big0 - } else { - // non 0 => non 0 (or 0 => 0) - mult = ethutil.Big1 - } - gas.Set(new(big.Int).Mul(mult, GasSStore)) - case BALANCE: - stack.require(1) - gas.Set(GasBalance) - case MSTORE: - stack.require(2) - newMemSize = calcMemSize(stack.Peek(), u256(32)) - case MLOAD: - stack.require(1) - - newMemSize = calcMemSize(stack.Peek(), u256(32)) - case MSTORE8: - stack.require(2) - newMemSize = calcMemSize(stack.Peek(), u256(1)) - case RETURN: - stack.require(2) - - newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2]) - case SHA3: - stack.require(2) - gas.Set(GasSha) - newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2]) - additionalGas.Set(stack.data[stack.Len()-2]) - case CALLDATACOPY: - stack.require(2) - - newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3]) - additionalGas.Set(stack.data[stack.Len()-3]) - case CODECOPY: - stack.require(3) - - newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3]) - additionalGas.Set(stack.data[stack.Len()-3]) - case EXTCODECOPY: - stack.require(4) - - newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-4]) - additionalGas.Set(stack.data[stack.Len()-4]) - case CALL, CALLCODE: - stack.require(7) - gas.Set(GasCall) - addStepGasUsage(stack.data[stack.Len()-1]) - - x := calcMemSize(stack.data[stack.Len()-6], stack.data[stack.Len()-7]) - y := calcMemSize(stack.data[stack.Len()-4], stack.data[stack.Len()-5]) - - newMemSize = ethutil.BigMax(x, y) - case CREATE: - stack.require(3) - gas.Set(GasCreate) - - newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-3]) - } - - switch op { - case CALLDATACOPY, CODECOPY, EXTCODECOPY: - additionalGas.Add(additionalGas, u256(31)) - additionalGas.Div(additionalGas, u256(32)) - addStepGasUsage(additionalGas) - case SHA3: - additionalGas.Add(additionalGas, u256(31)) - additionalGas.Div(additionalGas, u256(32)) - additionalGas.Mul(additionalGas, GasSha3Byte) - addStepGasUsage(additionalGas) - } - - if newMemSize.Cmp(ethutil.Big0) > 0 { - newMemSize.Add(newMemSize, u256(31)) - newMemSize.Div(newMemSize, u256(32)) - newMemSize.Mul(newMemSize, u256(32)) - - if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { - memGasUsage := new(big.Int).Sub(newMemSize, u256(int64(mem.Len()))) - memGasUsage.Mul(GasMemory, memGasUsage) - memGasUsage.Div(memGasUsage, u256(32)) - - addStepGasUsage(memGasUsage) - } - - } - - return newMemSize, gas -} - -func (self *DebugVm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) { - gas := p.Gas(len(callData)) - if context.UseGas(gas) { - ret = p.Call(callData) - self.Printf("NATIVE_FUNC => %x", ret) - self.Endl() - - return context.Return(ret), nil - } else { - self.Printf("NATIVE_FUNC => failed").Endl() - - tmp := new(big.Int).Set(context.Gas) - - panic(OOG(gas, tmp).Error()) - } -} - -func (self *DebugVm) Printf(format string, v ...interface{}) VirtualMachine { - if self.logTy == LogTyPretty { - self.logStr += fmt.Sprintf(format, v...) - } - - return self -} - -func (self *DebugVm) Endl() VirtualMachine { - if self.logTy == LogTyPretty { - vmlogger.Debugln(self.logStr) - self.logStr = "" - } - - return self -} - -func (self *DebugVm) Env() Environment { - return self.env -} diff --git a/vm/vm_jit.go b/vm/vm_jit.go index c715abab0b..1cb9652af2 100644 --- a/vm/vm_jit.go +++ b/vm/vm_jit.go @@ -3,12 +3,12 @@ package vm import "math/big" type JitVm struct { - env Environment - backup *DebugVm + env Environment + backup *Vm } func NewJitVm(env Environment) *JitVm { - backupVm := NewDebugVm(env) + backupVm := NewVm(env) return &JitVm{env: env, backup: backupVm} } @@ -28,4 +28,4 @@ func (self *JitVm) Env() Environment { return self.env } -//go is nice \ No newline at end of file +//go is nice From 9b509f64781a5bac37ed5b125ba4214cd7250b59 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 Jan 2015 11:20:12 +0100 Subject: [PATCH 06/15] Print error instead of returning for seed node err Returning an error would indicate a complete failure initialising the Ethereum backend. Instead we should print the message and continue. --- eth/backend.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/backend.go b/eth/backend.go index ad04863092..c3c7d12878 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -245,7 +245,7 @@ func (s *Ethereum) Start(seed bool) error { if seed { logger.Infof("Connect to seed node %v", seedNodeAddress) if err := s.SuggestPeer(seedNodeAddress); err != nil { - return err + logger.Infoln(err) } } From b03614527bea8fee378b406e50071948e6d02f93 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 Jan 2015 11:20:55 +0100 Subject: [PATCH 07/15] VmDebug => StdVm --- core/execution.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/execution.go b/core/execution.go index 756a3ed036..f1e6f83f59 100644 --- a/core/execution.go +++ b/core/execution.go @@ -33,7 +33,7 @@ func (self *Execution) Call(codeAddr []byte, caller vm.ContextRef) ([]byte, erro func (self *Execution) exec(code, contextAddr []byte, caller vm.ContextRef) (ret []byte, err error) { env := self.env - evm := vm.New(env, vm.DebugVmTy) + evm := vm.New(env, vm.StdVmTy) if env.Depth() == vm.MaxCallDepth { caller.ReturnGas(self.Gas, self.price) From 8d1637f5677304b3d540648cf9b4ff9f92ad7f1f Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 Jan 2015 11:21:46 +0100 Subject: [PATCH 08/15] Moved connection errors to DebugDetail level --- p2p/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/p2p/server.go b/p2p/server.go index cfff442f71..4fd1f7d034 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -333,7 +333,7 @@ func (srv *Server) dialLoop() { case desc := <-suggest: // candidate peer found, will dial out asyncronously // if connection fails slot will be released - srvlog.Infof("dial %v (%v)", desc, *slot) + srvlog.DebugDetailf("dial %v (%v)", desc, *slot) go srv.dialPeer(desc, *slot) // we can watch if more peers needed in the next loop slots = srv.peerSlots @@ -355,7 +355,7 @@ func (srv *Server) dialPeer(desc *peerAddr, slot int) { srvlog.Debugf("Dialing %v (slot %d)\n", desc, slot) conn, err := srv.Dialer.Dial(desc.Network(), desc.String()) if err != nil { - srvlog.Errorf("Dial error: %v", err) + srvlog.DebugDetailf("dial error: %v", err) srv.peerSlots <- slot return } From bcb1166e524c3fb1fad66af8ddd4bf3a4161a7dc Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 Jan 2015 11:22:56 +0100 Subject: [PATCH 09/15] Added 0 key proof error Private key \x00\x00...\x00 returns the _exact_ same public key as \x11 \x11...\x11. Currently investigating. --- crypto/crypto_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index b579e6e4e5..41c05a2422 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -6,6 +6,9 @@ import ( "fmt" "testing" "time" + + "github.com/ethereum/go-ethereum/ethutil" + "github.com/obscuren/secp256k1-go" ) // These tests are sanity checks. @@ -47,3 +50,13 @@ func BenchmarkSha3(b *testing.B) { fmt.Println(amount, ":", time.Since(start)) } + +func Test0Key(t *testing.T) { + + key := ethutil.Hex2Bytes("1111111111111111111111111111111111111111111111111111111111111111") + + p, err := secp256k1.GeneratePubKey(key) + addr := Sha3(p[1:])[12:] + fmt.Printf("%x\n", p) + fmt.Printf("%v %x\n", err, addr) +} From f34838688ebd1c775a3b700824e0c6c2e98e5018 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 19 Jan 2015 11:23:17 +0100 Subject: [PATCH 10/15] Minor browser improvements --- cmd/mist/assets/qml/browser.qml | 23 +++++++++++++++++++---- cmd/mist/assets/qml/main.qml | 9 +++++++++ cmd/mist/debugger.go | 4 ++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/cmd/mist/assets/qml/browser.qml b/cmd/mist/assets/qml/browser.qml index 7f0417d463..9cf1fe8be7 100644 --- a/cmd/mist/assets/qml/browser.qml +++ b/cmd/mist/assets/qml/browser.qml @@ -9,6 +9,10 @@ import Ethereum 1.0 Rectangle { id: window + objectName: "browserView" + anchors.fill: parent + color: "#00000000" + property var title: "Browser" property var iconSource: "../browser.png" property var menuItem @@ -106,10 +110,9 @@ Rectangle { anchors { left: back.right right: toggleInspector.left - leftMargin: 5 - rightMargin: 5 + leftMargin: 10 + rightMargin: 10 } - //text: "http://etherian.io" text: webview.url; id: uriNav y: parent.height / 2 - this.height / 2 @@ -136,6 +139,18 @@ Rectangle { } } + // Border + Rectangle { + id: divider + anchors { + left: parent.left + right: parent.right + top: navBar.bottom + } + z: -1 + height: 1 + color: "#CCCCCC" + } WebView { objectName: "webView" @@ -144,7 +159,7 @@ Rectangle { left: parent.left right: parent.right bottom: parent.bottom - top: navBar.bottom + top: divider.bottom } //property var cleanPath: false diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml index e287e384f2..3fee479a0f 100644 --- a/cmd/mist/assets/qml/main.qml +++ b/cmd/mist/assets/qml/main.qml @@ -446,6 +446,14 @@ ApplicationWindow { anchors.fill: parent onClicked: { mainSplit.setView(view, menuItem) + console.log(view); + if (view.objectName === "browserView") { + urlPane.visible = false; + mainView.anchors.top = rootView.top + } else { + urlPane.visible = true; + mainView.anchors.top = divider.bottom + } } } @@ -606,6 +614,7 @@ ApplicationWindow { * Main view ********************/ Rectangle { + id: rootView anchors.right: parent.right anchors.left: menu.right anchors.bottom: parent.bottom diff --git a/cmd/mist/debugger.go b/cmd/mist/debugger.go index b960e52cf7..9df140ab17 100644 --- a/cmd/mist/debugger.go +++ b/cmd/mist/debugger.go @@ -40,7 +40,7 @@ type DebuggerWindow struct { engine *qml.Engine lib *UiLib - vm *vm.DebugVm + vm *vm.Vm Db *Debugger state *state.StateDB @@ -57,7 +57,7 @@ func NewDebuggerWindow(lib *UiLib) *DebuggerWindow { win := component.CreateWindow(nil) - w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &vm.DebugVm{}} + w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &vm.Vm{}} w.Db = NewDebugger(w) return w From 3cf038f300950c5a14b99fb0249ec09f663ef6af Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Mon, 19 Jan 2015 20:24:30 +0100 Subject: [PATCH 11/15] Address pull request comments * Allocate with composite literal instead of new * Remove check of number of bytes read from rand --- crypto/key.go | 6 +----- crypto/key_store_passphrase.go | 12 +++++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/crypto/key.go b/crypto/key.go index 0e7c04275a..d371ad4dc9 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -87,13 +87,9 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) { func NewKey(rand io.Reader) *Key { randBytes := make([]byte, 32) - n, err := rand.Read(randBytes) + _, err := rand.Read(randBytes) if err != nil { panic("key generation: could not read from random source: " + err.Error()) - } else { - if n != 32 { - panic("key generation: read less than required bytes from random source: " + err.Error()) - } } reader := bytes.NewReader(randBytes) _, x, y, err := elliptic.GenerateKey(S256(), reader) diff --git a/crypto/key_store_passphrase.go b/crypto/key_store_passphrase.go index c7f740ae36..68135a4f04 100644 --- a/crypto/key_store_passphrase.go +++ b/crypto/key_store_passphrase.go @@ -89,8 +89,9 @@ type keyStorePassphrase struct { } func NewKeyStorePassphrase(path string) KeyStore2 { - ks := new(keyStorePassphrase) - ks.keysDirPath = path + ks := &keyStorePassphrase{ + keysDirPath : path, + } return ks } @@ -103,9 +104,10 @@ func (ks keyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, er if err != nil { return nil, err } - key = new(Key) - key.Id = keyId - key.PrivateKey = ToECDSA(keyBytes) + key = &Key{ + Id : keyId, + PrivateKey : ToECDSA(keyBytes), + } return key, err } From d48140cab39923bb06bb4df8667efd2df000d17b Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Mon, 19 Jan 2015 22:12:22 +0100 Subject: [PATCH 12/15] Address pull request comments * Further simplify "constructor" function's allocation of structs * Fix formatting --- crypto/key_store_passphrase.go | 9 +++------ crypto/key_store_plain.go | 4 +--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/crypto/key_store_passphrase.go b/crypto/key_store_passphrase.go index 68135a4f04..e30a0a785f 100644 --- a/crypto/key_store_passphrase.go +++ b/crypto/key_store_passphrase.go @@ -89,10 +89,7 @@ type keyStorePassphrase struct { } func NewKeyStorePassphrase(path string) KeyStore2 { - ks := &keyStorePassphrase{ - keysDirPath : path, - } - return ks + return &keyStorePassphrase{path} } func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) { @@ -105,8 +102,8 @@ func (ks keyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, er return nil, err } key = &Key{ - Id : keyId, - PrivateKey : ToECDSA(keyBytes), + Id: keyId, + PrivateKey: ToECDSA(keyBytes), } return key, err } diff --git a/crypto/key_store_plain.go b/crypto/key_store_plain.go index 2aa813f5e8..b6e2a309a9 100644 --- a/crypto/key_store_plain.go +++ b/crypto/key_store_plain.go @@ -54,9 +54,7 @@ func DefaultDataDir() string { } func NewKeyStorePlain(path string) KeyStore2 { - ks := new(keyStorePlain) - ks.keysDirPath = path - return ks + return &keyStorePlain{path} } func (ks keyStorePlain) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) { From 375cc67ba6dbc57e69aa594a6ac26ebc9b3a713a Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 20 Jan 2015 15:48:38 +0100 Subject: [PATCH 13/15] Hide browser bar when coming from a DApp url --- cmd/mist/assets/qml/main.qml | 52 +++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml index 3fee479a0f..7b207cbcc6 100644 --- a/cmd/mist/assets/qml/main.qml +++ b/cmd/mist/assets/qml/main.qml @@ -63,6 +63,17 @@ ApplicationWindow { gui.sendCommand(0) } + function activeView(view, menuItem) { + mainSplit.setView(view, menuItem) + if (view.objectName === "browserView") { + urlPane.visible = false; + mainView.anchors.top = rootView.top + } else { + urlPane.visible = true; + mainView.anchors.top = divider.bottom + } + } + function addViews(view, path, options) { var views = mainSplit.addComponent(view, options) views.menuItem.path = path @@ -284,6 +295,7 @@ ApplicationWindow { } ProgressBar { + visible: false id: downloadIndicator value: 0 objectName: "downloadIndicator" @@ -293,6 +305,7 @@ ApplicationWindow { } Label { + visible: false objectName: "downloadLabel" //y: 7 anchors.left: downloadIndicator.right @@ -445,15 +458,7 @@ ApplicationWindow { MouseArea { anchors.fill: parent onClicked: { - mainSplit.setView(view, menuItem) - console.log(view); - if (view.objectName === "browserView") { - urlPane.visible = false; - mainView.anchors.top = rootView.top - } else { - urlPane.visible = true; - mainView.anchors.top = divider.bottom - } + activeView(view, menuItem); } } @@ -520,14 +525,14 @@ ApplicationWindow { var section; switch(options.section) { case "ethereum": - section = menuDefault; - break; + section = menuDefault; + break; case "legacy": - section = menuLegacy; - break; + section = menuLegacy; + break; default: - section = menuApps; - break; + section = menuApps; + break; } var comp = menuItemTemplate.createObject(section) @@ -614,7 +619,7 @@ ApplicationWindow { * Main view ********************/ Rectangle { - id: rootView + id: rootView anchors.right: parent.right anchors.left: menu.right anchors.bottom: parent.bottom @@ -648,8 +653,7 @@ ApplicationWindow { Keys.onReturnPressed: { if(/^https?/.test(this.text)) { - root.browser.view.open(this.text); - mainSplit.setView(root.browser.view, root.browser.menuItem); + activeView(root.browser.view, root.browser.menuItem); } else { addPlugin(this.text, {close: true, section: "apps"}) } @@ -873,13 +877,13 @@ ApplicationWindow { Component.onCompleted: { pastPeers.insert(0, {text: "poc-8.ethdev.com:30303"}) /* - var ips = eth.pastPeers() - for(var i = 0; i < ips.length; i++) { - pastPeers.append({text: ips.get(i)}) - } + var ips = eth.pastPeers() + for(var i = 0; i < ips.length; i++) { + pastPeers.append({text: ips.get(i)}) + } - pastPeers.insert(0, {text: "poc-7.ethdev.com:30303"}) - */ + pastPeers.insert(0, {text: "poc-7.ethdev.com:30303"}) + */ } } From 9845029a7535672746139084138f3e894e604e44 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 20 Jan 2015 15:49:12 +0100 Subject: [PATCH 14/15] StdVm by default --- cmd/mist/assets/debugger/debugger.qml | 3 ++- cmd/mist/debugger.go | 3 +++ cmd/mist/ui_lib.go | 6 +++--- core/execution.go | 2 +- vm/vm.go | 6 ++++-- vm/vm_jit.go | 2 +- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/cmd/mist/assets/debugger/debugger.qml b/cmd/mist/assets/debugger/debugger.qml index b30d20e6b3..5566e8fcab 100644 --- a/cmd/mist/assets/debugger/debugger.qml +++ b/cmd/mist/assets/debugger/debugger.qml @@ -19,7 +19,7 @@ ApplicationWindow { property alias dataText: rawDataField.text onClosing: { - dbg.Stop() + //dbg.Stop() } menuBar: MenuBar { @@ -353,6 +353,7 @@ ApplicationWindow { ComboBox { + visible: false id: snippets anchors.right: parent.right model: ListModel { diff --git a/cmd/mist/debugger.go b/cmd/mist/debugger.go index 9df140ab17..58d8da7b2e 100644 --- a/cmd/mist/debugger.go +++ b/cmd/mist/debugger.go @@ -267,6 +267,9 @@ type storeVal struct { Key, Value string } +func (self *Debugger) Step(evm vm.VirtualMachine, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, context *vm.Context) { +} + func (self *Debugger) BreakHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool { self.main.Logln("break on instr:", pc) diff --git a/cmd/mist/ui_lib.go b/cmd/mist/ui_lib.go index f3559183e6..c88c4dab6b 100644 --- a/cmd/mist/ui_lib.go +++ b/cmd/mist/ui_lib.go @@ -212,16 +212,16 @@ func (self *UiLib) StartDbWithContractAndData(contractHash, data string) { dbWindow := NewDebuggerWindow(self) object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash)) if len(object.Code) > 0 { - dbWindow.SetCode("0x" + ethutil.Bytes2Hex(object.Code)) + dbWindow.SetCode(ethutil.Bytes2Hex(object.Code)) } - dbWindow.SetData("0x" + data) + dbWindow.SetData(data) dbWindow.Show() } func (self *UiLib) StartDbWithCode(code string) { dbWindow := NewDebuggerWindow(self) - dbWindow.SetCode("0x" + code) + dbWindow.SetCode(code) dbWindow.Show() } diff --git a/core/execution.go b/core/execution.go index f1e6f83f59..cad4e84aa2 100644 --- a/core/execution.go +++ b/core/execution.go @@ -33,7 +33,7 @@ func (self *Execution) Call(codeAddr []byte, caller vm.ContextRef) ([]byte, erro func (self *Execution) exec(code, contextAddr []byte, caller vm.ContextRef) (ret []byte, err error) { env := self.env - evm := vm.New(env, vm.StdVmTy) + evm := vm.New(env) if env.Depth() == vm.MaxCallDepth { caller.ReturnGas(self.Gas, self.price) diff --git a/vm/vm.go b/vm/vm.go index 6aa39797ec..37e4249f5b 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -17,7 +17,6 @@ type Vm struct { err error - // Debugging Dbg Debugger BreakPoints []int64 @@ -27,7 +26,7 @@ type Vm struct { Recoverable bool } -func NewVm(env Environment) *Vm { +func New(env Environment) *Vm { lt := LogTyPretty if ethutil.Config.Diff { lt = LogTyDiff @@ -111,6 +110,9 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I op = context.GetOp(pc) self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.Len()) + if self.Dbg != nil { + //self.Dbg.Step(self, op, mem, stack, context) + } newMemSize, gas := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) diff --git a/vm/vm_jit.go b/vm/vm_jit.go index 1cb9652af2..0882bcf0cb 100644 --- a/vm/vm_jit.go +++ b/vm/vm_jit.go @@ -8,7 +8,7 @@ type JitVm struct { } func NewJitVm(env Environment) *JitVm { - backupVm := NewVm(env) + backupVm := New(env) return &JitVm{env: env, backup: backupVm} } From 87f50659db7a4bf194769b05f541d2ccf02f4fc8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 20 Jan 2015 18:42:29 +0100 Subject: [PATCH 15/15] fixed url bug in browser --- cmd/mist/assets/qml/browser.qml | 5 ----- whisper/whisper_test.go | 9 --------- 2 files changed, 14 deletions(-) diff --git a/cmd/mist/assets/qml/browser.qml b/cmd/mist/assets/qml/browser.qml index 9cf1fe8be7..a89c3c97d3 100644 --- a/cmd/mist/assets/qml/browser.qml +++ b/cmd/mist/assets/qml/browser.qml @@ -162,11 +162,6 @@ Rectangle { top: divider.bottom } - //property var cleanPath: false - onNavigationRequested: { - window.open(request.url.toString()); - } - function injectJs(js) { webview.experimental.navigatorQtObjectEnabled = true; webview.experimental.evaluateJavaScript(js) diff --git a/whisper/whisper_test.go b/whisper/whisper_test.go index 107cb8c978..c5ad730216 100644 --- a/whisper/whisper_test.go +++ b/whisper/whisper_test.go @@ -6,15 +6,6 @@ import ( "time" ) -func TestKeyManagement(t *testing.T) { - whisper := New() - - key := whisper.NewIdentity() - if !whisper.HasIdentity(key) { - t.Error("expected whisper to have identify") - } -} - func TestEvent(t *testing.T) { res := make(chan *Message, 1) whisper := New()