diff options
Diffstat (limited to 'internal/ethapi/api.go')
-rw-r--r-- | internal/ethapi/api.go | 148 |
1 files changed, 136 insertions, 12 deletions
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 9a97be25f..0e8e905aa 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -39,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/util" "golang.org/x/net/context" ) @@ -286,6 +287,66 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs return submitTransaction(ctx, s.b, tx, signature) } +// signHash is a helper function that calculates a hash for the given message that can be +// safely used to calculate a signature from. The hash is calulcated with: +// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}). +func signHash(message string) []byte { + data := common.FromHex(message) + // Give context to the signed message. This prevents an adversery to sign a tx. + // It has no cryptographic purpose. + msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) + // Always hash, this prevents choosen plaintext attacks that can extract key information + return crypto.Keccak256([]byte(msg)) +} + +// Sign calculates an Ethereum ECDSA signature for: +// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message)) +// +// The key used to calculate the signature is decrypted with the given password. +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign +func (s *PrivateAccountAPI) Sign(ctx context.Context, message string, addr common.Address, passwd string) (string, error) { + hash := signHash(message) + signature, err := s.b.AccountManager().SignWithPassphrase(addr, passwd, hash) + if err != nil { + return "0x", err + } + return common.ToHex(signature), nil +} + +// EcRecover returns the address for the account that was used to create the signature. +// Note, this function is compatible with eth_sign and personal_sign. As such it recovers +// the address of: +// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message}) +// addr = ecrecover(hash, signature) +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover +func (s *PrivateAccountAPI) EcRecover(ctx context.Context, message string, signature string) (common.Address, error) { + var ( + hash = signHash(message) + sig = common.FromHex(signature) + ) + + if len(sig) != 65 { + return common.Address{}, fmt.Errorf("signature must be 65 bytes long") + } + + // see crypto.Ecrecover description + if sig[64] == 27 || sig[64] == 28 { + sig[64] -= 27 + } + + rpk, err := crypto.Ecrecover(hash, sig) + if err != nil { + return common.Address{}, err + } + + pubKey := crypto.ToECDSAPub(rpk) + recoveredAddr := crypto.PubkeyToAddress(*pubKey) + + return recoveredAddr, nil +} + // SignAndSendTransaction was renamed to SendTransaction. This method is deprecated // and will be removed in the future. It primary goal is to give clients time to update. func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) { @@ -597,7 +658,7 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx "gasUsed": rpc.NewHexNumber(head.GasUsed), "timestamp": rpc.NewHexNumber(head.Time), "transactionsRoot": head.TxHash, - "receiptRoot": head.ReceiptHash, + "receiptsRoot": head.ReceiptHash, } if inclTx { @@ -699,6 +760,16 @@ func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransacti return nil, nil } +// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. +func newRPCRawTransactionFromBlockIndex(b *types.Block, txIndex int) (rpc.HexBytes, error) { + if txIndex >= 0 && txIndex < len(b.Transactions()) { + tx := b.Transactions()[txIndex] + return rlp.EncodeToBytes(tx) + } + + return nil, nil +} + // newRPCTransaction returns a transaction that will serialize to the RPC representation. func newRPCTransaction(b *types.Block, txHash common.Hash) (*RPCTransaction, error) { for idx, tx := range b.Transactions() { @@ -770,6 +841,22 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context return nil, nil } +// GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. +func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (rpc.HexBytes, error) { + if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + return newRPCRawTransactionFromBlockIndex(block, index.Int()) + } + return nil, nil +} + +// GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index. +func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (rpc.HexBytes, error) { + if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + return newRPCRawTransactionFromBlockIndex(block, index.Int()) + } + return nil, nil +} + // GetTransactionCount returns the number of transactions the given address has sent for the given block number func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) { state, _, err := s.b.StateAndHeaderByNumber(blockNr) @@ -835,6 +922,21 @@ func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txH return nil, nil } +// GetRawTransactionByHash returns the bytes of the transaction for the given hash. +func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txHash common.Hash) (rpc.HexBytes, error) { + var tx *types.Transaction + var err error + + if tx, _, err = getTransaction(s.b.ChainDb(), s.b, txHash); err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } else if tx == nil { + return nil, nil + } + + return rlp.EncodeToBytes(tx) +} + // GetTransactionReceipt returns the transaction receipt for the given transaction hash. func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (map[string]interface{}, error) { receipt := core.GetReceipt(s.b.ChainDb(), txHash) @@ -887,7 +989,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma // sign is a helper function that signs a transaction with the private key of the given address. func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { - signature, err := s.b.AccountManager().Sign(addr, tx.SigHash().Bytes()) + signature, err := s.b.AccountManager().SignEthereum(addr, tx.SigHash().Bytes()) if err != nil { return nil, err } @@ -969,7 +1071,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } - signature, err := s.b.AccountManager().Sign(args.From, tx.SigHash().Bytes()) + signature, err := s.b.AccountManager().SignEthereum(args.From, tx.SigHash().Bytes()) if err != nil { return common.Hash{}, err } @@ -1003,11 +1105,16 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod return tx.Hash().Hex(), nil } -// Sign signs the given hash using the key that matches the address. The key must be -// unlocked in order to sign the hash. -func (s *PublicTransactionPoolAPI) Sign(addr common.Address, hash common.Hash) (string, error) { - signature, error := s.b.AccountManager().Sign(addr, hash[:]) - return common.ToHex(signature), error +// Sign calculates an ECDSA signature for: +// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message). +// +// The account associated with addr must be unlocked. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign +func (s *PublicTransactionPoolAPI) Sign(addr common.Address, message string) (string, error) { + hash := signHash(message) + signature, err := s.b.AccountManager().SignEthereum(addr, hash) + return common.ToHex(signature), err } // SignTransactionArgs represents the arguments to sign a transaction. @@ -1170,8 +1277,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { // Resend accepts an existing transaction and a new gas price and limit. It will remove the given transaction from the // pool and reinsert it with the new gas price and limit. -func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx *Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) { - +func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) { pending := s.b.GetPoolTransactions() for _, p := range pending { if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() { @@ -1184,9 +1290,9 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx *Tx, gasPrice, var newTx *types.Transaction if tx.tx.To() == nil { - newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) + newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasLimit.BigInt(), gasPrice.BigInt(), tx.tx.Data()) } else { - newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) + newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasLimit.BigInt(), gasPrice.BigInt(), tx.tx.Data()) } signedTx, err := s.sign(tx.From, newTx) @@ -1281,6 +1387,24 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { return ldb.LDB().GetProperty(property) } +func (api *PrivateDebugAPI) ChaindbCompact() error { + ldb, ok := api.b.ChainDb().(interface { + LDB() *leveldb.DB + }) + if !ok { + return fmt.Errorf("chaindbCompact does not work for memory databases") + } + for b := byte(0); b < 255; b++ { + glog.V(logger.Info).Infof("compacting chain DB range 0x%0.2X-0x%0.2X", b, b+1) + err := ldb.LDB().CompactRange(util.Range{Start: []byte{b}, Limit: []byte{b + 1}}) + if err != nil { + glog.Errorf("compaction error: %v", err) + return err + } + } + return nil +} + // SetHead rewinds the head of the blockchain to a previous block. func (api *PrivateDebugAPI) SetHead(number rpc.HexNumber) { api.b.SetHead(uint64(number.Int64())) |