Skip to content

Commit

Permalink
Merge pull request ethereum#12 from jpmorganchase/jpm-master
Browse files Browse the repository at this point in the history
core: added small private test framework
  • Loading branch information
patrickmn authored Nov 17, 2016
2 parents ed599df + 591b463 commit f080020
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 94 deletions.
94 changes: 0 additions & 94 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1058,97 +1058,3 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
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")
}
}
86 changes: 86 additions & 0 deletions core/call_helper.go
Original file line number Diff line number Diff line change
@@ -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
}
101 changes: 101 additions & 0 deletions core/private_state_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
}

0 comments on commit f080020

Please sign in to comment.