mirror of https://github.com/poanetwork/quorum.git
Adding QuorumPrivateTxSigner for signing and recovering private txs. (#767)
Refactor private transaction signing and add a QuorumPrivateTxSigner for signing and recovering private txs.
This commit is contained in:
parent
0b12c423e0
commit
e0564f7b41
|
@ -111,7 +111,7 @@ type Wallet interface {
|
|||
// about which fields or actions are needed. The user may retry by providing
|
||||
// the needed details via SignTxWithPassphrase, or by other means (e.g. unlock
|
||||
// the account in a keystore).
|
||||
SignTx(account Account, tx *types.Transaction, chainID *big.Int, isQuorum bool) (*types.Transaction, error)
|
||||
SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
|
||||
|
||||
// SignHashWithPassphrase requests the wallet to sign the given hash with the
|
||||
// given passphrase as extra authentication information.
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
crand "crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -268,7 +269,7 @@ func (ks *KeyStore) SignHash(a accounts.Account, hash []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
// SignTx signs the given transaction with the requested account.
|
||||
func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *big.Int, isQuorum bool) (*types.Transaction, error) {
|
||||
func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||
// Look up the key to sign with and abort if it cannot be found
|
||||
ks.mu.RLock()
|
||||
defer ks.mu.RUnlock()
|
||||
|
@ -277,8 +278,15 @@ func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *b
|
|||
if !found {
|
||||
return nil, ErrLocked
|
||||
}
|
||||
|
||||
// start quorum specific
|
||||
if tx.IsPrivate() {
|
||||
log.Info("Private transaction signing with QuorumPrivateTxSigner")
|
||||
return types.SignTx(tx, types.QuorumPrivateTxSigner{}, unlockedKey.PrivateKey)
|
||||
} // End quorum specific
|
||||
|
||||
// Depending on the presence of the chain ID, sign with EIP155 or homestead
|
||||
if chainID != nil && !tx.IsPrivate() {
|
||||
if chainID != nil {
|
||||
return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey)
|
||||
}
|
||||
return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey)
|
||||
|
@ -305,6 +313,9 @@ func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string,
|
|||
}
|
||||
defer zeroKey(key.PrivateKey)
|
||||
|
||||
if tx.IsPrivate() {
|
||||
return types.SignTx(tx, types.QuorumPrivateTxSigner{}, key.PrivateKey)
|
||||
}
|
||||
// Depending on the presence of the chain ID, sign with EIP155 or homestead
|
||||
if chainID != nil {
|
||||
return types.SignTx(tx, types.NewEIP155Signer(chainID), key.PrivateKey)
|
||||
|
|
|
@ -98,7 +98,7 @@ func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte
|
|||
// with the given account. If the wallet does not wrap this particular account,
|
||||
// an error is returned to avoid account leakage (even though in theory we may
|
||||
// be able to sign via our shared keystore backend).
|
||||
func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int, isQuorum bool) (*types.Transaction, error) {
|
||||
func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||
// Make sure the requested account is contained within
|
||||
if account.Address != w.account.Address {
|
||||
return nil, accounts.ErrUnknownAccount
|
||||
|
@ -107,7 +107,7 @@ func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction,
|
|||
return nil, accounts.ErrUnknownAccount
|
||||
}
|
||||
// Account seems valid, request the keystore to sign
|
||||
return w.keystore.SignTx(account, tx, chainID, isQuorum)
|
||||
return w.keystore.SignTx(account, tx, chainID)
|
||||
}
|
||||
|
||||
// SignHashWithPassphrase implements accounts.Wallet, attempting to sign the
|
||||
|
|
|
@ -509,12 +509,12 @@ func (w *wallet) SignHash(account accounts.Account, hash []byte) ([]byte, error)
|
|||
// Note, if the version of the Ethereum application running on the Ledger wallet is
|
||||
// too old to sign EIP-155 transactions, but such is requested nonetheless, an error
|
||||
// will be returned opposed to silently signing in Homestead mode.
|
||||
func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int, isQuorum bool) (*types.Transaction, error) {
|
||||
func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||
w.stateLock.RLock() // Comms have own mutex, this is for the state fields
|
||||
defer w.stateLock.RUnlock()
|
||||
|
||||
if isQuorum {
|
||||
return nil, errors.New("Signing Quorum transactions with a USB wallet not yet supported")
|
||||
if tx.IsPrivate() {
|
||||
return nil, errors.New("Signing Quorum Private transactions with a USB wallet not yet supported")
|
||||
}
|
||||
|
||||
// If the wallet is closed, abort
|
||||
|
@ -563,5 +563,5 @@ func (w *wallet) SignHashWithPassphrase(account accounts.Account, passphrase str
|
|||
// transaction with the given account using passphrase as extra authentication.
|
||||
// Since USB wallets don't rely on passphrases, these are silently ignored.
|
||||
func (w *wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||
return w.SignTx(account, tx, chainID, false)
|
||||
return w.SignTx(account, tx, chainID)
|
||||
}
|
||||
|
|
|
@ -473,7 +473,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
|
|||
amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil))
|
||||
|
||||
tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, 21000, f.price, nil)
|
||||
signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainID, false)
|
||||
signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainID)
|
||||
if err != nil {
|
||||
f.lock.Unlock()
|
||||
if err = sendError(conn, err); err != nil {
|
||||
|
|
|
@ -47,7 +47,12 @@ func (cg *callHelper) MakeCall(private bool, key *ecdsa.PrivateKey, to common.Ad
|
|||
cg.header.GasLimit = 4700000
|
||||
|
||||
signer := types.MakeSigner(params.QuorumTestChainConfig, cg.header.Number)
|
||||
if private {
|
||||
signer = types.QuorumPrivateTxSigner{}
|
||||
}
|
||||
|
||||
tx, err := types.SignTx(types.NewTransaction(cg.TxNonce(from), to, new(big.Int), 1000000, new(big.Int), input), signer, key)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -60,15 +65,13 @@ func (cg *callHelper) MakeCall(private bool, key *ecdsa.PrivateKey, to common.Ad
|
|||
publicState, privateState := cg.PublicState, cg.PrivateState
|
||||
if !private {
|
||||
privateState = publicState
|
||||
} else {
|
||||
tx.SetPrivate()
|
||||
}
|
||||
|
||||
// TODO(joel): can we just pass nil instead of bc?
|
||||
bc, _ := NewBlockChain(cg.db, nil, params.QuorumTestChainConfig, ethash.NewFaker(), vm.Config{}, nil)
|
||||
context := NewEVMContext(msg, &cg.header, bc, &from)
|
||||
vmenv := vm.NewEVM(context, publicState, privateState, params.QuorumTestChainConfig, vm.Config{})
|
||||
_, _, _, err = ApplyMessage(vmenv, msg, cg.gp)
|
||||
sender := vm.AccountRef(msg.From())
|
||||
vmenv.Call(sender, to, msg.Data(), 100000000, new(big.Int))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -263,13 +263,15 @@ func runTessera() (*osExec.Cmd, error) {
|
|||
}
|
||||
|
||||
// 600a600055600060006001a1
|
||||
// [1] PUSH1 0x0a (store value)
|
||||
// [3] PUSH1 0x00 (store addr)
|
||||
// [4] SSTORE
|
||||
// [6] PUSH1 0x00
|
||||
// [8] PUSH1 0x00
|
||||
// [10] PUSH1 0x01
|
||||
// [11] LOG1
|
||||
// 60 0a, 60 00, 55, 60 00, 60 00, 60 01, a1
|
||||
// [1] (0x60) PUSH1 0x0a (store value)
|
||||
// [3] (0x60) PUSH1 0x00 (store addr)
|
||||
// [4] (0x55) SSTORE (Store (k-00,v-a))
|
||||
|
||||
// [6] (0x60) PUSH1 0x00
|
||||
// [8] (0x60) PUSH1 0x00
|
||||
// [10](0x60) PUSH1 0x01
|
||||
// [11](0xa1) LOG1 offset(0x01), len(0x00), topic(0x00)
|
||||
//
|
||||
// Store then log
|
||||
func TestPrivateTransaction(t *testing.T) {
|
||||
|
@ -294,10 +296,12 @@ func TestPrivateTransaction(t *testing.T) {
|
|||
|
||||
prvContractAddr := common.Address{1}
|
||||
pubContractAddr := common.Address{2}
|
||||
// SSTORE (K,V) SSTORE(0, 10): 600a600055
|
||||
// +
|
||||
// LOG1 OFFSET LEN TOPIC, LOG1 (a1) 01, 00, 00: 600060006001a1
|
||||
privateState.SetCode(prvContractAddr, common.Hex2Bytes("600a600055600060006001a1"))
|
||||
privateState.SetState(prvContractAddr, common.Hash{}, common.Hash{9})
|
||||
// SSTORE (K,V) SSTORE(0, 14): 6014600055
|
||||
publicState.SetCode(pubContractAddr, common.Hex2Bytes("6014600055"))
|
||||
publicState.SetState(pubContractAddr, common.Hash{}, common.Hash{19})
|
||||
|
||||
if publicState.Exist(prvContractAddr) {
|
||||
t.Error("didn't expect private contract address to exist on public state")
|
||||
|
@ -305,6 +309,7 @@ func TestPrivateTransaction(t *testing.T) {
|
|||
|
||||
// Private transaction 1
|
||||
err = helper.MakeCall(true, key, prvContractAddr, nil)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ func deriveSigner(V *big.Int) Signer {
|
|||
// joel: this is one of the two places we used a wrong signer to print txes
|
||||
if V.Sign() != 0 && isProtectedV(V) {
|
||||
return NewEIP155Signer(deriveChainId(V))
|
||||
} else if isPrivate(V) {
|
||||
return QuorumPrivateTxSigner{}
|
||||
} else {
|
||||
return HomesteadSigner{}
|
||||
}
|
||||
|
@ -404,7 +406,7 @@ func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transa
|
|||
for from, accTxs := range txs {
|
||||
// Ensure the sender address is from the signer
|
||||
acc, err := Sender(signer, accTxs[0])
|
||||
if (err == nil) {
|
||||
if err == nil {
|
||||
heads = append(heads, accTxs[0])
|
||||
txs[acc] = accTxs[1:]
|
||||
} else {
|
||||
|
@ -498,6 +500,14 @@ func (tx *Transaction) IsPrivate() bool {
|
|||
return tx.data.V.Uint64() == 37 || tx.data.V.Uint64() == 38
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicates that a transaction is private, but doesn't necessarily set the correct v value, as it can be called on
|
||||
* an unsigned transaction.
|
||||
* pre homestead signer, all v values were v=27 or v=28, with EIP155Signer that change,
|
||||
* but SetPrivate() is also used on unsigned transactions to temporarily set the v value to indicate
|
||||
* the transaction is intended to be private, and so that the correct signer can be selected. The signer will correctly
|
||||
* set the valid v value (37 or 38): This helps minimize changes vs upstream go-ethereum code.
|
||||
*/
|
||||
func (tx *Transaction) SetPrivate() {
|
||||
if tx.IsPrivate() {
|
||||
return
|
||||
|
|
|
@ -126,7 +126,7 @@ var big8 = big.NewInt(8)
|
|||
|
||||
func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
|
||||
if tx.IsPrivate() {
|
||||
return HomesteadSigner{}.Sender(tx)
|
||||
return QuorumPrivateTxSigner{}.Sender(tx)
|
||||
}
|
||||
if !tx.Protected() {
|
||||
return HomesteadSigner{}.Sender(tx)
|
||||
|
@ -136,16 +136,12 @@ func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
|
|||
}
|
||||
V := new(big.Int).Sub(tx.data.V, s.chainIdMul)
|
||||
V.Sub(V, big8)
|
||||
return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true, tx.IsPrivate())
|
||||
return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true)
|
||||
}
|
||||
|
||||
// SignatureValues returns signature values. This signature
|
||||
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||
func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||
if tx.IsPrivate() {
|
||||
return HomesteadSigner{}.SignatureValues(tx, sig)
|
||||
}
|
||||
|
||||
R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
|
@ -187,7 +183,7 @@ func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v
|
|||
}
|
||||
|
||||
func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true, tx.IsPrivate())
|
||||
return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true)
|
||||
}
|
||||
|
||||
type FrontierSigner struct{}
|
||||
|
@ -205,11 +201,7 @@ func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *
|
|||
}
|
||||
r = new(big.Int).SetBytes(sig[:32])
|
||||
s = new(big.Int).SetBytes(sig[32:64])
|
||||
if tx.IsPrivate() {
|
||||
v = new(big.Int).SetBytes([]byte{sig[64] + 37})
|
||||
} else {
|
||||
v = new(big.Int).SetBytes([]byte{sig[64] + 27})
|
||||
}
|
||||
v = new(big.Int).SetBytes([]byte{sig[64] + 27})
|
||||
return r, s, v, nil
|
||||
}
|
||||
|
||||
|
@ -227,15 +219,16 @@ func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
|
|||
}
|
||||
|
||||
func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false, tx.IsPrivate())
|
||||
return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false)
|
||||
}
|
||||
|
||||
func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool, isPrivate bool) (common.Address, error) {
|
||||
func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) {
|
||||
if Vb.BitLen() > 8 {
|
||||
return common.Address{}, ErrInvalidSig
|
||||
}
|
||||
var offset uint64
|
||||
if isPrivate {
|
||||
// private transaction has a v value of 37 or 38
|
||||
if isPrivate(Vb) {
|
||||
offset = 37
|
||||
} else {
|
||||
offset = 27
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// Signs with Homestead
|
||||
// obtains sender from EIP55Signer
|
||||
type QuorumPrivateTxSigner struct{ HomesteadSigner }
|
||||
|
||||
func (s QuorumPrivateTxSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
return HomesteadSigner{}.Sender(tx)
|
||||
}
|
||||
|
||||
// SignatureValues returns signature values. This signature
|
||||
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||
func (qs QuorumPrivateTxSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||
r, s, v, err := HomesteadSigner{}.SignatureValues(tx, sig)
|
||||
// update v for private transaction marker: needs to be 37 (0+37) or 38 (1+37) for a private transaction.
|
||||
v = new(big.Int).SetBytes([]byte{sig[64] + 37})
|
||||
return r, s, v, nil
|
||||
}
|
||||
|
||||
// Hash returns the hash to be signed by the sender.
|
||||
// It does not uniquely identify the transaction.
|
||||
func (s QuorumPrivateTxSigner) Hash(tx *Transaction) common.Hash {
|
||||
return s.HomesteadSigner.Hash(tx)
|
||||
}
|
||||
|
||||
func (s QuorumPrivateTxSigner) Equal(s2 Signer) bool {
|
||||
_, ok := s2.(QuorumPrivateTxSigner)
|
||||
return ok
|
||||
}
|
||||
|
||||
/*
|
||||
* If v is `37` or `38` that marks the transaction as private in Quorum.
|
||||
* Note: this means quorum chains cannot have a public ethereum chainId == 1, as the EIP155 v
|
||||
* param is `37` and `38` for the public Ethereum chain. Having a private chain with a chainId ==1
|
||||
* is discouraged in the general Ethereum ecosystem.
|
||||
*/
|
||||
func isPrivate(v *big.Int) bool {
|
||||
return v.Cmp(big.NewInt(37)) == 0 || v.Cmp(big.NewInt(38)) == 0
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
testifyassert "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func signTxWithSigner(signer Signer, key *ecdsa.PrivateKey) (*Transaction, common.Address, error) {
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
tx := NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil)
|
||||
signedTx, err := SignTx(tx, signer, key)
|
||||
return signedTx, addr, err
|
||||
}
|
||||
|
||||
// run all the tests in this file
|
||||
// $> go test $(go list ./...) -run QuorumSignPrivate
|
||||
|
||||
// test with QuorumPrivateSigner
|
||||
/*
|
||||
* $> go test -run TestQuorumSignPrivateQuorum
|
||||
*/
|
||||
func TestQuorumSignPrivateQuorum(t *testing.T) {
|
||||
|
||||
assert := testifyassert.New(t)
|
||||
keys := []*big.Int{k0v, k1v}
|
||||
|
||||
for i := 0; i < len(keys); i++ {
|
||||
key, _ := createKey(crypto.S256(), keys[i])
|
||||
qpPrivateSigner := QuorumPrivateTxSigner{HomesteadSigner{}}
|
||||
|
||||
signedTx, addr, err := signTxWithSigner(qpPrivateSigner, key)
|
||||
assert.Nil(err, err)
|
||||
assert.True(signedTx.IsPrivate(),
|
||||
fmt.Sprintf("The signed transaction is not private, signedTx.data.V is [%v]", signedTx.data.V))
|
||||
from, err := Sender(qpPrivateSigner, signedTx)
|
||||
assert.Nil(err, err)
|
||||
assert.True(from == addr, fmt.Sprintf("Expected from == address, [%x] == [%x]", from, addr))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -392,7 +392,14 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
|
|||
defer s.nonceLock.UnlockAddr(args.From)
|
||||
}
|
||||
|
||||
isPrivate := args.PrivateFor != nil
|
||||
// Set some sanity defaults and terminate on failure
|
||||
if err := args.setDefaults(ctx, s.b); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
// Assemble the transaction and sign with the wallet
|
||||
tx := args.toTransaction()
|
||||
|
||||
isPrivate := args.IsPrivate()
|
||||
|
||||
if isPrivate {
|
||||
data := []byte(*args.Data)
|
||||
|
@ -407,25 +414,18 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
|
|||
// zekun: HACK
|
||||
d := hexutil.Bytes(data)
|
||||
args.Data = &d
|
||||
tx = args.toTransaction()
|
||||
// set to private before submitting to signer
|
||||
// this sets the v value to 37 temporarily to indicate a private tx, and to choose the correct signer.
|
||||
tx.SetPrivate()
|
||||
}
|
||||
|
||||
// Set some sanity defaults and terminate on failure
|
||||
if err := args.setDefaults(ctx, s.b); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
// Assemble the transaction and sign with the wallet
|
||||
tx := args.toTransaction()
|
||||
|
||||
var chainID *big.Int
|
||||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) && !isPrivate {
|
||||
chainID = config.ChainID
|
||||
}
|
||||
signed, err := wallet.SignTxWithPassphrase(account, passwd, tx, chainID)
|
||||
signed, err := wallet.SignTxWithPassphrase(account, passwd, tx, s.b.ChainConfig().ChainID)
|
||||
if err != nil {
|
||||
log.Warn("Failed transaction send attempt", "from", args.From, "to", args.To, "value", args.Value.ToInt(), "err", err)
|
||||
return common.Hash{}, err
|
||||
}
|
||||
return submitTransaction(ctx, s.b, signed, isPrivate)
|
||||
return submitTransaction(ctx, s.b, signed)
|
||||
}
|
||||
|
||||
// SignTransaction will create a transaction from the given arguments and
|
||||
|
@ -1238,6 +1238,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
|
|||
return fields, nil
|
||||
}
|
||||
|
||||
// quorum: if signing a private TX set with tx.SetPrivate() before calling this method.
|
||||
// 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) {
|
||||
// Look up the wallet containing the requested signer
|
||||
|
@ -1249,11 +1250,10 @@ func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transacti
|
|||
}
|
||||
// Request the wallet to sign the transaction
|
||||
var chainID *big.Int
|
||||
isQuorum := tx.IsPrivate()
|
||||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) && !tx.IsPrivate() {
|
||||
chainID = config.ChainID
|
||||
}
|
||||
return wallet.SignTx(account, tx, chainID, isQuorum)
|
||||
return wallet.SignTx(account, tx, chainID)
|
||||
}
|
||||
|
||||
// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool.
|
||||
|
@ -1276,6 +1276,10 @@ type SendTxArgs struct {
|
|||
//End-Quorum
|
||||
}
|
||||
|
||||
func (s SendTxArgs) IsPrivate() bool {
|
||||
return s.PrivateFor != nil
|
||||
}
|
||||
|
||||
// SendRawTxArgs represents the arguments to submit a new signed private transaction into the transaction pool.
|
||||
type SendRawTxArgs struct {
|
||||
PrivateFor []string `json:"privateFor"`
|
||||
|
@ -1325,17 +1329,19 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
|
|||
return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
|
||||
}
|
||||
|
||||
// TODO: this submits a signed transaction, if it is a signed private transaction that should already be recorded in the tx.
|
||||
// submitTransaction is a helper function that submits tx to txPool and logs a message.
|
||||
func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, isPrivate bool) (common.Hash, error) {
|
||||
if isPrivate {
|
||||
tx.SetPrivate()
|
||||
}
|
||||
|
||||
func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
|
||||
if err := b.SendTx(ctx, tx); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
if tx.To() == nil {
|
||||
signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
|
||||
var signer types.Signer
|
||||
if tx.IsPrivate() {
|
||||
signer = types.QuorumPrivateTxSigner{}
|
||||
} else {
|
||||
signer = types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
|
||||
}
|
||||
from, err := types.Sender(signer, tx)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
|
@ -1369,7 +1375,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
|
|||
defer s.nonceLock.UnlockAddr(args.From)
|
||||
}
|
||||
|
||||
isPrivate := args.PrivateFor != nil
|
||||
isPrivate := args.IsPrivate()
|
||||
var data []byte
|
||||
if isPrivate {
|
||||
if args.Data != nil {
|
||||
|
@ -1401,15 +1407,19 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
|
|||
tx := args.toTransaction()
|
||||
|
||||
var chainID *big.Int
|
||||
isQuorum := tx.IsPrivate()
|
||||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) && !isPrivate {
|
||||
chainID = config.ChainID
|
||||
}
|
||||
signed, err := wallet.SignTx(account, tx, chainID, isQuorum)
|
||||
|
||||
if isPrivate {
|
||||
tx.SetPrivate()
|
||||
}
|
||||
signed, err := wallet.SignTx(account, tx, chainID)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
return submitTransaction(ctx, s.b, signed, isPrivate)
|
||||
return submitTransaction(ctx, s.b, signed)
|
||||
|
||||
}
|
||||
|
||||
// SendRawTransaction will add the signed transaction to the transaction pool.
|
||||
|
@ -1419,7 +1429,7 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
|
|||
if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
return submitTransaction(ctx, s.b, tx, tx.IsPrivate())
|
||||
return submitTransaction(ctx, s.b, tx)
|
||||
}
|
||||
|
||||
// SendRawPrivateTransaction will add the signed transaction to the transaction pool.
|
||||
|
@ -1432,7 +1442,7 @@ func (s *PublicTransactionPoolAPI) SendRawPrivateTransaction(ctx context.Context
|
|||
}
|
||||
|
||||
txHash := []byte(tx.Data())
|
||||
isPrivate := args.PrivateFor != nil
|
||||
isPrivate := (args.PrivateFor != nil) && tx.IsPrivate()
|
||||
|
||||
if isPrivate {
|
||||
if len(txHash) > 0 {
|
||||
|
@ -1447,8 +1457,7 @@ func (s *PublicTransactionPoolAPI) SendRawPrivateTransaction(ctx context.Context
|
|||
} else {
|
||||
return common.Hash{}, fmt.Errorf("transaction is not private")
|
||||
}
|
||||
|
||||
return submitTransaction(ctx, s.b, tx, isPrivate)
|
||||
return submitTransaction(ctx, s.b, tx)
|
||||
}
|
||||
|
||||
// Sign calculates an ECDSA signature for:
|
||||
|
@ -1556,7 +1565,9 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
|
|||
|
||||
for _, p := range pending {
|
||||
var signer types.Signer = types.HomesteadSigner{}
|
||||
if p.Protected() && !p.IsPrivate() {
|
||||
if p.IsPrivate() {
|
||||
signer = types.QuorumPrivateTxSigner{}
|
||||
} else if p.Protected() {
|
||||
signer = types.NewEIP155Signer(p.ChainId())
|
||||
}
|
||||
wantSigHash := signer.Hash(matchTx)
|
||||
|
@ -1570,10 +1581,10 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
|
|||
sendArgs.Gas = gasLimit
|
||||
}
|
||||
newTx := sendArgs.toTransaction()
|
||||
// set v param to 37 to indicate private tx before submitting to the signer.
|
||||
if len(sendArgs.PrivateFor) > 0 {
|
||||
newTx.SetPrivate()
|
||||
}
|
||||
|
||||
signedTx, err := s.sign(sendArgs.From, newTx)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
|
|
|
@ -85,7 +85,7 @@ func TestAccountManagement(t *testing.T) {
|
|||
if err := ks.Unlock(signer, "Signer password"); err != nil {
|
||||
t.Fatalf("Failed to unlock account: %v", err)
|
||||
}
|
||||
if _, err := ks.SignTx(signer, tx, chain, false); err != nil {
|
||||
if _, err := ks.SignTx(signer, tx, chain); err != nil {
|
||||
t.Fatalf("Failed to sign with unlocked account: %v", err)
|
||||
}
|
||||
if err := ks.Lock(signer.Address); err != nil {
|
||||
|
@ -95,7 +95,7 @@ func TestAccountManagement(t *testing.T) {
|
|||
if err := ks.TimedUnlock(signer, "Signer password", time.Second); err != nil {
|
||||
t.Fatalf("Failed to time unlock account: %v", err)
|
||||
}
|
||||
if _, err := ks.SignTx(signer, tx, chain, false); err != nil {
|
||||
if _, err := ks.SignTx(signer, tx, chain); err != nil {
|
||||
t.Fatalf("Failed to sign with time unlocked account: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (
|
|||
if chainID == nil { // Null passed from mobile app
|
||||
chainID = new(BigInt)
|
||||
}
|
||||
signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint, false)
|
||||
signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue