tendermint/state/permissions_test.go

425 lines
12 KiB
Go

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"
ptypes "github.com/tendermint/tendermint/permission/types"
"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 = ptypes.Permissions{
Send: false,
Call: false,
Create: false,
Bond: false,
}
)
func newBaseGenDoc(globalPerm, accountPerm ptypes.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(),
Permissions: ptypes.NilPermissions.Copy(),
}
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(),
Permissions: ptypes.NilPermissions.Copy(),
}
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(),
Permissions: ptypes.NilPermissions.Copy(),
}
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 ""
}