mirror of https://github.com/poanetwork/quorum.git
core: added small private test framework
For an example on how to create such a test run: `godep -hhtp=:6060 and navigate to http://localhost:6060/pkg/github.com/ethereum/go-ethereum/core/#example_MakeCallHelper
This commit is contained in:
parent
1097bdb1b6
commit
591b463b32
|
@ -1058,97 +1058,3 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||||
blockchain.InsertChain(types.Blocks{chain[i]})
|
blockchain.InsertChain(types.Blocks{chain[i]})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type privateTestTx struct {
|
|
||||||
*types.Transaction
|
|
||||||
private bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ptx *privateTestTx) IsPrivate() bool { return ptx.private }
|
|
||||||
|
|
||||||
// Tests if the canonical block can be fetched from the database during chain insertion.
|
|
||||||
func TestPrivateTransactions(t *testing.T) {
|
|
||||||
var (
|
|
||||||
db, _ = ethdb.NewMemDatabase()
|
|
||||||
key, _ = crypto.GenerateKey()
|
|
||||||
evmux = &event.TypeMux{}
|
|
||||||
blockchain, _ = NewBlockChain(db, testChainConfig(), FakePow{}, evmux, false)
|
|
||||||
header = &types.Header{}
|
|
||||||
gp = new(GasPool).AddGas(big.NewInt(2000000))
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
publicState, err := state.New(common.Hash{}, db)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
privateState, err := state.New(common.Hash{}, db)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
prvContractAddr := common.Address{1}
|
|
||||||
pubContractAddr := common.Address{2}
|
|
||||||
|
|
||||||
/* gllc
|
|
||||||
asm {
|
|
||||||
PUSH1 10
|
|
||||||
PUSH1 0
|
|
||||||
SSTORE
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
privateState.SetCode(prvContractAddr, common.Hex2Bytes("600a60005500"))
|
|
||||||
privateState.SetState(prvContractAddr, common.Hash{}, common.Hash{9})
|
|
||||||
publicState.SetCode(pubContractAddr, common.Hex2Bytes("601460005500"))
|
|
||||||
publicState.SetState(pubContractAddr, common.Hash{}, common.Hash{19})
|
|
||||||
|
|
||||||
// Private transaction 1
|
|
||||||
ptx := privateTestTx{private: true}
|
|
||||||
ptx.Transaction, err = types.NewTransaction(0, prvContractAddr, new(big.Int), big.NewInt(1000000), new(big.Int), nil).SignECDSA(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
_, _, err = ApplyMessage(NewEnv(publicState, privateState, &ChainConfig{}, blockchain, ptx.Transaction, header, vm.Config{}), ptx, gp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
stateEntry := privateState.GetState(prvContractAddr, common.Hash{}).Big()
|
|
||||||
if stateEntry.Cmp(big.NewInt(10)) != 0 {
|
|
||||||
t.Error("expected state to have 10, got", stateEntry)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public transaction 1
|
|
||||||
ptx = privateTestTx{private: false}
|
|
||||||
ptx.Transaction, err = types.NewTransaction(1, pubContractAddr, new(big.Int), big.NewInt(1000000), new(big.Int), nil).SignECDSA(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
_, _, err = ApplyMessage(NewEnv(publicState, publicState, &ChainConfig{}, blockchain, ptx.Transaction, header, vm.Config{}), ptx, gp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
stateEntry = publicState.GetState(pubContractAddr, common.Hash{}).Big()
|
|
||||||
if stateEntry.Cmp(big.NewInt(20)) != 0 {
|
|
||||||
t.Error("expected state to have 20, got", stateEntry)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private transaction 2
|
|
||||||
ptx = privateTestTx{private: true}
|
|
||||||
ptx.Transaction, err = types.NewTransaction(2, prvContractAddr, new(big.Int), big.NewInt(1000000), new(big.Int), nil).SignECDSA(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
_, _, err = ApplyMessage(NewEnv(publicState, privateState, &ChainConfig{}, blockchain, ptx.Transaction, header, vm.Config{}), ptx, gp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
stateEntry = privateState.GetState(prvContractAddr, common.Hash{}).Big()
|
|
||||||
if stateEntry.Cmp(big.NewInt(10)) != 0 {
|
|
||||||
t.Error("expected state to have 10, got", stateEntry)
|
|
||||||
}
|
|
||||||
|
|
||||||
if publicState.Exist(prvContractAddr) {
|
|
||||||
t.Error("didn't expect private contract address to exist on public state")
|
|
||||||
}
|
|
||||||
if privateState.Exist(pubContractAddr) {
|
|
||||||
t.Error("didn't expect public contract address to exist on private state")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// privateTestTx stubs transaction so that it can be flagged as private or not
|
||||||
|
type privateTestTx struct {
|
||||||
|
*types.Transaction
|
||||||
|
private bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate returns whether the transaction should be considered privat.
|
||||||
|
func (ptx *privateTestTx) IsPrivate() bool { return ptx.private }
|
||||||
|
|
||||||
|
// callHelper makes it easier to do proper calls and use the state transition object.
|
||||||
|
// It also manages the nonces of the caller and keeps private and public state, which
|
||||||
|
// can be freely modified outside of the helper.
|
||||||
|
type callHelper struct {
|
||||||
|
db ethdb.Database
|
||||||
|
|
||||||
|
nonces map[common.Address]uint64
|
||||||
|
header types.Header
|
||||||
|
gp *GasPool
|
||||||
|
|
||||||
|
PrivateState, PublicState *state.StateDB
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxNonce returns the pending nonce
|
||||||
|
func (cg *callHelper) TxNonce(addr common.Address) uint64 {
|
||||||
|
return cg.nonces[addr]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeCall makes does a call to the recipient using the given input. It can switch between private and public
|
||||||
|
// by setting the private boolean flag. It returns an error if the call failed.
|
||||||
|
func (cg *callHelper) MakeCall(private bool, key *ecdsa.PrivateKey, to common.Address, input []byte) error {
|
||||||
|
var (
|
||||||
|
from = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
ptx = privateTestTx{private: private}
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
ptx.Transaction, err = types.NewTransaction(cg.TxNonce(from), to, new(big.Int), big.NewInt(1000000), new(big.Int), input).SignECDSA(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { cg.nonces[from]++ }()
|
||||||
|
|
||||||
|
publicState, privateState := cg.PublicState, cg.PrivateState
|
||||||
|
if !private {
|
||||||
|
privateState = publicState
|
||||||
|
}
|
||||||
|
_, _, err = ApplyMessage(NewEnv(publicState, privateState, &ChainConfig{}, nil, ptx.Transaction, &cg.header, vm.Config{}), ptx, cg.gp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeCallHelper returns a new callHelper
|
||||||
|
func MakeCallHelper() *callHelper {
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
|
||||||
|
publicState, err := state.New(common.Hash{}, db)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
privateState, err := state.New(common.Hash{}, db)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
cg := &callHelper{
|
||||||
|
nonces: make(map[common.Address]uint64),
|
||||||
|
gp: new(GasPool).AddGas(big.NewInt(5000000)),
|
||||||
|
PublicState: publicState,
|
||||||
|
PrivateState: privateState,
|
||||||
|
}
|
||||||
|
return cg
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleMakeCallHelper() {
|
||||||
|
var (
|
||||||
|
// setup new pair of keys for the calls
|
||||||
|
key, _ = crypto.GenerateKey()
|
||||||
|
// create a new helper
|
||||||
|
helper = MakeCallHelper()
|
||||||
|
)
|
||||||
|
// Private contract address
|
||||||
|
prvContractAddr := common.Address{1}
|
||||||
|
// Initialise custom code for private contract
|
||||||
|
helper.PrivateState.SetCode(prvContractAddr, common.Hex2Bytes("600a60005500"))
|
||||||
|
// Public contract address
|
||||||
|
pubContractAddr := common.Address{2}
|
||||||
|
// Initialise custom code for public contract
|
||||||
|
helper.PublicState.SetCode(pubContractAddr, common.Hex2Bytes("601460005500"))
|
||||||
|
|
||||||
|
// Make a call to the private contract
|
||||||
|
err := helper.MakeCall(true, key, prvContractAddr, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
// Make a call to the public contract
|
||||||
|
err = helper.MakeCall(false, key, pubContractAddr, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Private: 10
|
||||||
|
// Public: 20
|
||||||
|
fmt.Println("Private:", helper.PrivateState.GetState(prvContractAddr, common.Hash{}).Big())
|
||||||
|
fmt.Println("Public:", helper.PublicState.GetState(pubContractAddr, common.Hash{}).Big())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrivateTransaction(t *testing.T) {
|
||||||
|
var (
|
||||||
|
key, _ = crypto.GenerateKey()
|
||||||
|
helper = MakeCallHelper()
|
||||||
|
privateState = helper.PrivateState
|
||||||
|
publicState = helper.PublicState
|
||||||
|
)
|
||||||
|
|
||||||
|
prvContractAddr := common.Address{1}
|
||||||
|
pubContractAddr := common.Address{2}
|
||||||
|
/* gllc
|
||||||
|
asm {
|
||||||
|
PUSH1 10
|
||||||
|
PUSH1 0
|
||||||
|
SSTORE
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
privateState.SetCode(prvContractAddr, common.Hex2Bytes("600a60005500"))
|
||||||
|
privateState.SetState(prvContractAddr, common.Hash{}, common.Hash{9})
|
||||||
|
publicState.SetCode(pubContractAddr, common.Hex2Bytes("601460005500"))
|
||||||
|
publicState.SetState(pubContractAddr, common.Hash{}, common.Hash{19})
|
||||||
|
|
||||||
|
// Private transaction 1
|
||||||
|
err := helper.MakeCall(true, key, prvContractAddr, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
stateEntry := privateState.GetState(prvContractAddr, common.Hash{}).Big()
|
||||||
|
if stateEntry.Cmp(big.NewInt(10)) != 0 {
|
||||||
|
t.Error("expected state to have 10, got", stateEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public transaction 1
|
||||||
|
err = helper.MakeCall(false, key, pubContractAddr, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
stateEntry = publicState.GetState(pubContractAddr, common.Hash{}).Big()
|
||||||
|
if stateEntry.Cmp(big.NewInt(20)) != 0 {
|
||||||
|
t.Error("expected state to have 20, got", stateEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private transaction 2
|
||||||
|
err = helper.MakeCall(true, key, prvContractAddr, nil)
|
||||||
|
stateEntry = privateState.GetState(prvContractAddr, common.Hash{}).Big()
|
||||||
|
if stateEntry.Cmp(big.NewInt(10)) != 0 {
|
||||||
|
t.Error("expected state to have 10, got", stateEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if publicState.Exist(prvContractAddr) {
|
||||||
|
t.Error("didn't expect private contract address to exist on public state")
|
||||||
|
}
|
||||||
|
if privateState.Exist(pubContractAddr) {
|
||||||
|
t.Error("didn't expect public contract address to exist on private state")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue