From 32e02acb0cb5b910337ee4cb0b38d5bf35a7f36e Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 21 May 2015 19:50:48 -0400 Subject: [PATCH] testing and fixes for snative CALL and CallTx --- state/execution.go | 35 ++-- state/permissions_test.go | 363 +++++++++++++++++++++++++++++++++++++- vm/snative.go | 27 ++- 3 files changed, 393 insertions(+), 32 deletions(-) diff --git a/state/execution.go b/state/execution.go index 9d3f4c6b0..5a8e3fd19 100644 --- a/state/execution.go +++ b/state/execution.go @@ -434,30 +434,29 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea // get or create callee if !createAccount { - if outAcc == nil { + + if outAcc == nil || len(outAcc.Code) == 0 { // check if its an snative if _, ok := vm.RegisteredSNativeContracts[LeftPadWord256(tx.Address)]; ok { // set the outAcc (simply a placeholder until we reach the call) outAcc = &account.Account{Address: tx.Address} - } - } - - if outAcc == nil || len(outAcc.Code) == 0 { - // if you call an account that doesn't exist - // or an account with no code then we take fees (sorry pal) - // NOTE: it's fine to create a contract and call it within one - // block (nonce will prevent re-ordering of those txs) - // but to create with one account and call with another - // you have to wait a block to avoid a re-ordering attack - // that will take your fees - inAcc.Balance -= tx.Fee - blockCache.UpdateAccount(inAcc) - if outAcc == nil { - log.Debug(Fmt("Cannot find destination address %X. Deducting fee from caller", tx.Address)) } else { - log.Debug(Fmt("Attempting to call an account (%X) with no code. Deducting fee from caller", tx.Address)) + // if you call an account that doesn't exist + // or an account with no code then we take fees (sorry pal) + // NOTE: it's fine to create a contract and call it within one + // block (nonce will prevent re-ordering of those txs) + // but to create with one account and call with another + // you have to wait a block to avoid a re-ordering attack + // that will take your fees + inAcc.Balance -= tx.Fee + blockCache.UpdateAccount(inAcc) + if outAcc == nil { + log.Debug(Fmt("Cannot find destination address %X. Deducting fee from caller", tx.Address)) + } else { + log.Debug(Fmt("Attempting to call an account (%X) with no code. Deducting fee from caller", tx.Address)) + } + return types.ErrTxInvalidAddress } - return types.ErrTxInvalidAddress } callee = toVMAccount(outAcc) code = callee.Code diff --git a/state/permissions_test.go b/state/permissions_test.go index 74deb2a4f..30794a51a 100644 --- a/state/permissions_test.go +++ b/state/permissions_test.go @@ -13,6 +13,7 @@ import ( "github.com/tendermint/tendermint/events" ptypes "github.com/tendermint/tendermint/permission/types" "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/vm" ) /* @@ -60,10 +61,6 @@ x - unknown output, without create (fail) x - unknown output, with create (pass) -- Gendoug: - - base: has,set,unset - - roles: has, add, r - */ // keys @@ -789,6 +786,278 @@ func TestCreateAccountPermission(t *testing.T) { } +/* +- SNative (CallTx, CALL): + - for each of CallTx, Call + - call each snative without permission, fails + - call each snative with permission, pass + - list: + - base: has,set,unset + - globals: set + - roles: has, add, r +*/ + +func TestSNativeCALL(t *testing.T) { + stateDB := dbm.GetDB("state") + genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) + genDoc.Accounts[0].Permissions.Base.Set(ptypes.Call, true) // give the 0 account permission + genDoc.Accounts[3].Permissions.Base.Set(ptypes.Bond, true) // some arbitrary permission to play with + genDoc.Accounts[3].Permissions.AddRole("bumble") + genDoc.Accounts[3].Permissions.AddRole("bee") + st := MakeGenesisState(stateDB, &genDoc) + blockCache := NewBlockCache(st) + + //---------------------------------------------------------- + // Test CALL to SNative contracts + + // make the main contract once + doug := &account.Account{ + Address: ptypes.DougAddress, + Balance: 0, + Code: nil, + Sequence: 0, + StorageRoot: Zero256.Bytes(), + Permissions: ptypes.NewAccountPermissions(), + } + doug.Permissions.Base.Set(ptypes.Call, true) + blockCache.UpdateAccount(doug) + + fmt.Println("#### hasBasePerm") + // hasBasePerm + snativeAddress, data := snativePermTestInput("hasBasePerm", user[3], ptypes.Bond, false) + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + // return value should be true or false as a 32 byte array... + if !IsZeros(ret[:31]) || ret[31] != byte(1) { + return fmt.Errorf("Expected 1. Got %X", ret) + } + return nil + }) + + fmt.Println("#### setBasePerm") + // setBasePerm + snativeAddress, data = snativePermTestInput("setBasePerm", user[3], ptypes.Bond, false) + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativePermTestInput("hasBasePerm", user[3], ptypes.Bond, false) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + // return value should be true or false as a 32 byte array... + if !IsZeros(ret) { + return fmt.Errorf("Expected 0. Got %X", ret) + } + return nil + }) + snativeAddress, data = snativePermTestInput("setBasePerm", user[3], ptypes.CreateContract, true) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativePermTestInput("hasBasePerm", user[3], ptypes.CreateContract, false) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + // return value should be true or false as a 32 byte array... + if !IsZeros(ret[:31]) || ret[31] != byte(1) { + return fmt.Errorf("Expected 1. Got %X", ret) + } + return nil + }) + + fmt.Println("#### unsetBasePerm") + // unsetBasePerm + snativeAddress, data = snativePermTestInput("unsetBasePerm", user[3], ptypes.CreateContract, false) + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativePermTestInput("hasBasePerm", user[3], ptypes.CreateContract, false) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + if !IsZeros(ret) { + return fmt.Errorf("Expected 0. Got %X", ret) + } + return nil + }) + + fmt.Println("#### setGlobalPerm") + // setGlobalPerm + snativeAddress, data = snativePermTestInput("setGlobalPerm", user[3], ptypes.CreateContract, true) + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativePermTestInput("hasBasePerm", user[3], ptypes.CreateContract, false) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + // return value should be true or false as a 32 byte array... + if !IsZeros(ret[:31]) || ret[31] != byte(1) { + return fmt.Errorf("Expected 1. Got %X", ret) + } + return nil + }) + + // clearBasePerm + // TODO + + fmt.Println("#### hasRole") + // hasRole + snativeAddress, data = snativeRoleTestInput("hasRole", user[3], "bumble") + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + if !IsZeros(ret[:31]) || ret[31] != byte(1) { + return fmt.Errorf("Expected 1. Got %X", ret) + } + return nil + }) + + fmt.Println("#### addRole") + // addRole + snativeAddress, data = snativeRoleTestInput("hasRole", user[3], "chuck") + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + if !IsZeros(ret) { + return fmt.Errorf("Expected 0. Got %X", ret) + } + return nil + }) + snativeAddress, data = snativeRoleTestInput("addRole", user[3], "chuck") + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativeRoleTestInput("hasRole", user[3], "chuck") + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + if !IsZeros(ret[:31]) || ret[31] != byte(1) { + return fmt.Errorf("Expected 1. Got %X", ret) + } + return nil + }) + + fmt.Println("#### rmRole") + // rmRole + snativeAddress, data = snativeRoleTestInput("rmRole", user[3], "chuck") + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativeRoleTestInput("hasRole", user[3], "chuck") + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + if !IsZeros(ret) { + return fmt.Errorf("Expected 0. Got %X", ret) + } + return nil + }) +} + +func TestSNativeCallTx(t *testing.T) { + stateDB := dbm.GetDB("state") + genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) + genDoc.Accounts[0].Permissions.Base.Set(ptypes.Call, true) // give the 0 account permission + genDoc.Accounts[3].Permissions.Base.Set(ptypes.Bond, true) // some arbitrary permission to play with + genDoc.Accounts[3].Permissions.AddRole("bumble") + genDoc.Accounts[3].Permissions.AddRole("bee") + st := MakeGenesisState(stateDB, &genDoc) + blockCache := NewBlockCache(st) + + //---------------------------------------------------------- + // Test CallTx to SNative contracts + var doug *account.Account = nil + + fmt.Println("#### hasBasePerm") + // hasBasePerm + snativeAddress, data := snativePermTestInput("hasBasePerm", user[3], ptypes.Bond, false) + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + // return value should be true or false as a 32 byte array... + if !IsZeros(ret[:31]) || ret[31] != byte(1) { + return fmt.Errorf("Expected 1. Got %X", ret) + } + return nil + }) + + fmt.Println("#### setBasePerm") + // setBasePerm + snativeAddress, data = snativePermTestInput("setBasePerm", user[3], ptypes.Bond, false) + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativePermTestInput("hasBasePerm", user[3], ptypes.Bond, false) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + // return value should be true or false as a 32 byte array... + if !IsZeros(ret) { + return fmt.Errorf("Expected 0. Got %X", ret) + } + return nil + }) + snativeAddress, data = snativePermTestInput("setBasePerm", user[3], ptypes.CreateContract, true) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativePermTestInput("hasBasePerm", user[3], ptypes.CreateContract, false) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + // return value should be true or false as a 32 byte array... + if !IsZeros(ret[:31]) || ret[31] != byte(1) { + return fmt.Errorf("Expected 1. Got %X", ret) + } + return nil + }) + + fmt.Println("#### unsetBasePerm") + // unsetBasePerm + snativeAddress, data = snativePermTestInput("unsetBasePerm", user[3], ptypes.CreateContract, false) + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativePermTestInput("hasBasePerm", user[3], ptypes.CreateContract, false) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + if !IsZeros(ret) { + return fmt.Errorf("Expected 0. Got %X", ret) + } + return nil + }) + + fmt.Println("#### setGlobalPerm") + // setGlobalPerm + snativeAddress, data = snativePermTestInput("setGlobalPerm", user[3], ptypes.CreateContract, true) + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativePermTestInput("hasBasePerm", user[3], ptypes.CreateContract, false) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + // return value should be true or false as a 32 byte array... + if !IsZeros(ret[:31]) || ret[31] != byte(1) { + return fmt.Errorf("Expected 1. Got %X", ret) + } + return nil + }) + + // clearBasePerm + // TODO + + fmt.Println("#### hasRole") + // hasRole + snativeAddress, data = snativeRoleTestInput("hasRole", user[3], "bumble") + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + if !IsZeros(ret[:31]) || ret[31] != byte(1) { + return fmt.Errorf("Expected 1. Got %X", ret) + } + return nil + }) + + fmt.Println("#### addRole") + // addRole + snativeAddress, data = snativeRoleTestInput("hasRole", user[3], "chuck") + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + if !IsZeros(ret) { + return fmt.Errorf("Expected 0. Got %X", ret) + } + return nil + }) + snativeAddress, data = snativeRoleTestInput("addRole", user[3], "chuck") + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativeRoleTestInput("hasRole", user[3], "chuck") + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + if !IsZeros(ret[:31]) || ret[31] != byte(1) { + return fmt.Errorf("Expected 1. Got %X", ret) + } + return nil + }) + + fmt.Println("#### rmRole") + // rmRole + snativeAddress, data = snativeRoleTestInput("rmRole", user[3], "chuck") + testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) + snativeAddress, data = snativeRoleTestInput("hasRole", user[3], "chuck") + testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { + if !IsZeros(ret) { + return fmt.Errorf("Expected 0. Got %X", ret) + } + return nil + }) +} + //------------------------------------------------------------------------------------- // helpers @@ -821,11 +1090,95 @@ 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)] + var addr []byte + if doug != nil { + contractCode := callContractCode(snativeAddress) + doug.Code = contractCode + doug.Permissions.Base.Set(perm, true) + blockCache.UpdateAccount(doug) + addr = doug.Address + } else { + acc := blockCache.GetAccount(user[0].Address) + acc.Permissions.Base.Set(perm, true) + blockCache.UpdateAccount(acc) + addr = snativeAddress + } + tx, _ := types.NewCallTx(blockCache, user[0].PubKey, addr, data, 100, 10000, 100) + tx.Sign(user[0]) + ev, exception := execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(snativeAddress)) // + if exception != "" { + t.Fatal("Unexpected exception", exception) + } + evv := ev.(types.EventMsgCall) + ret := evv.Return + if err := f(ret); err != nil { + t.Fatal(err) + } +} + +// assumes the contract has not been given the permission. calls the it, it calls the snative, expects to fail +func testSNativeCALLExpectFail(t *testing.T, blockCache *BlockCache, doug *account.Account, snativeAddress, data []byte) { + var addr []byte + if doug != nil { + contractCode := callContractCode(snativeAddress) + doug.Code = contractCode + blockCache.UpdateAccount(doug) + addr = doug.Address + } else { + addr = snativeAddress + } + tx, _ := types.NewCallTx(blockCache, user[0].PubKey, addr, data, 100, 10000, 100) + tx.Sign(user[0]) + fmt.Println("subscribing to", types.EventStringAccReceive(snativeAddress)) + _, exception := execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(snativeAddress)) + if exception == "" { + t.Fatal("Expected exception") + } +} + +func boolToWord256(v bool) Word256 { + var vint byte + if v { + vint = 0x1 + } else { + vint = 0x0 + } + return LeftPadWord256([]byte{vint}) +} + +func snativePermTestInput(name string, user *account.PrivAccount, perm ptypes.PermFlag, val bool) (addr []byte, data []byte) { + addr = LeftPadWord256([]byte(name)).Postfix(20) + switch name { + case "hasBasePerm", "unsetBasePerm": + data = LeftPadBytes(user.Address, 32) + data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...) + case "setBasePerm": + data = LeftPadBytes(user.Address, 32) + data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...) + data = append(data, boolToWord256(val).Bytes()...) + case "setGlobalPerm": + data = Uint64ToWord256(uint64(perm)).Bytes() + data = append(data, boolToWord256(val).Bytes()...) + case "clearBasePerm": + } + return +} + +func snativeRoleTestInput(name string, user *account.PrivAccount, role string) (addr []byte, data []byte) { + addr = LeftPadWord256([]byte(name)).Postfix(20) + data = LeftPadBytes(user.Address, 32) + data = append(data, LeftPadBytes([]byte(role), 32)...) + return +} + // convenience function for contract that calls a given address func callContractCode(contractAddr []byte) []byte { // calldatacopy into mem and use as input to call memOff, inputOff := byte(0x0), byte(0x0) - contractCode := []byte{0x60, memOff, 0x60, inputOff, 0x36, 0x37} + contractCode := []byte{0x36, 0x60, inputOff, 0x60, memOff, 0x37} gas1, gas2 := byte(0x1), byte(0x1) value := byte(0x1) diff --git a/vm/snative.go b/vm/snative.go index 741a522ee..67b3a6ccd 100644 --- a/vm/snative.go +++ b/vm/snative.go @@ -111,6 +111,7 @@ func (vm *VM) hasBasePerm(acc *Account, args []byte) (output []byte, err error) } else { permInt = 0x0 } + dbg.Printf("snative.hasBasePerm(0x%X, %b) = %v\n", addr.Postfix(20), permN, permInt) return LeftPadWord256([]byte{permInt}).Bytes(), nil } @@ -138,6 +139,7 @@ func (vm *VM) setBasePerm(acc *Account, args []byte) (output []byte, err error) return nil, err } vm.appState.UpdateAccount(vmAcc) + dbg.Printf("snative.setBasePerm(0x%X, %b, %v)\n", addr.Postfix(20), permN, permV) return perm.Bytes(), nil } @@ -163,6 +165,7 @@ func (vm *VM) unsetBasePerm(acc *Account, args []byte) (output []byte, err error return nil, err } vm.appState.UpdateAccount(vmAcc) + dbg.Printf("snative.unsetBasePerm(0x%X, %b)\n", addr.Postfix(20), permN) return permNum.Bytes(), nil } @@ -171,11 +174,11 @@ func (vm *VM) setGlobalPerm(acc *Account, args []byte) (output []byte, err error return nil, ErrInvalidPermission{acc.Address, "SetGlobalPerm"} } if len(args) != 2*32 { - return nil, fmt.Errorf("setGlobalPerm() takes three arguments (permission number, permission value)") + return nil, fmt.Errorf("setGlobalPerm() takes two arguments (permission number, permission value)") } var permNum, perm Word256 - copy(permNum[:], args[32:64]) - copy(perm[:], args[64:96]) + copy(permNum[:], args[:32]) + copy(perm[:], args[32:64]) vmAcc := vm.appState.GetAccount(ptypes.GlobalPermissionsAddress256) if vmAcc == nil { panic("cant find the global permissions account") @@ -189,6 +192,7 @@ func (vm *VM) setGlobalPerm(acc *Account, args []byte) (output []byte, err error return nil, err } vm.appState.UpdateAccount(vmAcc) + dbg.Printf("snative.setGlobalPerm(%b, %v)\n", permN, permV) return perm.Bytes(), nil } @@ -208,8 +212,8 @@ func (vm *VM) hasRole(acc *Account, args []byte) (output []byte, err error) { return nil, fmt.Errorf("hasRole() takes two arguments (address, role)") } var addr, role Word256 - copy(addr[:], args[32:64]) - copy(role[:], args[64:96]) + copy(addr[:], args[:32]) + copy(role[:], args[32:64]) vmAcc := vm.appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) @@ -221,6 +225,7 @@ func (vm *VM) hasRole(acc *Account, args []byte) (output []byte, err error) { } else { permInt = 0x0 } + dbg.Printf("snative.hasRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0) return LeftPadWord256([]byte{permInt}).Bytes(), nil } @@ -232,8 +237,8 @@ func (vm *VM) addRole(acc *Account, args []byte) (output []byte, err error) { return nil, fmt.Errorf("addRole() takes two arguments (address, role)") } var addr, role Word256 - copy(addr[:], args[32:64]) - copy(role[:], args[64:96]) + copy(addr[:], args[:32]) + copy(role[:], args[32:64]) vmAcc := vm.appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) @@ -245,6 +250,8 @@ func (vm *VM) addRole(acc *Account, args []byte) (output []byte, err error) { } else { permInt = 0x0 } + vm.appState.UpdateAccount(vmAcc) + dbg.Printf("snative.addRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0) return LeftPadWord256([]byte{permInt}).Bytes(), nil } @@ -256,8 +263,8 @@ func (vm *VM) rmRole(acc *Account, args []byte) (output []byte, err error) { return nil, fmt.Errorf("rmRole() takes two arguments (address, role)") } var addr, role Word256 - copy(addr[:], args[32:64]) - copy(role[:], args[64:96]) + copy(addr[:], args[:32]) + copy(role[:], args[32:64]) vmAcc := vm.appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) @@ -269,5 +276,7 @@ func (vm *VM) rmRole(acc *Account, args []byte) (output []byte, err error) { } else { permInt = 0x0 } + vm.appState.UpdateAccount(vmAcc) + dbg.Printf("snative.rmRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0) return LeftPadWord256([]byte{permInt}).Bytes(), nil }