From 988a366a185a1171f54d893c0a4deb0cbd22f2d8 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Fri, 13 Feb 2015 14:28:46 +0100 Subject: [PATCH 1/2] Add accounts association and use address to refer to accounts --- accounts/account_manager.go | 82 +++++++++++++++++++------------------ accounts/accounts_test.go | 7 +++- 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index da0bd89004..a42a846a9b 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -35,61 +35,65 @@ package accounts import ( crand "crypto/rand" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/syndtr/goleveldb/leveldb" ) -// TODO: better name for this struct? +/* type Account struct { - Address []byte + Address []byte + AssociatedAddress []byte // e.g. a wallet contract "owned" by the account } +*/ type AccountManager struct { - keyStore crypto.KeyStore2 + keyStore crypto.KeyStore2 + accountsDb *ethdb.LDBDatabase } // TODO: get key by addr - modify KeyStore2 GetKey to work with addr // TODO: pass through passphrase for APIs which require access to private key? -func NewAccountManager(keyStore crypto.KeyStore2) AccountManager { - am := &AccountManager{ - keyStore: keyStore, +func NewAccountManager(keyStore crypto.KeyStore2) (AccountManager, error) { + db, err := ethdb.NewLDBDatabase("accounts") + if err != nil { + panic(err) } - return *am + am := &AccountManager{ + keyStore: keyStore, + accountsDb: db, + } + return *am, nil } -func (am *AccountManager) Sign(fromAccount *Account, keyAuth string, toSign []byte) (signature []byte, err error) { - key, err := am.keyStore.GetKey(fromAccount.Address, keyAuth) +func (am AccountManager) NewAccount(auth string) ([]byte, error) { + newKey, err := am.keyStore.GenerateNewKey(crand.Reader, auth) if err != nil { return nil, err } - signature, err = crypto.Sign(toSign, key.PrivateKey) + return newKey.Address, err +} + +func (am AccountManager) AssociateContract(ownerAddr []byte, associateAddr []byte) error { + am.accountsDb.Put(ownerAddr, associateAddr) + am.accountsDb.Put(associateAddr, ownerAddr) + return nil +} + +func (am *AccountManager) GetAssociatedAddr(addr []byte) ([]byte, error) { + res, err := am.accountsDb.Get(addr) + if err == leveldb.ErrNotFound { + return []byte{}, nil + } else { + return res, nil + } +} + +func (am *AccountManager) Sign(fromAddr []byte, keyAuth string, toSign []byte) ([]byte, error) { + key, err := am.keyStore.GetKey(fromAddr, keyAuth) + if err != nil { + return nil, err + } + signature, err := crypto.Sign(toSign, key.PrivateKey) return signature, err } - -func (am AccountManager) NewAccount(auth string) (*Account, error) { - key, err := am.keyStore.GenerateNewKey(crand.Reader, auth) - if err != nil { - return nil, err - } - ua := &Account{ - Address: key.Address, - } - return ua, err -} - -// set of accounts == set of keys in given key store -// TODO: do we need persistence of accounts as well? -func (am *AccountManager) Accounts() ([]Account, error) { - addresses, err := am.keyStore.GetKeyAddresses() - if err != nil { - return nil, err - } - - accounts := make([]Account, len(addresses)) - - for i, addr := range addresses { - accounts[i] = Account{ - Address: addr, - } - } - return accounts, err -} diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go index da9406ebe8..52ef1fb16d 100644 --- a/accounts/accounts_test.go +++ b/accounts/accounts_test.go @@ -2,12 +2,17 @@ package accounts import ( "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" "testing" ) +func init() { + ethutil.ReadConfig("/tmp/ethtest", "/tmp", "ETH") +} + func TestAccountManager(t *testing.T) { ks := crypto.NewKeyStorePlain(crypto.DefaultDataDir()) - am := NewAccountManager(ks) + am, _ := NewAccountManager(ks) pass := "" // not used but required by API a1, err := am.NewAccount(pass) toSign := crypto.GetEntropyCSPRNG(32) From 4d1a9cb8356254a731a3369ad8654835540489ce Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Sun, 15 Feb 2015 00:39:50 +0100 Subject: [PATCH 2/2] Add first take on new transact and wallet creation RPC calls --- core/types/transaction.go | 8 ++++ eth/backend.go | 14 +++++++ rpc/args.go | 10 +++++ rpc/http/server.go | 2 +- rpc/packages.go | 16 +++++++- rpc/util.go | 2 +- xeth/xeth.go | 80 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 129 insertions(+), 3 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index 7a1d6104e4..7d34c86f47 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -129,6 +129,7 @@ func (tx *Transaction) sender() []byte { return crypto.Sha3(pubkey[1:])[12:] } +// TODO: deprecate after new accounts & key stores are integrated func (tx *Transaction) Sign(privk []byte) error { sig := tx.Signature(privk) @@ -140,6 +141,13 @@ func (tx *Transaction) Sign(privk []byte) error { return nil } +func (tx *Transaction) SetSignatureValues(sig []byte) error { + tx.R = sig[:32] + tx.S = sig[32:64] + tx.V = uint64(sig[64] + 27) + return nil +} + func (tx *Transaction) SignECDSA(key *ecdsa.PrivateKey) error { return tx.Sign(crypto.FromECDSA(key)) } diff --git a/eth/backend.go b/eth/backend.go index 43e7574358..324889261a 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -5,6 +5,7 @@ import ( "net" "sync" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -54,6 +55,7 @@ type Ethereum struct { blockProcessor *core.BlockProcessor txPool *core.TxPool chainManager *core.ChainManager + accountManager *accounts.AccountManager blockPool *BlockPool whisper *whisper.Whisper @@ -90,6 +92,7 @@ func New(config *Config) (*Ethereum, error) { return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, ProtocolVersion, ethutil.Config.ExecPath+"/database") } + // TODO: remove keymanager & keyring once accountManager is integrated // Create new keymanager var keyManager *crypto.KeyManager switch config.KeyStore { @@ -121,6 +124,13 @@ func New(config *Config) (*Ethereum, error) { } eth.chainManager = core.NewChainManager(db, eth.EventMux()) + // TODO: add config flag and case on plain/protected key store + ks := crypto.NewKeyStorePlain(crypto.DefaultDataDir()) + am, err := accounts.NewAccountManager(ks) + if err != nil { + return nil, err + } + eth.accountManager = &am eth.txPool = core.NewTxPool(eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(db, eth.txPool, eth.chainManager, eth.EventMux()) eth.chainManager.SetProcessor(eth.blockProcessor) @@ -170,6 +180,10 @@ func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager } +func (s *Ethereum) AccountManager() *accounts.AccountManager { + return s.accountManager +} + func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor } diff --git a/rpc/args.go b/rpc/args.go index 84b076d4ad..bc393fd647 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -37,6 +37,16 @@ type NewTxArgs struct { Data string `json:"data"` } +type NewTxArgs2 struct { + From string `json:"from"` + Pass string `json:"pass"` + To string `json:"to"` + Value string `json:"value"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Data string `json:"data"` +} + type PushTxArgs struct { Tx string `json:"tx"` } diff --git a/rpc/http/server.go b/rpc/http/server.go index 10c8fa8138..c642021370 100644 --- a/rpc/http/server.go +++ b/rpc/http/server.go @@ -9,7 +9,7 @@ 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. + GNU General Public License for more details. You should have received a copy of the GNU General Public License along with go-ethereum. If not, see . diff --git a/rpc/packages.go b/rpc/packages.go index ac3127356a..e731ebd1a0 100644 --- a/rpc/packages.go +++ b/rpc/packages.go @@ -9,7 +9,7 @@ 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. + GNU General Public License for more details. You should have received a copy of the GNU General Public License along with go-ethereum. If not, see . @@ -154,6 +154,20 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { return nil } +func (p *EthereumApi) Transact2(args *NewTxArgs2, reply *interface{}) error { + if len(args.Gas) == 0 { + args.Gas = defaultGas + } + + if len(args.GasPrice) == 0 { + args.GasPrice = defaultGasPrice + } + + result, _ := p.xeth.Transact2(args.From, args.Pass, args.To, args.Value, args.Gas, args.GasPrice, args.Data) + *reply = result + return nil +} + func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error { result, err := p.xeth.Call( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data) if err != nil { diff --git a/rpc/util.go b/rpc/util.go index 679d83754d..982b777ab8 100644 --- a/rpc/util.go +++ b/rpc/util.go @@ -9,7 +9,7 @@ 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. + GNU General Public License for more details. You should have received a copy of the GNU General Public License along with go-ethereum. If not, see . diff --git a/xeth/xeth.go b/xeth/xeth.go index 0e71a0ed92..c7b0e1c700 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -8,6 +8,7 @@ import ( "bytes" "encoding/json" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -25,6 +26,7 @@ var pipelogger = logger.NewLogger("XETH") type Backend interface { BlockProcessor() *core.BlockProcessor ChainManager() *core.ChainManager + AccountManager() *accounts.AccountManager TxPool() *core.TxPool PeerCount() int IsMining() bool @@ -41,6 +43,7 @@ type XEth struct { eth Backend blockProcessor *core.BlockProcessor chainManager *core.ChainManager + accountManager *accounts.AccountManager state *State whisper *Whisper } @@ -50,6 +53,7 @@ func New(eth Backend) *XEth { eth: eth, blockProcessor: eth.BlockProcessor(), chainManager: eth.ChainManager(), + accountManager: eth.AccountManager(), whisper: NewWhisper(eth.Whisper()), } xeth.state = NewState(xeth) @@ -301,3 +305,79 @@ func (self *XEth) Transact(toStr, valueStr, gasStr, gasPriceStr, codeStr string) return toHex(tx.Hash()), nil } + +func (self *XEth) Transact2(fromStr, fromPassStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { + + var ( + from []byte + to []byte + value = ethutil.NewValue(valueStr) + gas = ethutil.NewValue(gasStr) + price = ethutil.NewValue(gasPriceStr) + data []byte + contractCreation bool + ) + + from = fromHex(fromStr) + data = fromHex(codeStr) + to = fromHex(toStr) + if len(to) == 0 { + contractCreation = true + } + + var tx *types.Transaction + if contractCreation { + tx = types.NewContractCreationTx(value.BigInt(), gas.BigInt(), price.BigInt(), data) + } else { + tx = types.NewTransactionMessage(to, value.BigInt(), gas.BigInt(), price.BigInt(), data) + } + + state := self.chainManager.TransState() + nonce := state.GetNonce(from) + + tx.SetNonce(nonce) + sig, err := self.accountManager.Sign(from, fromPassStr, tx.Hash()) + if err != nil { + return "", err + } + tx.SetSignatureValues(sig) + // Do some pre processing for our "pre" events and hooks + block := self.chainManager.NewBlock(from) + coinbase := state.GetOrNewStateObject(from) + coinbase.SetGasPool(block.GasLimit()) + self.blockProcessor.ApplyTransactions(coinbase, state, block, types.Transactions{tx}, true) + + err = self.eth.TxPool().Add(tx) + if err != nil { + return "", err + } + state.SetNonce(from, nonce+1) + + if contractCreation { + addr := core.AddressFromMessage(tx) + pipelogger.Infof("Contract addr %x\n", addr) + } + + if types.IsContractAddr(to) { + return toHex(core.AddressFromMessage(tx)), nil + } + + return toHex(tx.Hash()), nil +} + +// TODO: clarify what is amount, gas and gas price when creating a new wallet contract? +func (self *XEth) NewWallet(fromStr, fromPassStr, valueStr, gasStr, gasPriceStr, walletContractDataStr string) (newAddrHex string, newWalletContractAddrHex string, err error) { + from := fromHex(fromStr) + newAddr, err := self.accountManager.NewAccount(fromPassStr) + if err != nil { + return "", "", err + } + err = self.accountManager.AssociateContract(from, newAddr) + if err != nil { + return "", "", err + } + + // TODO: how to add newAddr as authorised signer to the wallet contract code? + newWalletContractAddrHex, err = self.Transact2(fromStr, fromPassStr, "", valueStr, gasStr, gasPriceStr, walletContractDataStr) + return toHex(newAddr), newWalletContractAddrHex, nil +}