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:
Jeffrey Wilcke 2016-11-17 14:14:05 +01:00
parent 1097bdb1b6
commit 591b463b32
3 changed files with 187 additions and 94 deletions

View File

@ -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
core/call_helper.go Normal file
View File

@ -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
core/private_state_test.go Normal file
View File

@ -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")
}
}