From ab2b1643cb2668b2806c0aeffeabb43efad44892 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 20 May 2015 02:01:33 -0400 Subject: [PATCH] snative --- state/execution.go | 129 +---------------------------------- vm/native.go | 10 --- vm/snative.go | 165 +++++++++++++++++++++++++++++++++++++++++++++ vm/vm.go | 20 ++++-- 4 files changed, 182 insertions(+), 142 deletions(-) create mode 100644 vm/snative.go diff --git a/state/execution.go b/state/execution.go index 679936f1a..fd06234f3 100644 --- a/state/execution.go +++ b/state/execution.go @@ -462,10 +462,8 @@ 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 (from the txCache) - // so the gendoug can make a native call to create accounts and update - // permissions - // setupDoug(vmach, txCache, _s) + // enables the snative contracts + vmach.EnableDoug() // setupDoug(vmach, txCache, _s) } ret, err := vmach.Call(caller, callee, code, tx.Data, value, &gas) @@ -831,126 +829,3 @@ func hasBondPermission(state AccountGetter, accs map[string]*account.Account) bo } return true } - -/* -// permission management functions -// get/set closures which bind the txCache (for modifying an accounts permissions) -// add/rm closures which bind txCache & state (for creating/removing permissions on *all* accounts - expensive!) -func setupDoug(vmach *vm.VM, txCache *TxCache, _s *State) { - - // get takes (address, permissionNum Word256), returns a permission int - getFunc := func(args []byte, gas *uint64) (output []byte, err error) { - if len(args) != 2*32 { - return nil, fmt.Errorf("Get() takes two arguments (address, permission number)") - } - var addr, permNum Word256 - copy(addr[:], args[:32]) - copy(permNum[:], args[32:64]) - vmAcc := txCache.GetAccount(addr) - if vmAcc == nil { - return nil, fmt.Errorf("Unknown account %X", addr) - } - stAcc := toStateAccount(vmAcc) - permN := uint(Uint64FromWord256(permNum)) - perm, err := stAcc.Permissions.Base.Get(permN) - if err != nil { - return nil, err - } - var permInt byte - if perm { - permInt = 0x1 - } else { - permInt = 0x0 - } - return LeftPadWord256([]byte{permInt}).Bytes(), nil - } - - // set takes (address, permissionNum, permissionValue Word256), returns the permission value - setFunc := func(args []byte, gas *uint64) (output []byte, err error) { - if len(args) != 3*32 { - return nil, fmt.Errorf("Set() takes three arguments (address, permission number, permission value)") - } - var addr, permNum, perm Word256 - copy(addr[:], args[:32]) - copy(permNum[:], args[32:64]) - copy(perm[:], args[64:96]) - vmAcc := txCache.GetAccount(addr) - if vmAcc == nil { - return nil, fmt.Errorf("Unknown account %X", addr) - } - stAcc := toStateAccount(vmAcc) - permN := uint(Uint64FromWord256(permNum)) - permV := !perm.IsZero() - if err = stAcc.Permissions.Base.Set(permN, permV); err != nil { - return nil, err - } - vmAcc = toVMAccount(stAcc) - txCache.UpdateAccount(vmAcc) - return perm.Bytes(), nil - } - - // add creates a new permission at the next available index and returns the index - addFunc := func(args []byte, gas *uint64) (output []byte, err error) { - if len(args) != 0 { - return nil, fmt.Errorf("Add() takes no arguments") - } - - accounts := _s.GetAccounts() - size := accounts.Size() - var l int - for i := uint64(0); i < size; i++ { - _, v := accounts.GetByIndex(uint64(i)) - acc := v.(*account.Account) - - if i == 0 { - l = len(acc.Permissions.Other) - } else if l != len(acc.Permissions.Other) { - panic(Fmt("Accounts have different numbers of permissions: %v, %v", l, acc.Permissions.Other)) - } - if _, err := acc.Permissions.Add(false); err != nil { - return nil, err - } - txCache.UpdateAccount(toVMAccount(acc)) - } - return Uint64ToWord256(uint64(l)).Bytes(), nil - } - - // rm takes (permissionNum) and removes the corresponding permission, shortening the `Other` list. - // returns the permissionNum - rmFunc := func(args []byte, gas *uint64) (output []byte, err error) { - if len(args) != 32 { - return nil, fmt.Errorf("Get() takes one argument (permissionNum)") - } - var permNum Word256 - copy(permNum[:], args[:32]) - permN := uint(Uint64FromWord256(permNum)) // danger? - - accounts := _s.GetAccounts() - size := accounts.Size() - var l int - for i := uint64(0); i < size; i++ { - _, v := accounts.GetByIndex(uint64(i)) - acc := v.(*account.Account) - - if i == 0 { - l = len(acc.Permissions.Other) - } else if l != len(acc.Permissions.Other) { - panic(Fmt("Accounts have different numbers of permissions: %v, %v", l, acc.Permissions.Other)) - } - acc.Permissions.Remove(permN) - txCache.UpdateAccount(toVMAccount(acc)) - } - return args, nil - - } - - // Set the native contract addresses and functions - vmach.SetDougFunc(RightPadWord256([]byte("get")), vm.NativeContract(getFunc)) - vmach.SetDougFunc(RightPadWord256([]byte("set")), vm.NativeContract(setFunc)) - vmach.SetDougFunc(RightPadWord256([]byte("add")), vm.NativeContract(addFunc)) - vmach.SetDougFunc(RightPadWord256([]byte("rm")), vm.NativeContract(rmFunc)) - - // must be called or else functions not accessible - vmach.EnableDoug() -} -*/ diff --git a/vm/native.go b/vm/native.go index 9fc444192..003f557ff 100644 --- a/vm/native.go +++ b/vm/native.go @@ -89,13 +89,3 @@ func identityFunc(input []byte, gas *int64) (output []byte, err error) { // Return identity return input, nil } - -//----------------------------------------------------------------------------- -// Doug Contracts are stateful and must be set with closures wrapping the current tx cache -// Note they should be reset to refresh the closure or it will be stale - -var dougContracts = make(map[Word256]NativeContract) - -func (vm *VM) SetDougFunc(n Word256, f NativeContract) { - dougContracts[n] = f -} diff --git a/vm/snative.go b/vm/snative.go new file mode 100644 index 000000000..a29f26e3d --- /dev/null +++ b/vm/snative.go @@ -0,0 +1,165 @@ +package vm + +import ( + "fmt" + + . "github.com/tendermint/tendermint/common" + ptypes "github.com/tendermint/tendermint/permission/types" +) + +type SNativeContract func(input []byte) (output []byte, err error) + +//----------------------------------------------------------------------------- +// snative are native contracts that can access and manipulate the chain state +// (in particular the permissions values) + +// TODO: catch errors, log em, return 0s to the vm (should some errors cause exceptions though?) + +func (vm *VM) hasBasePerm(args []byte) (output []byte, err error) { + if len(args) != 2*32 { + return nil, fmt.Errorf("hasBasePerm() takes two arguments (address, permission number)") + } + var addr, permNum Word256 + copy(addr[:], args[:32]) + copy(permNum[:], args[32:64]) + vmAcc := vm.appState.GetAccount(addr) + if vmAcc == nil { + return nil, fmt.Errorf("Unknown account %X", addr) + } + permN := ptypes.PermFlag(Uint64FromWord256(permNum)) + var permInt byte + if vm.HasPermission(vmAcc, permN) { + permInt = 0x1 + } else { + permInt = 0x0 + } + return LeftPadWord256([]byte{permInt}).Bytes(), nil +} + +func (vm *VM) setBasePerm(args []byte) (output []byte, err error) { + if len(args) != 3*32 { + return nil, fmt.Errorf("setBasePerm() takes three arguments (address, permission number, permission value)") + } + var addr, permNum, perm Word256 + copy(addr[:], args[:32]) + copy(permNum[:], args[32:64]) + copy(perm[:], args[64:96]) + vmAcc := vm.appState.GetAccount(addr) + if vmAcc == nil { + return nil, fmt.Errorf("Unknown account %X", addr) + } + permN := ptypes.PermFlag(Uint64FromWord256(permNum)) + permV := !perm.IsZero() + if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { + return nil, err + } + vm.appState.UpdateAccount(vmAcc) + return perm.Bytes(), nil +} + +func (vm *VM) unsetBasePerm(args []byte) (output []byte, err error) { + if len(args) != 2*32 { + return nil, fmt.Errorf("unsetBasePerm() takes two arguments (address, permission number)") + } + var addr, permNum Word256 + copy(addr[:], args[:32]) + copy(permNum[:], args[32:64]) + vmAcc := vm.appState.GetAccount(addr) + if vmAcc == nil { + return nil, fmt.Errorf("Unknown account %X", addr) + } + permN := ptypes.PermFlag(Uint64FromWord256(permNum)) + if err = vmAcc.Permissions.Base.Unset(permN); err != nil { + return nil, err + } + vm.appState.UpdateAccount(vmAcc) + return permNum.Bytes(), nil +} + +func (vm *VM) setGlobalPerm(args []byte) (output []byte, err error) { + if len(args) != 2*32 { + return nil, fmt.Errorf("setGlobalPerm() takes three arguments (permission number, permission value)") + } + var permNum, perm Word256 + copy(permNum[:], args[32:64]) + copy(perm[:], args[64:96]) + vmAcc := vm.appState.GetAccount(ptypes.GlobalPermissionsAddress256) + if vmAcc == nil { + panic("cant find the global permissions account") + } + permN := ptypes.PermFlag(Uint64FromWord256(permNum)) + permV := !perm.IsZero() + if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { + return nil, err + } + vm.appState.UpdateAccount(vmAcc) + return perm.Bytes(), nil +} + +// TODO: needs access to an iterator ... +func (vm *VM) clearPerm(args []byte) (output []byte, err error) { + return nil, nil +} + +func (vm *VM) hasRole(args []byte) (output []byte, err error) { + if len(args) != 2*32 { + return nil, fmt.Errorf("hasRole() takes two arguments (address, role)") + } + var addr, role Word256 + copy(addr[:], args[32:64]) + copy(role[:], args[64:96]) + vmAcc := vm.appState.GetAccount(addr) + if vmAcc == nil { + return nil, fmt.Errorf("Unknown account %X", addr) + } + roleS := string(role.Bytes()) + var permInt byte + if vmAcc.Permissions.HasRole(roleS) { + permInt = 0x1 + } else { + permInt = 0x0 + } + return LeftPadWord256([]byte{permInt}).Bytes(), nil +} + +func (vm *VM) addRole(args []byte) (output []byte, err error) { + if len(args) != 2*32 { + return nil, fmt.Errorf("addRole() takes two arguments (address, role)") + } + var addr, role Word256 + copy(addr[:], args[32:64]) + copy(role[:], args[64:96]) + vmAcc := vm.appState.GetAccount(addr) + if vmAcc == nil { + return nil, fmt.Errorf("Unknown account %X", addr) + } + roleS := string(role.Bytes()) + var permInt byte + if vmAcc.Permissions.AddRole(roleS) { + permInt = 0x1 + } else { + permInt = 0x0 + } + return LeftPadWord256([]byte{permInt}).Bytes(), nil +} + +func (vm *VM) rmRole(args []byte) (output []byte, err error) { + if len(args) != 2*32 { + return nil, fmt.Errorf("rmRole() takes two arguments (address, role)") + } + var addr, role Word256 + copy(addr[:], args[32:64]) + copy(role[:], args[64:96]) + vmAcc := vm.appState.GetAccount(addr) + if vmAcc == nil { + return nil, fmt.Errorf("Unknown account %X", addr) + } + roleS := string(role.Bytes()) + var permInt byte + if vmAcc.Permissions.RmRole(roleS) { + permInt = 0x1 + } else { + permInt = 0x0 + } + return LeftPadWord256([]byte{permInt}).Bytes(), nil +} diff --git a/vm/vm.go b/vm/vm.go index cc63eb5f2..375e28496 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -61,8 +61,9 @@ type VM struct { evc events.Fireable - doug bool // is this the gendoug contract - perms bool // permission checking can be turned off + doug bool // is this the gendoug contract + perms bool // permission checking can be turned off + snativeContracts map[Word256]SNativeContract } func NewVM(appState AppState, params Params, origin Word256, txid []byte) *VM { @@ -84,6 +85,15 @@ func (vm *VM) SetFireable(evc events.Fireable) { // to allow calls to native DougContracts (off by default) func (vm *VM) EnableDoug() { vm.doug = true + vm.snativeContracts = map[Word256]SNativeContract{ + RightPadWord256([]byte("hasBasePerm")): vm.hasBasePerm, + RightPadWord256([]byte("setBasePerm")): vm.setBasePerm, + RightPadWord256([]byte("unsetBasePerm")): vm.unsetBasePerm, + RightPadWord256([]byte("setGlobalPerm")): vm.setGlobalPerm, + RightPadWord256([]byte("hasRole")): vm.hasRole, + RightPadWord256([]byte("addRole")): vm.addRole, + RightPadWord256([]byte("rmRole")): vm.rmRole, + } } // run permission checks before call and create @@ -747,10 +757,10 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas if nativeContract := nativeContracts[addr]; nativeContract != nil { // Native contract ret, err = nativeContract(args, &gasLimit) - } else if dougContract := dougContracts[addr]; vm.doug && dougContract != nil { - // This is Doug and we're calling a doug contract + } else if snativeContract := vm.snativeContracts[addr]; vm.doug && snativeContract != nil { + // This is Doug and we're calling a snative contract // TODO: Doug contract should have all permissions - ret, err = dougContract(args, &gasLimit) + ret, err = snativeContract(args) } else { // EVM contract if ok = useGas(gas, GasGetAccount); !ok {