diff --git a/state/permissions_test.go b/state/permissions_test.go index 1eadc0e6a..dc3484791 100644 --- a/state/permissions_test.go +++ b/state/permissions_test.go @@ -1091,7 +1091,7 @@ func execTxWaitEvent(t *testing.T, blockCache *BlockCache, tx types.Tx, eventid // give a contract perms for an snative, call it, it calls the snative, ensure the check funciton (f) succeeds func testSNativeCALLExpectPass(t *testing.T, blockCache *BlockCache, doug *account.Account, snativeAddress, data []byte, f func([]byte) error) { - perm := vm.RegisteredSNativeContracts[LeftPadWord256(snativeAddress)] + perm := vm.RegisteredSNativePermissions[LeftPadWord256(snativeAddress)] var addr []byte if doug != nil { contractCode := callContractCode(snativeAddress) diff --git a/vm/snative.go b/vm/snative.go index 67b3a6ccd..a216934c5 100644 --- a/vm/snative.go +++ b/vm/snative.go @@ -7,24 +7,8 @@ import ( ptypes "github.com/tendermint/tendermint/permission/types" ) -type ErrInvalidPermission struct { - Address Word256 - SNative string -} - -func (e ErrInvalidPermission) Error() string { - return fmt.Sprintf("Account %X does not have permission snative.%s", e.Address.Postfix(20), e.SNative) -} - -// Checks if a permission flag is valid (a known base chain or snative permission) -func ValidPermN(n ptypes.PermFlag) bool { - if n > ptypes.TopBasePermission && n < FirstSNativePerm { - return false - } else if n > TopSNativePermission { - return false - } - return true -} +//------------------------------------------------------------------------------------------------ +// Registered SNative contracts const ( // first 32 bits of BasePermission are for chain, second 32 are for snative @@ -45,41 +29,20 @@ const ( TopSNativePermission ptypes.PermFlag = FirstSNativePerm << (NumSNativePermissions - 1) ) -var RegisteredSNativeContracts = map[Word256]ptypes.PermFlag{ - LeftPadWord256([]byte("hasBasePerm")): HasBasePerm, - LeftPadWord256([]byte("setBasePerm")): SetBasePerm, - LeftPadWord256([]byte("unsetBasePerm")): UnsetBasePerm, - LeftPadWord256([]byte("setGlobalPerm")): SetGlobalPerm, - LeftPadWord256([]byte("hasRole")): HasRole, - LeftPadWord256([]byte("addRole")): AddRole, - LeftPadWord256([]byte("rmRole")): RmRole, +var RegisteredSNativeContracts = map[Word256]SNativeContract{ + LeftPadWord256([]byte("hasBasePerm")): hasBasePerm, + LeftPadWord256([]byte("setBasePerm")): setBasePerm, + LeftPadWord256([]byte("unsetBasePerm")): unsetBasePerm, + LeftPadWord256([]byte("setGlobalPerm")): setGlobalPerm, + LeftPadWord256([]byte("hasRole")): hasRole, + LeftPadWord256([]byte("addRole")): addRole, + LeftPadWord256([]byte("rmRole")): rmRole, } -// takes an account so it can check for permission to access the contract -// NOTE: the account is the currently executing account (the callee), not say an origin caller -type SNativeContract func(acc *Account, input []byte) (output []byte, err error) - -func (vm *VM) SNativeContract(name Word256) SNativeContract { - flag := RegisteredSNativeContracts[name] - switch flag { - case HasBasePerm: - return vm.hasBasePerm - case SetBasePerm: - return vm.setBasePerm - case UnsetBasePerm: - return vm.unsetBasePerm - case SetGlobalPerm: - return vm.setGlobalPerm - case HasRole: - return vm.hasRole - case AddRole: - return vm.addRole - case RmRole: - return vm.rmRole - default: - return nil - } -} +// Takes an appState so it can lookup/update accounts, +// an account to check for permission to access the snative contract +// and some input bytes (presumably 32byte words) +type SNativeContract func(appState AppState, acc *Account, input []byte) (output []byte, err error) //----------------------------------------------------------------------------- // snative are native contracts that can access and manipulate the chain state @@ -87,17 +50,15 @@ func (vm *VM) SNativeContract(name Word256) SNativeContract { // TODO: catch errors, log em, return 0s to the vm (should some errors cause exceptions though?) -func (vm *VM) hasBasePerm(acc *Account, args []byte) (output []byte, err error) { - if !vm.HasPermission(acc, HasBasePerm) { +func hasBasePerm(appState AppState, acc *Account, args []byte) (output []byte, err error) { + if !HasPermission(appState, acc, HasBasePerm) { return nil, ErrInvalidPermission{acc.Address, "HasBasePerm"} } 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) + addr, permNum := returnTwoArgs(args) + vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } @@ -106,7 +67,7 @@ func (vm *VM) hasBasePerm(acc *Account, args []byte) (output []byte, err error) return nil, ptypes.ErrInvalidPermission(permN) } var permInt byte - if vm.HasPermission(vmAcc, permN) { + if HasPermission(appState, vmAcc, permN) { permInt = 0x1 } else { permInt = 0x0 @@ -115,18 +76,15 @@ func (vm *VM) hasBasePerm(acc *Account, args []byte) (output []byte, err error) return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func (vm *VM) setBasePerm(acc *Account, args []byte) (output []byte, err error) { - if !vm.HasPermission(acc, SetBasePerm) { +func setBasePerm(appState AppState, acc *Account, args []byte) (output []byte, err error) { + if !HasPermission(appState, acc, SetBasePerm) { return nil, ErrInvalidPermission{acc.Address, "SetBasePerm"} } 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) + addr, permNum, perm := returnThreeArgs(args) + vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } @@ -138,22 +96,20 @@ func (vm *VM) setBasePerm(acc *Account, args []byte) (output []byte, err error) if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { return nil, err } - vm.appState.UpdateAccount(vmAcc) + appState.UpdateAccount(vmAcc) dbg.Printf("snative.setBasePerm(0x%X, %b, %v)\n", addr.Postfix(20), permN, permV) return perm.Bytes(), nil } -func (vm *VM) unsetBasePerm(acc *Account, args []byte) (output []byte, err error) { - if !vm.HasPermission(acc, UnsetBasePerm) { +func unsetBasePerm(appState AppState, acc *Account, args []byte) (output []byte, err error) { + if !HasPermission(appState, acc, UnsetBasePerm) { return nil, ErrInvalidPermission{acc.Address, "UnsetBasePerm"} } 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) + addr, permNum := returnTwoArgs(args) + vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } @@ -164,22 +120,20 @@ func (vm *VM) unsetBasePerm(acc *Account, args []byte) (output []byte, err error if err = vmAcc.Permissions.Base.Unset(permN); err != nil { return nil, err } - vm.appState.UpdateAccount(vmAcc) + appState.UpdateAccount(vmAcc) dbg.Printf("snative.unsetBasePerm(0x%X, %b)\n", addr.Postfix(20), permN) return permNum.Bytes(), nil } -func (vm *VM) setGlobalPerm(acc *Account, args []byte) (output []byte, err error) { - if !vm.HasPermission(acc, SetGlobalPerm) { +func setGlobalPerm(appState AppState, acc *Account, args []byte) (output []byte, err error) { + if !HasPermission(appState, acc, SetGlobalPerm) { return nil, ErrInvalidPermission{acc.Address, "SetGlobalPerm"} } if len(args) != 2*32 { return nil, fmt.Errorf("setGlobalPerm() takes two arguments (permission number, permission value)") } - var permNum, perm Word256 - copy(permNum[:], args[:32]) - copy(perm[:], args[32:64]) - vmAcc := vm.appState.GetAccount(ptypes.GlobalPermissionsAddress256) + permNum, perm := returnTwoArgs(args) + vmAcc := appState.GetAccount(ptypes.GlobalPermissionsAddress256) if vmAcc == nil { panic("cant find the global permissions account") } @@ -191,30 +145,28 @@ func (vm *VM) setGlobalPerm(acc *Account, args []byte) (output []byte, err error if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { return nil, err } - vm.appState.UpdateAccount(vmAcc) + appState.UpdateAccount(vmAcc) dbg.Printf("snative.setGlobalPerm(%b, %v)\n", permN, permV) return perm.Bytes(), nil } // TODO: needs access to an iterator ... -func (vm *VM) clearPerm(acc *Account, args []byte) (output []byte, err error) { - if !vm.HasPermission(acc, ClearBasePerm) { +func clearPerm(appState AppState, acc *Account, args []byte) (output []byte, err error) { + if !HasPermission(appState, acc, ClearBasePerm) { return nil, ErrInvalidPermission{acc.Address, "ClearPerm"} } return nil, nil } -func (vm *VM) hasRole(acc *Account, args []byte) (output []byte, err error) { - if !vm.HasPermission(acc, HasRole) { +func hasRole(appState AppState, acc *Account, args []byte) (output []byte, err error) { + if !HasPermission(appState, acc, HasRole) { return nil, ErrInvalidPermission{acc.Address, "HasRole"} } if len(args) != 2*32 { return nil, fmt.Errorf("hasRole() takes two arguments (address, role)") } - var addr, role Word256 - copy(addr[:], args[:32]) - copy(role[:], args[32:64]) - vmAcc := vm.appState.GetAccount(addr) + addr, role := returnTwoArgs(args) + vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } @@ -229,17 +181,15 @@ func (vm *VM) hasRole(acc *Account, args []byte) (output []byte, err error) { return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func (vm *VM) addRole(acc *Account, args []byte) (output []byte, err error) { - if !vm.HasPermission(acc, AddRole) { +func addRole(appState AppState, acc *Account, args []byte) (output []byte, err error) { + if !HasPermission(appState, acc, AddRole) { return nil, ErrInvalidPermission{acc.Address, "AddRole"} } if len(args) != 2*32 { return nil, fmt.Errorf("addRole() takes two arguments (address, role)") } - var addr, role Word256 - copy(addr[:], args[:32]) - copy(role[:], args[32:64]) - vmAcc := vm.appState.GetAccount(addr) + addr, role := returnTwoArgs(args) + vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } @@ -250,22 +200,20 @@ func (vm *VM) addRole(acc *Account, args []byte) (output []byte, err error) { } else { permInt = 0x0 } - vm.appState.UpdateAccount(vmAcc) + appState.UpdateAccount(vmAcc) dbg.Printf("snative.addRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0) return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func (vm *VM) rmRole(acc *Account, args []byte) (output []byte, err error) { - if !vm.HasPermission(acc, RmRole) { +func rmRole(appState AppState, acc *Account, args []byte) (output []byte, err error) { + if !HasPermission(appState, acc, RmRole) { return nil, ErrInvalidPermission{acc.Address, "RmRole"} } if len(args) != 2*32 { return nil, fmt.Errorf("rmRole() takes two arguments (address, role)") } - var addr, role Word256 - copy(addr[:], args[:32]) - copy(role[:], args[32:64]) - vmAcc := vm.appState.GetAccount(addr) + addr, role := returnTwoArgs(args) + vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } @@ -276,7 +224,55 @@ func (vm *VM) rmRole(acc *Account, args []byte) (output []byte, err error) { } else { permInt = 0x0 } - vm.appState.UpdateAccount(vmAcc) + appState.UpdateAccount(vmAcc) dbg.Printf("snative.rmRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0) return LeftPadWord256([]byte{permInt}).Bytes(), nil } + +//------------------------------------------------------------------------------------------------ +// Errors and utility funcs + +type ErrInvalidPermission struct { + Address Word256 + SNative string +} + +func (e ErrInvalidPermission) Error() string { + return fmt.Sprintf("Account %X does not have permission snative.%s", e.Address.Postfix(20), e.SNative) +} + +// Checks if a permission flag is valid (a known base chain or snative permission) +func ValidPermN(n ptypes.PermFlag) bool { + if n > ptypes.TopBasePermission && n < FirstSNativePerm { + return false + } else if n > TopSNativePermission { + return false + } + return true +} + +// assumes length has already been checked +func returnTwoArgs(args []byte) (a Word256, b Word256) { + copy(a[:], args[:32]) + copy(b[:], args[32:64]) + return +} + +// assumes length has already been checked +func returnThreeArgs(args []byte) (a Word256, b Word256, c Word256) { + copy(a[:], args[:32]) + copy(b[:], args[32:64]) + copy(c[:], args[64:96]) + return +} + +// mostly a convenience for testing +var RegisteredSNativePermissions = map[Word256]ptypes.PermFlag{ + LeftPadWord256([]byte("hasBasePerm")): HasBasePerm, + LeftPadWord256([]byte("setBasePerm")): SetBasePerm, + LeftPadWord256([]byte("unsetBasePerm")): UnsetBasePerm, + LeftPadWord256([]byte("setGlobalPerm")): SetGlobalPerm, + LeftPadWord256([]byte("hasRole")): HasRole, + LeftPadWord256([]byte("addRole")): AddRole, + LeftPadWord256([]byte("rmRole")): RmRole, +} diff --git a/vm/vm.go b/vm/vm.go index c7ce36116..f8c6e2bf2 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -90,10 +90,13 @@ func (vm *VM) EnablePermissions() { vm.perms = true } -func (vm *VM) HasPermission(acc *Account, perm ptypes.PermFlag) bool { +func HasPermission(appState AppState, acc *Account, perm ptypes.PermFlag) bool { v, err := acc.Permissions.Base.Get(perm) if _, ok := err.(ptypes.ErrValueNotSet); ok { - return vm.HasPermission(vm.appState.GetAccount(ptypes.GlobalPermissionsAddress256), perm) + if appState == nil { + panic(fmt.Sprintf("Global permission value not set for %b", perm)) + } + return HasPermission(nil, appState.GetAccount(ptypes.GlobalPermissionsAddress256), perm) } return v } @@ -119,8 +122,8 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value int64, gas // if code is empty, callee may be snative contract if vm.snative && len(code) == 0 { - if snativeContract := vm.SNativeContract(callee.Address); snativeContract != nil { - output, err = snativeContract(caller, input) + if snativeContract, ok := RegisteredSNativeContracts[callee.Address]; ok { + output, err = snativeContract(vm.appState, caller, input) if err != nil { *exception = err.Error() } @@ -700,7 +703,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas dbg.Printf(" => %v\n", log) case CREATE: // 0xF0 - if vm.perms && !vm.HasPermission(callee, ptypes.CreateContract) { + if vm.perms && !HasPermission(vm.appState, callee, ptypes.CreateContract) { return nil, ErrPermission{"create_contract"} } contractValue := stack.Pop64() @@ -728,7 +731,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas } case CALL, CALLCODE: // 0xF1, 0xF2 - if vm.perms && !vm.HasPermission(callee, ptypes.Call) { + if vm.perms && !HasPermission(vm.appState, callee, ptypes.Call) { return nil, ErrPermission{"call"} } gasLimit := stack.Pop64() @@ -779,7 +782,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas } else { // if we have not seen the account before, create it // so we can send funds - if vm.perms && !vm.HasPermission(caller, ptypes.CreateAccount) { + if vm.perms && !HasPermission(vm.appState, caller, ptypes.CreateAccount) { return nil, ErrPermission{"create_account"} } acc = &Account{Address: addr}