Browse Source

move perms to vm.Account

pull/102/head
Ethan Buchman 10 years ago
committed by Jae Kwon
parent
commit
94f21ad012
10 changed files with 55 additions and 304 deletions
  1. +3
    -93
      account/account.go
  2. +2
    -1
      rpc/core/accounts.go
  3. +3
    -10
      state/execution.go
  4. +12
    -11
      state/genesis.go
  5. +6
    -134
      state/permissions_test.go
  6. +2
    -1
      state/test.go
  7. +8
    -14
      state/tx_cache.go
  8. +3
    -3
      vm/test/vm_test.go
  9. +3
    -0
      vm/types.go
  10. +13
    -37
      vm/vm.go

+ 3
- 93
account/account.go View File

@ -6,8 +6,8 @@ import (
"io"
"github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/merkle"
ptypes "github.com/tendermint/tendermint/permission/types"
)
// Signable is an interface for all signable things.
@ -45,7 +45,7 @@ type Account struct {
Code []byte `json:"code"` // VM code
StorageRoot []byte `json:"storage_root"` // VM storage merkle root.
Permissions Permissions `json:"permissions"`
Permissions *ptypes.Permissions `json:"permissions"`
}
func (acc *Account) Copy() *Account {
@ -73,100 +73,10 @@ var AccountCodec = binary.Codec{
//-----------------------------------------------------------------------------
var GlobalPermissionsAddress = LeftPadBytes([]byte{}, 20)
var DougAddress = GlobalPermissionsAddress
type Permission uint
const (
SendPermission Permission = iota
CallPermission
CreatePermission
BondPermission
NumBasePermissions = 4
)
type Permissions struct {
Send bool
Call bool
Create bool
Bond bool
Other []bool
}
func (p Permissions) Get(ty uint) (bool, error) {
tyP := Permission(ty)
switch tyP {
case SendPermission:
return p.Send, nil
case CallPermission:
return p.Call, nil
case CreatePermission:
return p.Create, nil
case BondPermission:
return p.Bond, nil
default:
ty = ty - 4
if ty <= uint(len(p.Other)-1) {
return p.Other[ty], nil
}
return false, fmt.Errorf("Unknown permission number %v", ty)
}
}
func (p Permissions) Set(ty uint, val bool) error {
tyP := Permission(ty)
switch tyP {
case SendPermission:
p.Send = val
case CallPermission:
p.Call = val
case CreatePermission:
p.Create = val
case BondPermission:
p.Bond = val
default:
ty = ty - 4
if ty <= uint(len(p.Other)-1) {
p.Other[ty] = val
return nil
}
return fmt.Errorf("Unknown permission number %v", ty)
}
return nil
}
// Add should be called on all accounts in tandem
func (p Permissions) Add(val bool) (uint, error) {
l := len(p.Other)
p.Other = append(p.Other, val)
return uint(l), nil
}
// Remove should be called on all accounts in tandem
func (p Permissions) Remove(ty uint) error {
if ty < uint(NumBasePermissions) || ty >= uint(len(p.Other)) {
return fmt.Errorf("Invalid permission number %v", ty)
}
// pop the permission out of the array
perms := p.Other[:ty]
if ty+1 < uint(len(p.Other)) {
perms = append(perms, p.Other[ty+1:]...)
}
p.Other = perms
return nil
}
// defaults for a Big Bad Public Blockchain
var DefaultPermissions = Permissions{
var DefaultPermissions = ptypes.Permissions{
Send: true,
Call: true,
Create: true,
Bond: true,
}
func (p Permissions) String() string {
return fmt.Sprintf("CanSend:%v, CanCall:%v, CanCreate:%v, CanBond:%v", p.Send, p.Call, p.Create, p.Bond)
}

+ 2
- 1
rpc/core/accounts.go View File

@ -4,6 +4,7 @@ import (
"fmt"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/common"
ptypes "github.com/tendermint/tendermint/permission/types"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
)
@ -22,7 +23,7 @@ func GetAccount(address []byte) (*acm.Account, error) {
Balance: 0,
Code: nil,
StorageRoot: nil,
Permissions: cache.GetAccount(acm.GlobalPermissionsAddress).Permissions,
Permissions: cache.GetAccount(ptypes.GlobalPermissionsAddress).Permissions,
}
}
return account, nil


+ 3
- 10
state/execution.go View File

@ -7,6 +7,7 @@ import (
"github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/events"
ptypes "github.com/tendermint/tendermint/permission/types" // for GlobalPermissionAddress ...
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/vm"
)
@ -177,7 +178,7 @@ func getOrMakeOutputs(state AccountGetter, accounts map[string]*account.Account,
PubKey: nil,
Sequence: 0,
Balance: 0,
Permissions: state.GetAccount(account.GlobalPermissionsAddress).Permissions,
Permissions: state.GetAccount(ptypes.GlobalPermissionsAddress).Permissions,
}
}
accounts[string(out.Address)] = acc
@ -442,7 +443,6 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
log.Debug(Fmt("Attempting to call an account (%X) with no code. Deducting fee from caller", tx.Address))
}
return types.ErrTxInvalidAddress
}
callee = toVMAccount(outAcc)
code = callee.Code
@ -458,6 +458,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
txCache.UpdateAccount(callee) // because we adjusted by input above.
vmach := vm.NewVM(txCache, params, caller.Address, account.HashSignBytes(_s.ChainID, tx))
vmach.SetFireable(evc)
vmach.EnablePermissions()
// NOTE: Call() transfers the value from caller to callee iff call succeeds.
if isDoug {
@ -466,14 +467,6 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
// permissions
setupDoug(vmach, txCache, _s)
}
// 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 := ""


+ 12
- 11
state/genesis.go View File

@ -10,15 +10,14 @@ import (
. "github.com/tendermint/tendermint/common"
dbm "github.com/tendermint/tendermint/db"
"github.com/tendermint/tendermint/merkle"
ptypes "github.com/tendermint/tendermint/permission/types"
"github.com/tendermint/tendermint/types"
)
type GenesisAccount struct {
Address []byte `json:"address"`
Amount int64 `json:"amount"`
Address []byte `json:"address"`
Amount uint64 `json:"amount"`
Permissions *account.Permissions `json:"global_permissions"` // pointer so optional
Address []byte `json:"address"`
Amount uint64 `json:"amount"`
Permissions *ptypes.Permissions `json:"global_permissions"` // pointer so optional
}
type GenesisValidator struct {
@ -29,7 +28,7 @@ type GenesisValidator struct {
type GenesisParams struct {
// Default permissions for newly created accounts
GlobalPermissions *account.Permissions `json:"global_permissions"`
GlobalPermissions *ptypes.Permissions `json:"global_permissions"`
// TODO: other params we may want to tweak?
}
@ -74,9 +73,10 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
// Make accounts state tree
accounts := merkle.NewIAVLTree(binary.BasicCodec, account.AccountCodec, defaultAccountsCacheCapacity, db)
for _, genAcc := range genDoc.Accounts {
perm := account.DefaultPermissions
perm_ := account.DefaultPermissions
perm := &perm_
if genAcc.Permissions != nil {
perm = *(genAcc.Permissions)
perm = genAcc.Permissions
}
acc := &account.Account{
Address: genAcc.Address,
@ -90,12 +90,13 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
// global permissions are saved as the 0 address
// so they are included in the accounts tree
globalPerms := account.DefaultPermissions
globalPerms_ := account.DefaultPermissions
globalPerms := &globalPerms_
if genDoc.Params != nil && genDoc.Params.GlobalPermissions != nil {
globalPerms = *(genDoc.Params.GlobalPermissions)
globalPerms = genDoc.Params.GlobalPermissions
}
permsAcc := &account.Account{
Address: account.GlobalPermissionsAddress,
Address: ptypes.GlobalPermissionsAddress,
PubKey: nil,
Sequence: 0,
Balance: 1337,


+ 6
- 134
state/permissions_test.go View File

@ -10,6 +10,7 @@ import (
. "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"
)
@ -61,7 +62,7 @@ func makeUsers(n int) []*account.PrivAccount {
}
var (
PermsAllFalse = account.Permissions{
PermsAllFalse = ptypes.Permissions{
Send: false,
Call: false,
Create: false,
@ -69,7 +70,7 @@ var (
}
)
func newBaseGenDoc(globalPerm, accountPerm account.Permissions) GenesisDoc {
func newBaseGenDoc(globalPerm, accountPerm ptypes.Permissions) GenesisDoc {
genAccounts := []GenesisAccount{}
for _, u := range user[:5] {
accPerm := accountPerm
@ -290,6 +291,7 @@ func TestCallPermission(t *testing.T) {
Code: []byte{0x60},
Sequence: 0,
StorageRoot: Zero256.Bytes(),
Permissions: ptypes.NilPermissions.Copy(),
}
st.UpdateAccount(simpleAcc)
@ -313,6 +315,7 @@ func TestCallPermission(t *testing.T) {
Code: contractCode,
Sequence: 0,
StorageRoot: Zero256.Bytes(),
Permissions: ptypes.NilPermissions.Copy(),
}
blockCache.UpdateAccount(caller1Acc)
@ -356,6 +359,7 @@ func TestCallPermission(t *testing.T) {
Code: contractCode2,
Sequence: 0,
StorageRoot: Zero256.Bytes(),
Permissions: ptypes.NilPermissions.Copy(),
}
caller1Acc.Permissions.Call = false
caller2Acc.Permissions.Call = true
@ -418,135 +422,3 @@ func execTxWaitEvent(t *testing.T, blockCache *BlockCache, tx types.Tx, addr []b
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)
}
}

+ 2
- 1
state/test.go View File

@ -24,12 +24,13 @@ func Tempfile(prefix string) (*os.File, string) {
func RandAccount(randBalance bool, minBalance int64) (*account.Account, *account.PrivAccount) {
privAccount := account.GenPrivAccount()
perms := account.DefaultPermissions
acc := &account.Account{
Address: privAccount.PubKey.Address(),
PubKey: privAccount.PubKey,
Sequence: RandInt(),
Balance: minBalance,
Permissions: account.DefaultPermissions,
Permissions: &perms,
}
if randBalance {
acc.Balance += int64(RandUint32())


+ 8
- 14
state/tx_cache.go View File

@ -3,6 +3,7 @@ package state
import (
ac "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/common"
ptypes "github.com/tendermint/tendermint/permission/types" // for GlobalPermissionAddress ...
"github.com/tendermint/tendermint/vm"
"github.com/tendermint/tendermint/vm/sha3"
)
@ -79,7 +80,8 @@ func (cache *TxCache) CreateAccount(creator *vm.Account) *vm.Account {
Code: nil,
Nonce: 0,
StorageRoot: Zero256,
Other: otherAccountInfo{nil, toStateAccount(cache.GetAccount(LeftPadWord256(ac.GlobalPermissionsAddress))).Permissions},
Permissions: cache.GetAccount(ptypes.GlobalPermissionsAddress256).Permissions,
Other: nil,
}
cache.accounts[addr] = vmAccountInfo{account, false}
return account
@ -155,12 +157,6 @@ func NewContractAddress(caller []byte, nonce int) []byte {
return sha3.Sha3(temp)[:20]
}
// struct for carrying data the vm need not know about
type otherAccountInfo struct {
PubKey ac.PubKey
Permissions ac.Permissions
}
// Converts backend.Account to vm.Account struct.
func toVMAccount(acc *ac.Account) *vm.Account {
return &vm.Account{
@ -169,20 +165,18 @@ func toVMAccount(acc *ac.Account) *vm.Account {
Code: acc.Code, // This is crazy.
Nonce: int64(acc.Sequence),
StorageRoot: LeftPadWord256(acc.StorageRoot),
Other: otherAccountInfo{acc.PubKey, acc.Permissions},
Permissions: acc.Permissions.Copy(),
Other: acc.PubKey,
}
}
// Converts vm.Account to backend.Account struct.
func toStateAccount(acc *vm.Account) *ac.Account {
otherInfo, ok := acc.Other.(otherAccountInfo)
pubKey, ok := acc.Other.(ac.PubKey)
if !ok {
panic("vm.Account.Other should be type state.otherAccountInfo")
pubKey = nil
}
pubKey := otherInfo.PubKey
perms := otherInfo.Permissions
var storageRoot []byte
if acc.StorageRoot.IsZero() {
storageRoot = nil
@ -196,7 +190,7 @@ func toStateAccount(acc *vm.Account) *ac.Account {
Code: acc.Code,
Sequence: int(acc.Nonce),
StorageRoot: storageRoot,
Permissions: perms,
Permissions: acc.Permissions,
}
}


+ 3
- 3
vm/test/vm_test.go View File

@ -53,9 +53,7 @@ func TestVM(t *testing.T) {
N := []byte{0x0f, 0x0f}
// Loop N times
code := []byte{0x60, 0x00, 0x60, 0x20, 0x52, 0x5B, byte(0x60 + len(N) - 1)}
for i := 0; i < len(N); i++ {
code = append(code, N[i])
}
code = append(code, N...)
code = append(code, []byte{0x60, 0x20, 0x51, 0x12, 0x15, 0x60, byte(0x1b + len(N)), 0x57, 0x60, 0x01, 0x60, 0x20, 0x51, 0x01, 0x60, 0x20, 0x52, 0x60, 0x05, 0x56, 0x5B}...)
start := time.Now()
output, err := ourVm.Call(account1, account2, code, []byte{}, 0, &gas)
@ -120,6 +118,7 @@ func TestSendCall(t *testing.T) {
//----------------------------------------------
// account2 has insufficient balance, should fail
fmt.Println("Should fail with insufficient balance")
exception := runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 1000)
if exception == "" {
@ -137,6 +136,7 @@ func TestSendCall(t *testing.T) {
//----------------------------------------------
// insufficient gas, should fail
fmt.Println("Should fail with insufficient gas")
account2.Balance = 100000
exception = runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 100)


+ 3
- 0
vm/types.go View File

@ -2,6 +2,7 @@ package vm
import (
. "github.com/tendermint/tendermint/common"
ptypes "github.com/tendermint/tendermint/permission/types"
)
const (
@ -15,6 +16,8 @@ type Account struct {
Nonce int64
StorageRoot Word256
Other interface{} // For holding all other data.
Permissions *ptypes.Permissions
}
func (acc *Account) String() string {


+ 13
- 37
vm/vm.go View File

@ -60,24 +60,18 @@ type VM struct {
evc events.Fireable
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)
doug bool // is this the gendoug contract
perms bool // permission checking can be turned off
}
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,
sendPerm: true,
callPerm: true,
createPerm: true,
appState: appState,
params: params,
origin: origin,
callDepth: 0,
txid: txid,
doug: false,
}
}
@ -91,23 +85,9 @@ 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) {
// TODO: distinction between send and call not defined at the VM yet (it's all through a CALL!)
vm.sendPerm = send
vm.callPerm = call
vm.createPerm = create
// run permission checks before call and create
func (vm *VM) EnablePermissions() {
vm.perms = true
}
// CONTRACT appState is aware of caller and callee, so we can just mutate them.
@ -701,7 +681,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas
dbg.Printf(" => %v\n", log)
case CREATE: // 0xF0
if !vm.createPerm {
if vm.perms && !callee.Permissions.Create {
return nil, ErrPermission{"create"}
}
contractValue := stack.Pop64()
@ -729,7 +709,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas
}
case CALL, CALLCODE: // 0xF1, 0xF2
if !vm.callPerm {
if vm.perms && !callee.Permissions.Call {
return nil, ErrPermission{"call"}
}
gasLimit := stack.Pop64()
@ -786,11 +766,7 @@ 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
}
}


Loading…
Cancel
Save