forked from ethereum/go-ethereum
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request ethereum#12 from jpmorganchase/jpm-master
core: added small private test framework
- Loading branch information
Showing
3 changed files
with
187 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} | ||
} |