quorum/core/types/transaction_signing_quorum_...

355 lines
13 KiB
Go

// 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"
"crypto/elliptic"
"fmt"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
testifyassert "github.com/stretchr/testify/assert"
)
// run all the tests in this file
// $> go test $(go list ./...) -run TestSignQuorum
// private key material to test both 0 and 1 bit for the recoveryId (v).
// key with v sign == 28 (Homestead)
var k0v, _ = new(big.Int).SetString("25807260602402504536675820444142779248993100028628438487502323668296269534891", 10)
// key with v sign == 27 (Homestead)
var k1v, _ = new(big.Int).SetString("10148397294747000913768625849546502595195728826990639993137198410557736548965", 10)
// helper to deterministically create an ECDSA key from an int.
func createKey(c elliptic.Curve, k *big.Int) (*ecdsa.PrivateKey, error) {
sk := new(ecdsa.PrivateKey)
sk.PublicKey.Curve = c
sk.D = k
sk.PublicKey.X, sk.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
return sk, nil
}
func signTx(key *ecdsa.PrivateKey, signer Signer) (*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)
//fmt.Printf("\ntx.data.V signTx after sign [%v] \n", signedTx.data.V)
return signedTx, addr, err
}
/**
* As of quorum v2.2.3 commit be7cc31ce208525ea1822e7d0fee88bf7f14500b 30 April 2019 behavior
*
* Test public transactions signed by homestead Signer. Homestead sets the v param on a signed transaction to
* either 27 or 28. The v parameter is used for recovering the sender of the signed transaction.
*
* 1. Homestead: should be 27, 28
* $> go test -run TestSignQuorumHomesteadPublic
*/
func TestSignQuorumHomesteadPublic(t *testing.T) {
assert := testifyassert.New(t)
k0, _ := createKey(crypto.S256(), k0v)
k1, _ := createKey(crypto.S256(), k1v)
homeSinger := HomesteadSigner{}
// odd parity should be 27 for Homestead
signedTx, addr, _ := signTx(k1, homeSinger)
assert.True(signedTx.data.V.Cmp(big.NewInt(27)) == 0, fmt.Sprintf("v wasn't 27 it was [%v]", signedTx.data.V))
// recover address from signed TX
from, _ := Sender(homeSinger, signedTx)
//fmt.Printf("from [%v] == addr [%v]\n\n", from, from == addr)
assert.True(from == addr, fmt.Sprintf("Expected from and address to be equal. Got %x want %x", from, addr))
// even parity should be 28 for Homestead
signedTx, addr, _ = signTx(k0, homeSinger)
assert.True(signedTx.data.V.Cmp(big.NewInt(28)) == 0, fmt.Sprintf("v wasn't 28 it was [%v]\n", signedTx.data.V))
// recover address from signed TX
from, _ = Sender(homeSinger, signedTx)
//fmt.Printf("from [%v] == addr [%v]\n", from, from == addr)
assert.True(from == addr, fmt.Sprintf("Expected from and address to be equal. Got %x want %x", from, addr))
}
/**
* As of quorum v2.2.3 commit be7cc31ce208525ea1822e7d0fee88bf7f14500b 30 April 2019 behavior
*
* Test the public transactions signed by the EIP155Signer.
* The EIP155Signer was introduced to protect against replay
* attacks https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md and stores
* the CHAINID in the signed transaction's `v` parameter as `v = chainId * 2 + 35`.
*
* The EthEIP155Signer change breaks private quorum transactions when the chainId == 1 (mainnet chainId),
* as the v parameter on a public transaction and on a private transaction will both be 37, 38.
*
* $> go test -run TestSignQuorumEIP155Public
*/
func TestSignQuorumEIP155Public(t *testing.T) {
assert := testifyassert.New(t)
k0, _ := createKey(crypto.S256(), k0v)
k1, _ := createKey(crypto.S256(), k1v)
// chainId 1 even EIP155Signer should be 37 conflicts with private transaction
var chainId int64 = 2 // 7 2 10
v0 := chainId*2 + 35 // sig[64] + 35 .. where sig[64] == 0
v1 := chainId*2 + 36 // sig[64] + 35 .. where sig[64] == 1
// Will calculate v to be `v = CHAINID * 2 + 35`
// To compute V:
// 2 * 2 + 35 == 39
// 2 * 2 + 36 == 40
// To retrieve Sender, pull out 27, 28 Eth Frontier / Homestead values.
// 39 - (2 * 2) - 8 == 27
// 40 - (2 * 2) - 8 == 28
EIPsigner := NewEIP155Signer(big.NewInt(chainId))
signedTx, addr, _ := signTx(k0, EIPsigner)
//fmt.Printf("After signing V is [%v] \n", signedTx.data.V)
assert.True(signedTx.data.V.Cmp(big.NewInt(v0)) == 0, fmt.Sprintf("v wasn't [%v] it was [%v]\n", v0, signedTx.data.V))
from, _ := Sender(EIPsigner, signedTx)
assert.True(from == addr, fmt.Sprintf("Expected from and address to be equal. Got %x want %x", from, addr))
// chainId 1 even EIP155Signer should be 38 conflicts with private transaction
assert.False(signedTx.IsPrivate(), fmt.Sprintf("Public transaction is set to a private transition v == [%v]", signedTx.data.V))
signedTx, addr, _ = signTx(k1, EIPsigner)
assert.True(signedTx.data.V.Cmp(big.NewInt(v1)) == 0, fmt.Sprintf("v wasn't [%v], it was [%v]\n", v1, signedTx.data.V))
from, _ = Sender(EIPsigner, signedTx)
assert.True(from == addr, fmt.Sprintf("Expected from and address to be equal. Got %x want %x", from, addr))
}
/**
* As of quorum v2.2.3 commit be7cc31ce208525ea1822e7d0fee88bf7f14500b 30 April 2019 behavior
*
* When the signer is EIP155Signer, chainId == 1 cannot be used because the EIP155 computed `v` value conflicts
* with the private `v` value that quorum uses to indicate a private transaction: v == 37 and v == 38.
*
* $> go test -run TestSignQuorumEIP155FailPublicChain1
*/
func TestSignQuorumEIP155FailPublicChain1(t *testing.T) {
assert := testifyassert.New(t)
k0, _ := createKey(crypto.S256(), k0v)
k1, _ := createKey(crypto.S256(), k1v)
// chainId 1 even EIP155Signer should be 37.38 which conflicts with private transaction
var chainId int64 = 1
v0 := chainId*2 + 35 // sig[64] + 35 .. where sig[64] == 0
v1 := chainId*2 + 36 // sig[64] + 35 .. where sig[64] == 1
// Will calculate v to be `v = CHAINID * 2 + 35`
// To compute V:
// 2 * 1 + 35 == 37
// 2 * 1 + 36 == 38
// To retrieve Sender, pull out 27, 28 Eth Frontier / Homestead values.
// 37 - (1 * 2) - 8 == 27
// 38 - (1 * 2) - 8 == 28
EIPsigner := NewEIP155Signer(big.NewInt(chainId))
signedTx, addr, _ := signTx(k0, EIPsigner)
// the calculated v value should equal `chainId * 2 + 35 `
assert.True(signedTx.data.V.Cmp(big.NewInt(v0)) == 0, fmt.Sprintf("v wasn't [%v] it was "+
"[%v]\n", v0, signedTx.data.V))
// the sender will not be equal as HomesteadSigner{}.Sender(tx) is used because IsPrivate() will be true
// although it is a public tx.
// This is test to catch when / if this behavior changes.
assert.True(signedTx.IsPrivate(), "A public transaction with EIP155 and chainID 1 is expected to be "+
"considered private, as its v param conflict with a private transaction. signedTx.IsPrivate() == [%v]", signedTx.IsPrivate())
from, _ := Sender(EIPsigner, signedTx)
assert.False(from == addr, fmt.Sprintf("Expected the sender of a public TX from chainId 1, \n "+
"should not be recoverable from [%x] addr [%v] ", from, addr))
signedTx, addr, _ = signTx(k1, EIPsigner)
// the calculated v value should equal `chainId * 2 + 35`
assert.True(signedTx.data.V.Cmp(big.NewInt(v1)) == 0,
fmt.Sprintf("v wasn't [%v] it was [%v]", v1, signedTx.data.V))
// the sender will not be equal as HomesteadSigner{}.Sender(tx) is used because IsPrivate() will be true
// although it is a public tx.
// This is test to catch when / if this behavior changes.
// we are signing the data with EIPsigner and chainID 1, so this would be considered a private tx.
assert.True(signedTx.IsPrivate(), "A public transaction with EIP155 and chainID 1 is expected to "+
"to be considered private, as its v param conflict with a private transaction. "+
"signedTx.IsPrivate() == [%v]", signedTx.IsPrivate())
from, _ = Sender(EIPsigner, signedTx)
assert.False(from == addr, fmt.Sprintf("Expected the sender of a public TX from chainId 1, "+
"should not be recoverable from [%x] addr [%v] ", from, addr))
}
/**
* As of quorum v2.2.3 commit be7cc31ce208525ea1822e7d0fee88bf7f14500b 30 April 2019 behavior
*
* Use Homestead to sign and EIPSigner to recover.
*
* SendTransaction creates a transaction for the given argument, signs it and submit it to the transaction pool.
* func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
* Current implementation in `internal/ethapi/api.go`
*
* accounts/keystore/keystore.SignTx(): would hash and sign with homestead
*
* When a private tx (obtained from json params PrivateFor) is submitted `internal/ethapi/api.go`:
*
* 1. sign with HomesteadSigner, this will set the v parameter to
* 27 or 28. // there is no indication that this is a private tx yet.
*
* 2. when submitting a transaction `submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, isPrivate bool)`
check isPrivate param, and call `tx.SetPrivate()`, this will update the `v` signature param (recoveryID)
* from 27 -> 37, 28 -> 38. // this is now considered a private tx.
*
* $> go test -run TestSignQuorumHomesteadEIP155SigningPrivateQuorum
*/
func TestSignQuorumHomesteadEIP155SigningPrivateQuorum(t *testing.T) {
assert := testifyassert.New(t)
keys := []*big.Int{k0v, k1v}
homeSinger := HomesteadSigner{}
recoverySigner := NewEIP155Signer(big.NewInt(18))
// check for both sig[64] == 0, and sig[64] == 1
for i := 0; i < len(keys); i++ {
key, _ := createKey(crypto.S256(), keys[i])
signedTx, addr, err := signTx(key, homeSinger)
assert.Nil(err, err)
// set to privateTX after the initial signing, this explicitly sets the v param.
// Note: only works when the tx was signed with the homesteadSinger (v==27 | 28).
signedTx.SetPrivate()
assert.True(signedTx.IsPrivate(), fmt.Sprintf("Expected the transaction to be private [%v]", signedTx.IsPrivate()))
// Try to recover Sender
from, err := Sender(recoverySigner, signedTx)
assert.Nil(err, err)
assert.True(from == addr, fmt.Sprintf("Expected from and address to be equal. Got %x want %x", from, addr))
}
}
/*
* As of quorum v2.2.3 commit be7cc31ce208525ea1822e7d0fee88bf7f14500b 30 April 2019 behavior
* Use Homestead to sign and Homestead to recover.
*
* Signing private transactions with HomesteadSigner, and recovering a private transaction with
* HomesteadSigner works, but the transaction has to be set to private `signedTx.SetPrivate()` after
* the signature and before recovering the address.
*
* $> go test -run TestSignQuorumHomesteadOnlyPrivateQuorum
*/
func TestSignQuorumHomesteadOnlyPrivateQuorum(t *testing.T) {
assert := testifyassert.New(t)
// check even and odd parity
keys := []*big.Int{k0v, k1v}
homeSinger := HomesteadSigner{}
recoverySigner := HomesteadSigner{}
for i := 0; i < len(keys); i++ {
key, _ := createKey(crypto.S256(), keys[i])
signedTx, addr, err := signTx(key, homeSinger)
assert.Nil(err, err)
//fmt.Printf("Private tx.data.V Home [%v] \n", signedTx.data.V)
// set to privateTX after the initial signing.
signedTx.SetPrivate()
assert.True(signedTx.IsPrivate(), fmt.Sprintf("Expected the transaction to be "+
"private [%v]", signedTx.IsPrivate()))
//fmt.Printf("Private tx.data.V Home [%v] \n", signedTx.data.V)
// Try to recover Sender
from, err := Sender(recoverySigner, signedTx)
assert.Nil(err, err)
assert.True(from == addr, fmt.Sprintf("Expected from and address to be equal. "+
" Got %x want %x", from, addr))
}
}
/*
* As of quorum v2.2.3 commit be7cc31ce208525ea1822e7d0fee88bf7f14500b 30 April 2019 behavior
*
* Use EIP155 to sign and EIP155 to recover (This is not a valid combination and does **not** work).
*
* Signing private transactions with EIP155Signer, and recovering a private transaction with
* EIP155Signer does **not** work.
* note: deriveChainId only checks for 27, 28 when using EIP155
* note: In the case where the v param is not 27 or 28 when setting private it will always be set to 37
*
* $> go test -run TestSignQuorumEIP155OnlyPrivateQuorum
*/
func TestSignQuorumEIP155OnlyPrivateQuorum(t *testing.T) {
assert := testifyassert.New(t)
// check even and odd parity
keys := []*big.Int{k0v, k1v}
EIP155Signer := NewEIP155Signer(big.NewInt(0))
for i := 0; i < len(keys); i++ {
key, _ := createKey(crypto.S256(), keys[i])
signedTx, addr, err := signTx(key, EIP155Signer)
assert.Nil(err, err)
//fmt.Printf("Private tx.data.V Home [%v] \n", signedTx.data.V)
// set to privateTX after the initial signing.
signedTx.SetPrivate()
assert.True(signedTx.IsPrivate(), fmt.Sprintf("Expected the transaction to be private [%v]", signedTx.IsPrivate()))
//fmt.Printf("Private tx.data.V Home [%v] \n", signedTx.data.V)
// Try to recover Sender
from, err := Sender(EIP155Signer, signedTx)
assert.Nil(err, err)
assert.False(from == addr, fmt.Sprintf("Expected recovery to fail. from [%x] should not equal "+
"addr [%x]", from, addr))
}
}