mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-07 22:05:18 +00:00
testing send and call permissions
This commit is contained in:
@@ -461,13 +461,19 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
|
||||
// NOTE: Call() transfers the value from caller to callee iff call succeeds.
|
||||
|
||||
if isDoug {
|
||||
// we need to bind a copy of the accounts tree (in the txCache)
|
||||
// we need to bind a copy of the accounts tree (from the txCache)
|
||||
// so the gendoug can make a native call to create accounts and update
|
||||
// permissions
|
||||
setupDoug(vmach, txCache, _s)
|
||||
}
|
||||
// set the contracts permissions
|
||||
vmach.SetPermissions(inAcc.Permissions.Send, inAcc.Permissions.Call, inAcc.Permissions.Create)
|
||||
// set the contract's permissions
|
||||
// (the vm doesn't know about account.Account or account.Permissions
|
||||
vmach.SetPermissionsGetter(func(vmAcc *vm.Account) (bool, bool, bool) {
|
||||
stAcc := toStateAccount(vmAcc)
|
||||
p := stAcc.Permissions
|
||||
return p.Send, p.Call, p.Create
|
||||
})
|
||||
vmach.SetPermissions(callee)
|
||||
|
||||
ret, err := vmach.Call(caller, callee, code, tx.Data, value, &gas)
|
||||
exception := ""
|
||||
@@ -853,7 +859,6 @@ func setupDoug(vmach *vm.VM, txCache *TxCache, _s *State) {
|
||||
permInt = 0x0
|
||||
}
|
||||
return LeftPadWord256([]byte{permInt}).Bytes(), nil
|
||||
|
||||
}
|
||||
|
||||
// set takes (address, permissionNum, permissionValue Word256), returns the permission value
|
||||
|
||||
83
state/make_txs.go
Normal file
83
state/make_txs.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/tendermint/tendermint/account"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// SendTx interface for adding inputs/outputs and adding signatures
|
||||
|
||||
func NewSendTx() *types.SendTx {
|
||||
return &types.SendTx{
|
||||
Inputs: []*types.TxInput{},
|
||||
Outputs: []*types.TxOutput{},
|
||||
}
|
||||
}
|
||||
|
||||
func SendTxAddInput(st AccountGetter, tx *types.SendTx, pubkey account.PubKey, amt uint64) error {
|
||||
addr := pubkey.Address()
|
||||
acc := st.GetAccount(addr)
|
||||
if acc == nil {
|
||||
return fmt.Errorf("Invalid address %X from pubkey %X", addr, pubkey)
|
||||
}
|
||||
|
||||
tx.Inputs = append(tx.Inputs, &types.TxInput{
|
||||
Address: addr,
|
||||
Amount: amt,
|
||||
Sequence: uint(acc.Sequence) + 1,
|
||||
Signature: account.SignatureEd25519{},
|
||||
PubKey: pubkey,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendTxAddOutput(tx *types.SendTx, addr []byte, amt uint64) error {
|
||||
tx.Outputs = append(tx.Outputs, &types.TxOutput{
|
||||
Address: addr,
|
||||
Amount: amt,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func SignSendTx(tx *types.SendTx, i int, privAccount *account.PrivAccount) error {
|
||||
if i >= len(tx.Inputs) {
|
||||
return fmt.Errorf("Index %v is greater than number of inputs (%v)", i, len(tx.Inputs))
|
||||
}
|
||||
tx.Inputs[i].PubKey = privAccount.PubKey
|
||||
tx.Inputs[i].Signature = privAccount.Sign(tx)
|
||||
return nil
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// CallTx interface for creating tx
|
||||
|
||||
func NewCallTx(st AccountGetter, from account.PubKey, to, data []byte, amt, gasLimit, fee uint64) (*types.CallTx, error) {
|
||||
addr := from.Address()
|
||||
acc := st.GetAccount(addr)
|
||||
if acc == nil {
|
||||
return nil, fmt.Errorf("Invalid address %X from pubkey %X", addr, from)
|
||||
}
|
||||
|
||||
input := &types.TxInput{
|
||||
Address: addr,
|
||||
Amount: amt,
|
||||
Sequence: uint(acc.Sequence) + 1,
|
||||
Signature: account.SignatureEd25519{},
|
||||
PubKey: from,
|
||||
}
|
||||
|
||||
return &types.CallTx{
|
||||
Input: input,
|
||||
Address: to,
|
||||
GasLimit: gasLimit,
|
||||
Fee: fee,
|
||||
Data: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SignCallTx(tx *types.CallTx, privAccount *account.PrivAccount) {
|
||||
tx.Input.PubKey = privAccount.PubKey
|
||||
tx.Input.Signature = privAccount.Sign(tx)
|
||||
}
|
||||
552
state/permissions_test.go
Normal file
552
state/permissions_test.go
Normal file
@@ -0,0 +1,552 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/account"
|
||||
. "github.com/tendermint/tendermint/common"
|
||||
dbm "github.com/tendermint/tendermint/db"
|
||||
"github.com/tendermint/tendermint/events"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
/*
|
||||
To Test:
|
||||
|
||||
- SendTx:
|
||||
x - 1 input, no perm, call perm, create perm
|
||||
x - 1 input, perm
|
||||
x - 2 inputs, one with perm one without
|
||||
|
||||
- CallTx
|
||||
x - 1 input, no perm, send perm, create perm
|
||||
x - 1 input, perm
|
||||
x - contract runs call but doesn't have call perm
|
||||
x - contract runs call and has call perm
|
||||
x - contract runs call (with perm), runs contract that runs call (without perm)
|
||||
x - contract runs call (with perm), runs contract that runs call (with perm)
|
||||
|
||||
- CallTx (Create)
|
||||
x - 1 input, no perm, send perm, call perm
|
||||
- 1 input, perm
|
||||
- contract runs create but doesn't have create perm
|
||||
- contract runs create but has perm
|
||||
- contract runs call with empty address (has call perm, not create perm)
|
||||
- contract runs call with empty address (has call perm, and create perm)
|
||||
|
||||
- BondTx
|
||||
- 1 input, no perm
|
||||
- 1 input, perm
|
||||
- 2 inputs, one with perm one without
|
||||
|
||||
- Gendoug: get/set/add/rm
|
||||
*/
|
||||
|
||||
// keys
|
||||
var (
|
||||
user = makeUsers(5)
|
||||
)
|
||||
|
||||
func makeUsers(n int) []*account.PrivAccount {
|
||||
accounts := []*account.PrivAccount{}
|
||||
for i := 0; i < n; i++ {
|
||||
secret := []byte("mysecret" + strconv.Itoa(i))
|
||||
user := account.GenPrivAccountFromSecret(secret)
|
||||
accounts = append(accounts, user)
|
||||
}
|
||||
return accounts
|
||||
}
|
||||
|
||||
var (
|
||||
PermsAllFalse = account.Permissions{
|
||||
Send: false,
|
||||
Call: false,
|
||||
Create: false,
|
||||
Bond: false,
|
||||
}
|
||||
)
|
||||
|
||||
func newBaseGenDoc(globalPerm, accountPerm account.Permissions) GenesisDoc {
|
||||
genAccounts := []GenesisAccount{}
|
||||
for _, u := range user[:5] {
|
||||
accPerm := accountPerm
|
||||
genAccounts = append(genAccounts, GenesisAccount{
|
||||
Address: u.Address,
|
||||
Amount: 1000000,
|
||||
Permissions: &accPerm,
|
||||
})
|
||||
}
|
||||
|
||||
return GenesisDoc{
|
||||
GenesisTime: time.Now(),
|
||||
Params: &GenesisParams{
|
||||
GlobalPermissions: &globalPerm,
|
||||
},
|
||||
Accounts: genAccounts,
|
||||
Validators: []GenesisValidator{
|
||||
GenesisValidator{
|
||||
PubKey: user[0].PubKey.(account.PubKeyEd25519),
|
||||
Amount: 10,
|
||||
UnbondTo: []GenesisAccount{
|
||||
GenesisAccount{
|
||||
Address: user[0].Address,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendFails(t *testing.T) {
|
||||
stateDB := dbm.GetDB("state")
|
||||
genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse)
|
||||
genDoc.Accounts[1].Permissions.Send = true
|
||||
genDoc.Accounts[2].Permissions.Call = true
|
||||
genDoc.Accounts[3].Permissions.Create = true
|
||||
st := MakeGenesisState(stateDB, &genDoc)
|
||||
blockCache := NewBlockCache(st)
|
||||
|
||||
//-------------------
|
||||
// send txs
|
||||
|
||||
// simple send tx should fail
|
||||
tx := NewSendTx()
|
||||
if err := SendTxAddInput(blockCache, tx, user[0].PubKey, 5); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
SendTxAddOutput(tx, user[1].Address, 5)
|
||||
SignSendTx(tx, 0, user[0])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||
t.Fatal("Expected error")
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// simple send tx with call perm should fail
|
||||
tx = NewSendTx()
|
||||
if err := SendTxAddInput(blockCache, tx, user[2].PubKey, 5); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
SendTxAddOutput(tx, user[4].Address, 5)
|
||||
SignSendTx(tx, 0, user[2])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||
t.Fatal("Expected error")
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// simple send tx with create perm should fail
|
||||
tx = NewSendTx()
|
||||
if err := SendTxAddInput(blockCache, tx, user[3].PubKey, 5); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
SendTxAddOutput(tx, user[4].Address, 5)
|
||||
SignSendTx(tx, 0, user[3])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||
t.Fatal("Expected error")
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallFails(t *testing.T) {
|
||||
stateDB := dbm.GetDB("state")
|
||||
genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse)
|
||||
genDoc.Accounts[1].Permissions.Send = true
|
||||
genDoc.Accounts[2].Permissions.Call = true
|
||||
genDoc.Accounts[3].Permissions.Create = true
|
||||
st := MakeGenesisState(stateDB, &genDoc)
|
||||
blockCache := NewBlockCache(st)
|
||||
|
||||
//-------------------
|
||||
// call txs
|
||||
|
||||
// simple call tx should fail
|
||||
tx, _ := NewCallTx(blockCache, user[0].PubKey, user[4].Address, nil, 100, 100, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||
t.Fatal("Expected error")
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// simple call tx with send permission should fail
|
||||
tx, _ = NewCallTx(blockCache, user[1].PubKey, user[4].Address, nil, 100, 100, 100)
|
||||
SignCallTx(tx, user[1])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||
t.Fatal("Expected error")
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// simple call tx with create permission should fail
|
||||
tx, _ = NewCallTx(blockCache, user[3].PubKey, user[4].Address, nil, 100, 100, 100)
|
||||
SignCallTx(tx, user[3])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||
t.Fatal("Expected error")
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
//-------------------
|
||||
// create txs
|
||||
|
||||
// simple call create tx should fail
|
||||
tx, _ = NewCallTx(blockCache, user[0].PubKey, nil, nil, 100, 100, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||
t.Fatal("Expected error")
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// simple call create tx with send perm should fail
|
||||
tx, _ = NewCallTx(blockCache, user[1].PubKey, nil, nil, 100, 100, 100)
|
||||
SignCallTx(tx, user[1])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||
t.Fatal("Expected error")
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// simple call create tx with call perm should fail
|
||||
tx, _ = NewCallTx(blockCache, user[2].PubKey, nil, nil, 100, 100, 100)
|
||||
SignCallTx(tx, user[2])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||
t.Fatal("Expected error")
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendPermission(t *testing.T) {
|
||||
stateDB := dbm.GetDB("state")
|
||||
genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse)
|
||||
genDoc.Accounts[0].Permissions.Send = true // give the 0 account permission
|
||||
st := MakeGenesisState(stateDB, &genDoc)
|
||||
blockCache := NewBlockCache(st)
|
||||
|
||||
// A single input, having the permission, should succeed
|
||||
tx := NewSendTx()
|
||||
if err := SendTxAddInput(blockCache, tx, user[0].PubKey, 5); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
SendTxAddOutput(tx, user[1].Address, 5)
|
||||
SignSendTx(tx, 0, user[0])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err != nil {
|
||||
t.Fatal("Transaction failed", err)
|
||||
}
|
||||
|
||||
// Two inputs, one with permission, one without, should fail
|
||||
tx = NewSendTx()
|
||||
if err := SendTxAddInput(blockCache, tx, user[0].PubKey, 5); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := SendTxAddInput(blockCache, tx, user[1].PubKey, 5); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
SendTxAddOutput(tx, user[2].Address, 10)
|
||||
SignSendTx(tx, 0, user[0])
|
||||
SignSendTx(tx, 1, user[1])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||
t.Fatal("Expected error")
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// convenience function for contract that calls a given address
|
||||
func callContractCode(contractAddr []byte) []byte {
|
||||
gas1, gas2 := byte(0x1), byte(0x1)
|
||||
value := byte(0x1)
|
||||
inOff, inSize := byte(0x0), byte(0x0) // no call data
|
||||
retOff, retSize := byte(0x0), byte(0x20)
|
||||
// this is the code we want to run (call a contract and return)
|
||||
contractCode := []byte{0x60, retSize, 0x60, retOff, 0x60, inSize, 0x60, inOff, 0x60, value, 0x73}
|
||||
contractCode = append(contractCode, contractAddr...)
|
||||
contractCode = append(contractCode, []byte{0x61, gas1, gas2, 0xf1, 0x60, 0x20, 0x60, 0x0, 0xf3}...)
|
||||
return contractCode
|
||||
}
|
||||
|
||||
func TestCallPermission(t *testing.T) {
|
||||
stateDB := dbm.GetDB("state")
|
||||
genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse)
|
||||
genDoc.Accounts[0].Permissions.Call = true // give the 0 account permission
|
||||
st := MakeGenesisState(stateDB, &genDoc)
|
||||
blockCache := NewBlockCache(st)
|
||||
|
||||
//------------------------------
|
||||
// call to simple contract
|
||||
fmt.Println("##### SIMPLE CONTRACT")
|
||||
|
||||
// create simple contract
|
||||
simpleContractAddr := NewContractAddress(user[0].Address, 100)
|
||||
simpleAcc := &account.Account{
|
||||
Address: simpleContractAddr,
|
||||
Balance: 0,
|
||||
Code: []byte{0x60},
|
||||
Sequence: 0,
|
||||
StorageRoot: Zero256.Bytes(),
|
||||
}
|
||||
st.UpdateAccount(simpleAcc)
|
||||
|
||||
// A single input, having the permission, should succeed
|
||||
tx, _ := NewCallTx(blockCache, user[0].PubKey, simpleContractAddr, nil, 100, 100, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err != nil {
|
||||
t.Fatal("Transaction failed", err)
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// call to contract that calls simple contract - without perm
|
||||
fmt.Println("##### CALL TO SIMPLE CONTRACT (FAIL)")
|
||||
|
||||
// create contract that calls the simple contract
|
||||
contractCode := callContractCode(simpleContractAddr)
|
||||
caller1ContractAddr := NewContractAddress(user[0].Address, 101)
|
||||
caller1Acc := &account.Account{
|
||||
Address: caller1ContractAddr,
|
||||
Balance: 0,
|
||||
Code: contractCode,
|
||||
Sequence: 0,
|
||||
StorageRoot: Zero256.Bytes(),
|
||||
}
|
||||
blockCache.UpdateAccount(caller1Acc)
|
||||
|
||||
// A single input, having the permission, but the contract doesn't have permission
|
||||
tx, _ = NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
|
||||
// we need to subscribe to the Receive event to detect the exception
|
||||
exception := execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) //
|
||||
if exception == "" {
|
||||
t.Fatal("Expected exception")
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// call to contract that calls simple contract - with perm
|
||||
fmt.Println("##### CALL TO SIMPLE CONTRACT (PASS)")
|
||||
|
||||
// A single input, having the permission, and the contract has permission
|
||||
caller1Acc.Permissions.Call = true
|
||||
blockCache.UpdateAccount(caller1Acc)
|
||||
tx, _ = NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
|
||||
// we need to subscribe to the Receive event to detect the exception
|
||||
exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) //
|
||||
if exception != "" {
|
||||
t.Fatal("Unexpected exception:", exception)
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// call to contract that calls contract that calls simple contract - without perm
|
||||
// caller1Contract calls simpleContract. caller2Contract calls caller1Contract.
|
||||
// caller1Contract does not have call perms, but caller2Contract does.
|
||||
fmt.Println("##### CALL TO CONTRACT CALLING SIMPLE CONTRACT (FAIL)")
|
||||
|
||||
contractCode2 := callContractCode(caller1ContractAddr)
|
||||
caller2ContractAddr := NewContractAddress(user[0].Address, 102)
|
||||
caller2Acc := &account.Account{
|
||||
Address: caller2ContractAddr,
|
||||
Balance: 1000,
|
||||
Code: contractCode2,
|
||||
Sequence: 0,
|
||||
StorageRoot: Zero256.Bytes(),
|
||||
}
|
||||
caller1Acc.Permissions.Call = false
|
||||
caller2Acc.Permissions.Call = true
|
||||
blockCache.UpdateAccount(caller1Acc)
|
||||
blockCache.UpdateAccount(caller2Acc)
|
||||
|
||||
tx, _ = NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
|
||||
// we need to subscribe to the Receive event to detect the exception
|
||||
exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) //
|
||||
if exception == "" {
|
||||
t.Fatal("Expected exception")
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// call to contract that calls contract that calls simple contract - without perm
|
||||
// caller1Contract calls simpleContract. caller2Contract calls caller1Contract.
|
||||
// both caller1 and caller2 have permission
|
||||
fmt.Println("##### CALL TO CONTRACT CALLING SIMPLE CONTRACT (PASS)")
|
||||
|
||||
caller1Acc.Permissions.Call = true
|
||||
blockCache.UpdateAccount(caller1Acc)
|
||||
|
||||
tx, _ = NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
|
||||
// we need to subscribe to the Receive event to detect the exception
|
||||
exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) //
|
||||
if exception != "" {
|
||||
t.Fatal("Unexpected exception", exception)
|
||||
}
|
||||
}
|
||||
|
||||
// run ExecTx and wait for the Receive event on given addr
|
||||
// returns error/exception
|
||||
func execTxWaitEvent(t *testing.T, blockCache *BlockCache, tx types.Tx, addr []byte) string {
|
||||
evsw := new(events.EventSwitch)
|
||||
evsw.Start()
|
||||
ch := make(chan interface{})
|
||||
evsw.AddListenerForEvent("test", types.EventStringAccReceive(addr), func(msg interface{}) {
|
||||
ch <- msg
|
||||
})
|
||||
evc := events.NewEventCache(evsw)
|
||||
go func() {
|
||||
if err := ExecTx(blockCache, tx, true, evc); err != nil {
|
||||
ch <- err.Error()
|
||||
}
|
||||
evc.Flush()
|
||||
}()
|
||||
msg := <-ch
|
||||
switch ev := msg.(type) {
|
||||
case types.EventMsgCallTx:
|
||||
return ev.Exception
|
||||
case types.EventMsgCall:
|
||||
return ev.Exception
|
||||
case string:
|
||||
return ev
|
||||
}
|
||||
return ""
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
- CallTx (Create)
|
||||
x - 1 input, no perm, send perm, call perm
|
||||
- CallTx to create new contract, perm
|
||||
- contract runs create but doesn't have create perm
|
||||
- contract runs create but has perm
|
||||
- contract runs call with empty address (has call perm, not create perm)
|
||||
- contract runs call with empty address (has call perm, and create perm)
|
||||
- contract runs call (with perm), runs contract that runs create (without perm)
|
||||
- contract runs call (with perm), runs contract that runs create (with perm)
|
||||
*/
|
||||
|
||||
// TODO: this is just a copy of CALL...
|
||||
func TestCreatePermission(t *testing.T) {
|
||||
stateDB := dbm.GetDB("state")
|
||||
genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse)
|
||||
genDoc.Accounts[0].Permissions.Call = true // give the 0 account permission
|
||||
st := MakeGenesisState(stateDB, &genDoc)
|
||||
blockCache := NewBlockCache(st)
|
||||
|
||||
//------------------------------
|
||||
// create simple contract
|
||||
fmt.Println("##### SIMPLE CONTRACT")
|
||||
|
||||
// create simple contract
|
||||
simpleContractAddr := NewContractAddress(user[0].Address, 100)
|
||||
simpleAcc := &account.Account{
|
||||
Address: simpleContractAddr,
|
||||
Balance: 0,
|
||||
Code: []byte{0x60},
|
||||
Sequence: 0,
|
||||
StorageRoot: Zero256.Bytes(),
|
||||
}
|
||||
st.UpdateAccount(simpleAcc)
|
||||
|
||||
// A single input, having the permission, should succeed
|
||||
tx, _ := NewCallTx(blockCache, user[0].PubKey, simpleContractAddr, nil, 100, 100, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
if err := ExecTx(blockCache, tx, true, nil); err != nil {
|
||||
t.Fatal("Transaction failed", err)
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// call to contract that calls simple contract - without perm
|
||||
fmt.Println("##### CALL TO SIMPLE CONTRACT (FAIL)")
|
||||
|
||||
// create contract that calls the simple contract
|
||||
contractCode := callContractCode(simpleContractAddr)
|
||||
caller1ContractAddr := NewContractAddress(user[0].Address, 101)
|
||||
caller1Acc := &account.Account{
|
||||
Address: caller1ContractAddr,
|
||||
Balance: 0,
|
||||
Code: contractCode,
|
||||
Sequence: 0,
|
||||
StorageRoot: Zero256.Bytes(),
|
||||
}
|
||||
blockCache.UpdateAccount(caller1Acc)
|
||||
|
||||
// A single input, having the permission, but the contract doesn't have permission
|
||||
tx, _ = NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
|
||||
// we need to subscribe to the Receive event to detect the exception
|
||||
exception := execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) //
|
||||
if exception == "" {
|
||||
t.Fatal("Expected exception")
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// call to contract that calls simple contract - with perm
|
||||
fmt.Println("##### CALL TO SIMPLE CONTRACT (PASS)")
|
||||
|
||||
// A single input, having the permission, and the contract has permission
|
||||
caller1Acc.Permissions.Call = true
|
||||
blockCache.UpdateAccount(caller1Acc)
|
||||
tx, _ = NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
|
||||
// we need to subscribe to the Receive event to detect the exception
|
||||
exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) //
|
||||
if exception != "" {
|
||||
t.Fatal("Unexpected exception:", exception)
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// call to contract that calls contract that calls simple contract - without perm
|
||||
// caller1Contract calls simpleContract. caller2Contract calls caller1Contract.
|
||||
// caller1Contract does not have call perms, but caller2Contract does.
|
||||
fmt.Println("##### CALL TO CONTRACT CALLING SIMPLE CONTRACT (FAIL)")
|
||||
|
||||
contractCode2 := callContractCode(caller1ContractAddr)
|
||||
caller2ContractAddr := NewContractAddress(user[0].Address, 102)
|
||||
caller2Acc := &account.Account{
|
||||
Address: caller2ContractAddr,
|
||||
Balance: 1000,
|
||||
Code: contractCode2,
|
||||
Sequence: 0,
|
||||
StorageRoot: Zero256.Bytes(),
|
||||
}
|
||||
caller1Acc.Permissions.Call = false
|
||||
caller2Acc.Permissions.Call = true
|
||||
blockCache.UpdateAccount(caller1Acc)
|
||||
blockCache.UpdateAccount(caller2Acc)
|
||||
|
||||
tx, _ = NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
|
||||
// we need to subscribe to the Receive event to detect the exception
|
||||
exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) //
|
||||
if exception == "" {
|
||||
t.Fatal("Expected exception")
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// call to contract that calls contract that calls simple contract - without perm
|
||||
// caller1Contract calls simpleContract. caller2Contract calls caller1Contract.
|
||||
// both caller1 and caller2 have permission
|
||||
fmt.Println("##### CALL TO CONTRACT CALLING SIMPLE CONTRACT (PASS)")
|
||||
|
||||
caller1Acc.Permissions.Call = true
|
||||
blockCache.UpdateAccount(caller1Acc)
|
||||
|
||||
tx, _ = NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100)
|
||||
SignCallTx(tx, user[0])
|
||||
|
||||
// we need to subscribe to the Receive event to detect the exception
|
||||
exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) //
|
||||
if exception != "" {
|
||||
t.Fatal("Unexpected exception", exception)
|
||||
}
|
||||
}
|
||||
36
vm/vm.go
36
vm/vm.go
@@ -63,16 +63,21 @@ type VM struct {
|
||||
doug bool // is this the gendoug contract
|
||||
|
||||
sendPerm, callPerm, createPerm bool // this contract's permissions
|
||||
|
||||
getPerms func(*Account) (bool, bool, bool) // gets permissions out of an account (wraps toStateFunction to get perms out of Other)
|
||||
}
|
||||
|
||||
func NewVM(appState AppState, params Params, origin Word256, txid []byte) *VM {
|
||||
return &VM{
|
||||
appState: appState,
|
||||
params: params,
|
||||
origin: origin,
|
||||
callDepth: 0,
|
||||
txid: txid,
|
||||
doug: false,
|
||||
appState: appState,
|
||||
params: params,
|
||||
origin: origin,
|
||||
callDepth: 0,
|
||||
txid: txid,
|
||||
doug: false,
|
||||
sendPerm: true,
|
||||
callPerm: true,
|
||||
createPerm: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +91,19 @@ func (vm *VM) EnableDoug() {
|
||||
vm.doug = true
|
||||
}
|
||||
|
||||
func (vm *VM) SetPermissionsGetter(getPerms func(acc *Account) (bool, bool, bool)) {
|
||||
vm.getPerms = getPerms
|
||||
}
|
||||
|
||||
func (vm *VM) SetPermissions(acc *Account) {
|
||||
if vm.getPerms != nil {
|
||||
send, call, create := vm.getPerms(acc)
|
||||
vm.setPermissions(send, call, create)
|
||||
}
|
||||
}
|
||||
|
||||
// set the contract's generic permissions
|
||||
func (vm *VM) SetPermissions(send, call, create bool) {
|
||||
func (vm *VM) setPermissions(send, call, create bool) {
|
||||
// TODO: distinction between send and call not defined at the VM yet (it's all through a CALL!)
|
||||
vm.sendPerm = send
|
||||
vm.callPerm = call
|
||||
@@ -744,6 +760,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas
|
||||
ret, err = nativeContract(args, &gasLimit)
|
||||
} else if dougContract := dougContracts[addr]; vm.doug && dougContract != nil {
|
||||
// This is Doug and we're calling a doug contract
|
||||
// TODO: Doug contract should have all permissions
|
||||
ret, err = dougContract(args, &gasLimit)
|
||||
} else {
|
||||
// EVM contract
|
||||
@@ -769,14 +786,17 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas
|
||||
}
|
||||
vm.appState.UpdateAccount(acc)
|
||||
}
|
||||
// copy permissions and set permissions for new contract
|
||||
send, call, create := vm.sendPerm, vm.callPerm, vm.createPerm
|
||||
vm.SetPermissions(acc)
|
||||
ret, err = vm.Call(callee, acc, acc.Code, args, value, gas)
|
||||
vm.sendPerm, vm.callPerm, vm.createPerm = send, call, create
|
||||
}
|
||||
}
|
||||
|
||||
// Push result
|
||||
if err != nil {
|
||||
dbg.Printf("error on call: %s", err.Error())
|
||||
// TODO: fire event
|
||||
stack.Push(Zero256)
|
||||
} else {
|
||||
stack.Push(One256)
|
||||
|
||||
Reference in New Issue
Block a user