diff --git a/permission/types/permissions.go b/permission/types/permissions.go index 4eeec55c..74242ae4 100644 --- a/permission/types/permissions.go +++ b/permission/types/permissions.go @@ -17,19 +17,29 @@ type PermFlag uint64 // Base permission references are like unix (the index is already bit shifted) const ( - Root PermFlag = 1 << iota // 1 - Send // 2 - Call // 4 - CreateContract // 8 - CreateAccount // 16 - Bond // 32 - Name // 64 - NumBasePermissions uint = 7 // NOTE Adjust this too. + // chain permissions + Root PermFlag = 1 << iota // 1 + Send // 2 + Call // 4 + CreateContract // 8 + CreateAccount // 16 + Bond // 32 + Name // 64 - TopBasePermFlag PermFlag = 1 << (NumBasePermissions - 1) - AllBasePermFlags PermFlag = TopBasePermFlag | (TopBasePermFlag - 1) - AllPermFlags PermFlag = AllBasePermFlags | AllSNativePermFlags - DefaultBasePermFlags PermFlag = Send | Call | CreateContract | CreateAccount | Bond | Name + // moderator permissions + HasBase + SetBase + UnsetBase + SetGlobal + HasRole + AddRole + RmRole + + NumPermissions uint = 14 // NOTE Adjust this too. We can support upto 64 + + TopPermFlag PermFlag = 1 << (NumPermissions - 1) + AllPermFlags PermFlag = TopPermFlag | (TopPermFlag - 1) + DefaultPermFlags PermFlag = Send | Call | CreateContract | CreateAccount | Bond | Name | HasBase | HasRole ) var ( @@ -39,7 +49,7 @@ var ( } DefaultAccountPermissions = AccountPermissions{ Base: BasePermissions{ - Perms: DefaultBasePermFlags, + Perms: DefaultPermFlags, SetBit: AllPermFlags, }, Roles: []string{}, @@ -154,16 +164,8 @@ func (aP *AccountPermissions) RmRole(role string) bool { //-------------------------------------------------------------------------------- // string utilities -// CONTRACT: PermFlagToString functions assume the permFlag is valid, else return "#-UNKNOWN-#" -func PermFlagToString(pf PermFlag) string { - if pf < FirstSNativePermFlag { - return BasePermFlagToString(pf) - } else { - return SNativePermFlagToString(pf) - } -} - -func BasePermFlagToString(pf PermFlag) (perm string) { +// PermFlagToString assumes the permFlag is valid, else returns "#-UNKNOWN-#" +func PermFlagToString(pf PermFlag) (perm string) { switch pf { case Root: perm = "root" @@ -179,13 +181,27 @@ func BasePermFlagToString(pf PermFlag) (perm string) { perm = "bond" case Name: perm = "name" + case HasBase: + perm = "has_base" + case SetBase: + perm = "set_base" + case UnsetBase: + perm = "unset_base" + case SetGlobal: + perm = "set_global" + case HasRole: + perm = "has_role" + case AddRole: + perm = "add_role" + case RmRole: + perm = "rm_role" default: perm = "#-UNKNOWN-#" } return } -func BasePermStringToFlag(perm string) (pf PermFlag, err error) { +func PermStringToFlag(perm string) (pf PermFlag, err error) { switch perm { case "root": pf = Root @@ -201,6 +217,20 @@ func BasePermStringToFlag(perm string) (pf PermFlag, err error) { pf = Bond case "name": pf = Name + case "has_base": + pf = HasBase + case "set_base": + pf = SetBase + case "unset_base": + pf = UnsetBase + case "set_global": + pf = SetGlobal + case "has_role": + pf = HasRole + case "add_role": + pf = AddRole + case "rm_role": + pf = RmRole default: err = fmt.Errorf("Unknown permission %s", perm) } diff --git a/permission/types/snatives.go b/permission/types/snatives.go index b7537477..5057bd42 100644 --- a/permission/types/snatives.go +++ b/permission/types/snatives.go @@ -1,66 +1,40 @@ package types import ( - "fmt" - "github.com/tendermint/tendermint/binary" ) //--------------------------------------------------------------------------------------------------- -// snative permissions +// PermissionsTx.PermArgs interface and argument encoding -const ( - // first 32 bits of BasePermission are for chain, second 32 are for snative - FirstSNativePermFlag PermFlag = 1 << 32 -) - -// we need to reset iota with new const block -const ( - // each snative has an associated permission flag - HasBase PermFlag = FirstSNativePermFlag << iota - SetBase - UnsetBase - SetGlobal - HasRole - AddRole - RmRole - NumSNativePermissions uint = 7 // NOTE adjust this too - - TopSNativePermFlag PermFlag = FirstSNativePermFlag << (NumSNativePermissions - 1) - AllSNativePermFlags PermFlag = (TopSNativePermFlag | (TopSNativePermFlag - 1)) &^ (FirstSNativePermFlag - 1) -) - -//--------------------------------------------------------------------------------------------------- -// snative tx interface and argument encoding - -// SNativesArgs are a registered interface in the SNativeTx, -// so binary handles the arguments and each snative gets a type-byte +// Arguments are a registered interface in the PermissionsTx, +// so binary handles the arguments and each permission function gets a type-byte // PermFlag() maps the type-byte to the permission -// The account sending the SNativeTx must have this PermFlag set -type SNativeArgs interface { +// The account sending the PermissionsTx must have this PermFlag set +type PermArgs interface { PermFlag() PermFlag } const ( - SNativeArgsTypeHasBase = byte(0x01) - SNativeArgsTypeSetBase = byte(0x02) - SNativeArgsTypeUnsetBase = byte(0x03) - SNativeArgsTypeSetGlobal = byte(0x04) - SNativeArgsTypeHasRole = byte(0x05) - SNativeArgsTypeAddRole = byte(0x06) - SNativeArgsTypeRmRole = byte(0x07) + PermArgsTypeHasBase = byte(0x01) + PermArgsTypeSetBase = byte(0x02) + PermArgsTypeUnsetBase = byte(0x03) + PermArgsTypeSetGlobal = byte(0x04) + PermArgsTypeHasRole = byte(0x05) + PermArgsTypeAddRole = byte(0x06) + PermArgsTypeRmRole = byte(0x07) ) // for binary.readReflect var _ = binary.RegisterInterface( - struct{ SNativeArgs }{}, - binary.ConcreteType{&HasBaseArgs{}, SNativeArgsTypeHasBase}, - binary.ConcreteType{&SetBaseArgs{}, SNativeArgsTypeSetBase}, - binary.ConcreteType{&UnsetBaseArgs{}, SNativeArgsTypeUnsetBase}, - binary.ConcreteType{&SetGlobalArgs{}, SNativeArgsTypeSetGlobal}, - binary.ConcreteType{&HasRoleArgs{}, SNativeArgsTypeHasRole}, - binary.ConcreteType{&AddRoleArgs{}, SNativeArgsTypeAddRole}, - binary.ConcreteType{&RmRoleArgs{}, SNativeArgsTypeRmRole}, + struct{ PermArgs }{}, + binary.ConcreteType{&HasBaseArgs{}, PermArgsTypeHasBase}, + binary.ConcreteType{&SetBaseArgs{}, PermArgsTypeSetBase}, + binary.ConcreteType{&UnsetBaseArgs{}, PermArgsTypeUnsetBase}, + binary.ConcreteType{&SetGlobalArgs{}, PermArgsTypeSetGlobal}, + binary.ConcreteType{&HasRoleArgs{}, PermArgsTypeHasRole}, + binary.ConcreteType{&AddRoleArgs{}, PermArgsTypeAddRole}, + binary.ConcreteType{&RmRoleArgs{}, PermArgsTypeRmRole}, ) type HasBaseArgs struct { @@ -126,50 +100,3 @@ type RmRoleArgs struct { func (*RmRoleArgs) PermFlag() PermFlag { return RmRole } - -//------------------------------------------------------------ -// string utilities - -func SNativePermFlagToString(pF PermFlag) (perm string) { - switch pF { - case HasBase: - perm = "HasBase" - case SetBase: - perm = "SetBase" - case UnsetBase: - perm = "UnsetBase" - case SetGlobal: - perm = "SetGlobal" - case HasRole: - perm = "HasRole" - case AddRole: - perm = "AddRole" - case RmRole: - perm = "RmRole" - default: - perm = "#-UNKNOWN-#" - } - return -} - -func SNativeStringToPermFlag(perm string) (pF PermFlag, err error) { - switch perm { - case "HasBase": - pF = HasBase - case "SetBase": - pF = SetBase - case "UnsetBase": - pF = UnsetBase - case "SetGlobal": - pF = SetGlobal - case "HasRole": - pF = HasRole - case "AddRole": - pF = AddRole - case "RmRole": - pF = RmRole - default: - err = fmt.Errorf("Unknown permission %s", perm) - } - return -} diff --git a/state/execution.go b/state/execution.go index 71ae09c2..7b43c120 100644 --- a/state/execution.go +++ b/state/execution.go @@ -392,7 +392,6 @@ func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireab // this may be nil if we are still in mempool and contract was created in same block as this tx // but that's fine, because the account will be created properly when the create tx runs in the block // and then this won't return nil. otherwise, we take their fee - // it may also be nil if its an snative (not a "real" account) outAcc = blockCache.GetAccount(tx.Address) } @@ -423,10 +422,9 @@ func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireab if !createAccount { if outAcc == nil || len(outAcc.Code) == 0 { - // check if its an snative - // TODO: should we restrict from calling natives too? - if _, ok := vm.RegisteredSNativeContracts[LeftPadWord256(tx.Address)]; ok { - return fmt.Errorf("SNatives can not be called using CallTx. Either use a contract or a SNativeTx") + // check if its a native contract + if vm.RegisteredNativeContract(LeftPadWord256(tx.Address)) { + return fmt.Errorf("NativeContracts can not be called using CallTx. Use a contract or the appropriate tx type (eg. PermissionsTx, NameTx)") } // if you call an account that doesn't exist @@ -794,7 +792,7 @@ func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireab } return nil - case *types.SNativeTx: + case *types.PermissionsTx: var inAcc *acm.Account // Validate input @@ -804,10 +802,10 @@ func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireab return types.ErrTxInvalidAddress } - permFlag := tx.SNative.PermFlag() + permFlag := tx.PermArgs.PermFlag() // check permission - if !hasSNativePermission(blockCache, inAcc, permFlag) { - return fmt.Errorf("Account %X does not have permission to call snative %s (%b)", tx.Input.Address, ptypes.SNativePermFlagToString(permFlag), permFlag) + if !HasPermission(blockCache, inAcc, permFlag) { + return fmt.Errorf("Account %X does not have moderator permission %s (%b)", tx.Input.Address, ptypes.PermFlagToString(permFlag), permFlag) } // pubKey should be present in either "inAcc" or "tx.Input" @@ -824,10 +822,10 @@ func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireab value := tx.Input.Amount - log.Debug("New SNativeTx", "snative", ptypes.SNativePermFlagToString(permFlag), "args", tx.SNative) + log.Debug("New PermissionsTx", "function", ptypes.PermFlagToString(permFlag), "args", tx.PermArgs) var permAcc *acm.Account - switch args := tx.SNative.(type) { + switch args := tx.PermArgs.(type) { case *ptypes.HasBaseArgs: // this one doesn't make sense from txs return fmt.Errorf("HasBase is for contracts, not humans. Just look at the blockchain") @@ -863,7 +861,7 @@ func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireab return fmt.Errorf("Role (%s) does not exist for account %X", args.Role, args.Address) } default: - PanicSanity(Fmt("invalid snative: %s", ptypes.SNativePermFlagToString(permFlag))) + PanicSanity(Fmt("invalid permission function: %s", ptypes.PermFlagToString(permFlag))) } // TODO: maybe we want to take funds on error and allow txs in that don't do anythingi? @@ -881,7 +879,7 @@ func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireab if evc != nil { evc.FireEvent(types.EventStringAccInput(tx.Input.Address), tx) - evc.FireEvent(types.EventStringSNative(ptypes.SNativePermFlagToString(permFlag)), tx) + evc.FireEvent(types.EventStringPermissions(ptypes.PermFlagToString(permFlag)), tx) } return nil @@ -897,8 +895,7 @@ func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireab // Get permission on an account or fall back to global value func HasPermission(state AccountGetter, acc *acm.Account, perm ptypes.PermFlag) bool { - if (perm > ptypes.AllBasePermFlags && perm < ptypes.FirstSNativePermFlag) || - (perm > ptypes.AllSNativePermFlags) { + if perm > ptypes.AllPermFlags { PanicSanity("Checking an unknown permission in state should never happen") } @@ -969,7 +966,3 @@ func hasBondOrSendPermission(state AccountGetter, accs map[string]*acm.Account) } return true } - -func hasSNativePermission(state AccountGetter, acc *acm.Account, permFlag ptypes.PermFlag) bool { - return HasPermission(state, acc, permFlag) -} diff --git a/state/permissions_test.go b/state/permissions_test.go index de0cf207..162fb154 100644 --- a/state/permissions_test.go +++ b/state/permissions_test.go @@ -867,7 +867,7 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### HasBase") // HasBase - snativeAddress, data := snativePermTestInputCALL("HasBase", user[3], ptypes.Bond, false) + snativeAddress, data := snativePermTestInputCALL("has_base", 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... @@ -879,10 +879,10 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### SetBase") // SetBase - snativeAddress, data = snativePermTestInputCALL("SetBase", user[3], ptypes.Bond, false) + snativeAddress, data = snativePermTestInputCALL("set_base", 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 = snativePermTestInputCALL("HasBase", user[3], ptypes.Bond, false) + snativeAddress, data = snativePermTestInputCALL("has_base", 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) { @@ -890,9 +890,9 @@ func TestSNativeCALL(t *testing.T) { } return nil }) - snativeAddress, data = snativePermTestInputCALL("SetBase", user[3], ptypes.CreateContract, true) + snativeAddress, data = snativePermTestInputCALL("set_base", user[3], ptypes.CreateContract, true) testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, data = snativePermTestInputCALL("HasBase", user[3], ptypes.CreateContract, false) + snativeAddress, data = snativePermTestInputCALL("has_base", 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) { @@ -903,10 +903,10 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### UnsetBase") // UnsetBase - snativeAddress, data = snativePermTestInputCALL("UnsetBase", user[3], ptypes.CreateContract, false) + snativeAddress, data = snativePermTestInputCALL("unset_base", 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 = snativePermTestInputCALL("HasBase", user[3], ptypes.CreateContract, false) + snativeAddress, data = snativePermTestInputCALL("has_base", 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) @@ -916,10 +916,10 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### SetGlobal") // SetGlobalPerm - snativeAddress, data = snativePermTestInputCALL("SetGlobal", user[3], ptypes.CreateContract, true) + snativeAddress, data = snativePermTestInputCALL("set_global", 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 = snativePermTestInputCALL("HasBase", user[3], ptypes.CreateContract, false) + snativeAddress, data = snativePermTestInputCALL("has_base", 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) { @@ -930,7 +930,7 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### HasRole") // HasRole - snativeAddress, data = snativeRoleTestInputCALL("HasRole", user[3], "bumble") + snativeAddress, data = snativeRoleTestInputCALL("has_role", 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) { @@ -941,17 +941,17 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### AddRole") // AddRole - snativeAddress, data = snativeRoleTestInputCALL("HasRole", user[3], "chuck") + snativeAddress, data = snativeRoleTestInputCALL("has_role", 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 = snativeRoleTestInputCALL("AddRole", user[3], "chuck") + snativeAddress, data = snativeRoleTestInputCALL("add_role", user[3], "chuck") testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, data = snativeRoleTestInputCALL("HasRole", user[3], "chuck") + snativeAddress, data = snativeRoleTestInputCALL("has_role", 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) @@ -961,10 +961,10 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### RmRole") // RmRole - snativeAddress, data = snativeRoleTestInputCALL("RmRole", user[3], "chuck") + snativeAddress, data = snativeRoleTestInputCALL("rm_role", user[3], "chuck") testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, data = snativeRoleTestInputCALL("HasRole", user[3], "chuck") + snativeAddress, data = snativeRoleTestInputCALL("has_role", user[3], "chuck") testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { if !IsZeros(ret) { return fmt.Errorf("Expected 0. Got %X", ret) @@ -988,14 +988,14 @@ func TestSNativeTx(t *testing.T) { fmt.Println("\n#### SetBase") // SetBase - snativeArgs := snativePermTestInputTx("SetBase", user[3], ptypes.Bond, false) + snativeArgs := snativePermTestInputTx("set_base", user[3], ptypes.Bond, false) testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.SetBase, snativeArgs) acc := blockCache.GetAccount(user[3].Address) if v, _ := acc.Permissions.Base.Get(ptypes.Bond); v { t.Fatal("expected permission to be set false") } - snativeArgs = snativePermTestInputTx("SetBase", user[3], ptypes.CreateContract, true) + snativeArgs = snativePermTestInputTx("set_base", user[3], ptypes.CreateContract, true) testSNativeTxExpectPass(t, blockCache, ptypes.SetBase, snativeArgs) acc = blockCache.GetAccount(user[3].Address) if v, _ := acc.Permissions.Base.Get(ptypes.CreateContract); !v { @@ -1004,7 +1004,7 @@ func TestSNativeTx(t *testing.T) { fmt.Println("\n#### UnsetBase") // UnsetBase - snativeArgs = snativePermTestInputTx("UnsetBase", user[3], ptypes.CreateContract, false) + snativeArgs = snativePermTestInputTx("unset_base", user[3], ptypes.CreateContract, false) testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.UnsetBase, snativeArgs) acc = blockCache.GetAccount(user[3].Address) @@ -1014,7 +1014,7 @@ func TestSNativeTx(t *testing.T) { fmt.Println("\n#### SetGlobal") // SetGlobalPerm - snativeArgs = snativePermTestInputTx("SetGlobal", user[3], ptypes.CreateContract, true) + snativeArgs = snativePermTestInputTx("set_global", user[3], ptypes.CreateContract, true) testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.SetGlobal, snativeArgs) acc = blockCache.GetAccount(ptypes.GlobalPermissionsAddress) @@ -1024,7 +1024,7 @@ func TestSNativeTx(t *testing.T) { fmt.Println("\n#### AddRole") // AddRole - snativeArgs = snativeRoleTestInputTx("AddRole", user[3], "chuck") + snativeArgs = snativeRoleTestInputTx("add_role", user[3], "chuck") testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.AddRole, snativeArgs) acc = blockCache.GetAccount(user[3].Address) @@ -1034,7 +1034,7 @@ func TestSNativeTx(t *testing.T) { fmt.Println("\n#### RmRole") // RmRole - snativeArgs = snativeRoleTestInputTx("RmRole", user[3], "chuck") + snativeArgs = snativeRoleTestInputTx("rm_role", user[3], "chuck") testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.RmRole, snativeArgs) acc = blockCache.GetAccount(user[3].Address) @@ -1096,7 +1096,7 @@ func testSNativeCALLExpectPass(t *testing.T, blockCache *BlockCache, doug *acm.A func testSNativeCALL(t *testing.T, expectPass bool, blockCache *BlockCache, doug *acm.Account, snativeAddress, data []byte, f func([]byte) error) { if expectPass { - perm, err := ptypes.SNativeStringToPermFlag(TrimmedString(snativeAddress)) + perm, err := ptypes.PermStringToFlag(TrimmedString(snativeAddress)) if err != nil { t.Fatal(err) } @@ -1130,21 +1130,21 @@ func testSNativeCALL(t *testing.T, expectPass bool, blockCache *BlockCache, doug } } -func testSNativeTxExpectFail(t *testing.T, blockCache *BlockCache, snativeArgs ptypes.SNativeArgs) { +func testSNativeTxExpectFail(t *testing.T, blockCache *BlockCache, snativeArgs ptypes.PermArgs) { testSNativeTx(t, false, blockCache, 0, snativeArgs) } -func testSNativeTxExpectPass(t *testing.T, blockCache *BlockCache, perm ptypes.PermFlag, snativeArgs ptypes.SNativeArgs) { +func testSNativeTxExpectPass(t *testing.T, blockCache *BlockCache, perm ptypes.PermFlag, snativeArgs ptypes.PermArgs) { testSNativeTx(t, true, blockCache, perm, snativeArgs) } -func testSNativeTx(t *testing.T, expectPass bool, blockCache *BlockCache, perm ptypes.PermFlag, snativeArgs ptypes.SNativeArgs) { +func testSNativeTx(t *testing.T, expectPass bool, blockCache *BlockCache, perm ptypes.PermFlag, snativeArgs ptypes.PermArgs) { if expectPass { acc := blockCache.GetAccount(user[0].Address) acc.Permissions.Base.Set(perm, true) blockCache.UpdateAccount(acc) } - tx, _ := types.NewSNativeTx(blockCache, user[0].PubKey, snativeArgs) + tx, _ := types.NewPermissionsTx(blockCache, user[0].PubKey, snativeArgs) tx.Sign(chainID, user[0]) err := ExecTx(blockCache, tx, true, nil) if expectPass { @@ -1171,29 +1171,29 @@ func boolToWord256(v bool) Word256 { func snativePermTestInputCALL(name string, user *acm.PrivAccount, perm ptypes.PermFlag, val bool) (addr []byte, data []byte) { addr = LeftPadWord256([]byte(name)).Postfix(20) switch name { - case "HasBase", "UnsetBase": + case "has_base", "unset_base": data = LeftPadBytes(user.Address, 32) data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...) - case "SetBase": + case "set_base": data = LeftPadBytes(user.Address, 32) data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...) data = append(data, boolToWord256(val).Bytes()...) - case "SetGlobal": + case "set_global": data = Uint64ToWord256(uint64(perm)).Bytes() data = append(data, boolToWord256(val).Bytes()...) } return } -func snativePermTestInputTx(name string, user *acm.PrivAccount, perm ptypes.PermFlag, val bool) (snativeArgs ptypes.SNativeArgs) { +func snativePermTestInputTx(name string, user *acm.PrivAccount, perm ptypes.PermFlag, val bool) (snativeArgs ptypes.PermArgs) { switch name { - case "HasBase": + case "has_base": snativeArgs = &ptypes.HasBaseArgs{user.Address, perm} - case "UnsetBase": + case "unset_base": snativeArgs = &ptypes.UnsetBaseArgs{user.Address, perm} - case "SetBase": + case "set_base": snativeArgs = &ptypes.SetBaseArgs{user.Address, perm, val} - case "SetGlobal": + case "set_global": snativeArgs = &ptypes.SetGlobalArgs{perm, val} } return @@ -1206,13 +1206,13 @@ func snativeRoleTestInputCALL(name string, user *acm.PrivAccount, role string) ( return } -func snativeRoleTestInputTx(name string, user *acm.PrivAccount, role string) (snativeArgs ptypes.SNativeArgs) { +func snativeRoleTestInputTx(name string, user *acm.PrivAccount, role string) (snativeArgs ptypes.PermArgs) { switch name { - case "HasRole": + case "has_role": snativeArgs = &ptypes.HasRoleArgs{user.Address, role} - case "AddRole": + case "add_role": snativeArgs = &ptypes.AddRoleArgs{user.Address, role} - case "RmRole": + case "rm_role": snativeArgs = &ptypes.RmRoleArgs{user.Address, role} } return diff --git a/types/events.go b/types/events.go index 8a78d360..a9a3ff48 100644 --- a/types/events.go +++ b/types/events.go @@ -22,8 +22,8 @@ func EventStringLogEvent(addr []byte) string { return fmt.Sprintf("Log/%X", addr) } -func EventStringSNative(name string) string { - return fmt.Sprintf("SNative/%s", name) +func EventStringPermissions(name string) string { + return fmt.Sprintf("Permissions/%s", name) } func EventStringBond() string { diff --git a/types/tx.go b/types/tx.go index db489c21..3ebb143e 100644 --- a/types/tx.go +++ b/types/tx.go @@ -47,7 +47,7 @@ Validation Txs: - DupeoutTx Validator dupes out (equivocates) Admin Txs: - - SNativeTx (CapTx ?) + - PermissionsTx */ type Tx interface { @@ -68,7 +68,7 @@ const ( TxTypeDupeout = byte(0x14) // Admin transactions - TxTypeSNative = byte(0x20) + TxTypePermissions = byte(0x20) ) // for binary.readReflect @@ -81,7 +81,7 @@ var _ = binary.RegisterInterface( binary.ConcreteType{&UnbondTx{}, TxTypeUnbond}, binary.ConcreteType{&RebondTx{}, TxTypeRebond}, binary.ConcreteType{&DupeoutTx{}, TxTypeDupeout}, - binary.ConcreteType{&SNativeTx{}, TxTypeSNative}, + binary.ConcreteType{&PermissionsTx{}, TxTypePermissions}, ) //----------------------------------------------------------------------------- @@ -323,23 +323,22 @@ func (tx *DupeoutTx) String() string { //----------------------------------------------------------------------------- -type SNativeTx struct { - Input *TxInput `json:"input"` - SNative ptypes.SNativeArgs `json:"snative"` +type PermissionsTx struct { + Input *TxInput `json:"input"` + PermArgs ptypes.PermArgs `json:"args"` } -func (tx *SNativeTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { +func (tx *PermissionsTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) - binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"args":"`, TxTypeSNative)), w, n, err) - binary.WriteJSON(tx.SNative, w, n, err) + binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"args":"`, TxTypePermissions)), w, n, err) + binary.WriteJSON(tx.PermArgs, w, n, err) binary.WriteTo([]byte(`","input":`), w, n, err) tx.Input.WriteSignBytes(w, n, err) - binary.WriteTo([]byte(Fmt(`,"snative":%s`, jsonEscape(ptypes.PermFlagToString(tx.SNative.PermFlag())))), w, n, err) binary.WriteTo([]byte(`}]}`), w, n, err) } -func (tx *SNativeTx) String() string { - return Fmt("SNativeTx{%v -> %v}", tx.Input, tx.SNative) +func (tx *PermissionsTx) String() string { + return Fmt("PermissionsTx{%v -> %v}", tx.Input, tx.PermArgs) } //----------------------------------------------------------------------------- diff --git a/types/tx_test.go b/types/tx_test.go index 62e4b33d..82ba6c75 100644 --- a/types/tx_test.go +++ b/types/tx_test.go @@ -157,22 +157,22 @@ func TestRebondTxSignable(t *testing.T) { } } -func TestSNativeTxSignable(t *testing.T) { - snativeTx := &SNativeTx{ +func TestPermissionsTxSignable(t *testing.T) { + permsTx := &PermissionsTx{ Input: &TxInput{ Address: []byte("input1"), Amount: 12345, Sequence: 250, }, - SNative: &ptypes.SetBaseArgs{ + PermArgs: &ptypes.SetBaseArgs{ Address: []byte("address1"), Permission: 1, Value: true, }, } - signBytes := acm.SignBytes(chainID, snativeTx) + signBytes := acm.SignBytes(chainID, permsTx) signStr := string(signBytes) - expected := Fmt(`{"chain_id":"%s","tx":[32,{"args":"[2,{"address":"6164647265737331","permission":1,"value":true}]","input":{"address":"696E70757431","amount":12345,"sequence":250},"snative":"SetBase"}]}`, + expected := Fmt(`{"chain_id":"%s","tx":[32,{"args":"[2,{"address":"6164647265737331","permission":1,"value":true}]","input":{"address":"696E70757431","amount":12345,"sequence":250}}]}`, config.GetString("chain_id")) if signStr != expected { t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr) diff --git a/types/tx_utils.go b/types/tx_utils.go index c90e7624..3750483d 100644 --- a/types/tx_utils.go +++ b/types/tx_utils.go @@ -225,9 +225,9 @@ func (tx *RebondTx) Sign(chainID string, privAccount *acm.PrivAccount) { } //---------------------------------------------------------------------------- -// SNativeTx interface for creating tx +// PermissionsTx interface for creating tx -func NewSNativeTx(st AccountGetter, from acm.PubKey, snativeArgs ptypes.SNativeArgs) (*SNativeTx, error) { +func NewPermissionsTx(st AccountGetter, from acm.PubKey, args ptypes.PermArgs) (*PermissionsTx, error) { addr := from.Address() acc := st.GetAccount(addr) if acc == nil { @@ -235,10 +235,10 @@ func NewSNativeTx(st AccountGetter, from acm.PubKey, snativeArgs ptypes.SNativeA } nonce := acc.Sequence + 1 - return NewSNativeTxWithNonce(from, snativeArgs, nonce), nil + return NewPermissionsTxWithNonce(from, args, nonce), nil } -func NewSNativeTxWithNonce(from acm.PubKey, snativeArgs ptypes.SNativeArgs, nonce int) *SNativeTx { +func NewPermissionsTxWithNonce(from acm.PubKey, args ptypes.PermArgs, nonce int) *PermissionsTx { addr := from.Address() input := &TxInput{ Address: addr, @@ -248,13 +248,13 @@ func NewSNativeTxWithNonce(from acm.PubKey, snativeArgs ptypes.SNativeArgs, nonc PubKey: from, } - return &SNativeTx{ - Input: input, - SNative: snativeArgs, + return &PermissionsTx{ + Input: input, + PermArgs: args, } } -func (tx *SNativeTx) Sign(chainID string, privAccount *acm.PrivAccount) { +func (tx *PermissionsTx) Sign(chainID string, privAccount *acm.PrivAccount) { tx.Input.PubKey = privAccount.PubKey tx.Input.Signature = privAccount.Sign(chainID, tx) } diff --git a/vm/native.go b/vm/native.go index 6f7223a2..004c10e9 100644 --- a/vm/native.go +++ b/vm/native.go @@ -8,7 +8,12 @@ import ( "github.com/tendermint/tendermint/vm/sha3" ) -var nativeContracts = make(map[Word256]NativeContract) +var registeredNativeContracts = make(map[Word256]NativeContract) + +func RegisteredNativeContract(addr Word256) bool { + _, ok := registeredNativeContracts[addr] + return ok +} func init() { registerNativeContracts() @@ -16,17 +21,17 @@ func init() { } func registerNativeContracts() { - nativeContracts[Int64ToWord256(1)] = ecrecoverFunc - nativeContracts[Int64ToWord256(2)] = sha256Func - nativeContracts[Int64ToWord256(3)] = ripemd160Func - nativeContracts[Int64ToWord256(4)] = identityFunc + registeredNativeContracts[Int64ToWord256(1)] = ecrecoverFunc + registeredNativeContracts[Int64ToWord256(2)] = sha256Func + registeredNativeContracts[Int64ToWord256(3)] = ripemd160Func + registeredNativeContracts[Int64ToWord256(4)] = identityFunc } //----------------------------------------------------------------------------- -type NativeContract func(input []byte, gas *int64) (output []byte, err error) +type NativeContract func(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) -func ecrecoverFunc(input []byte, gas *int64) (output []byte, err error) { +func ecrecoverFunc(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) { // Deduct gas gasRequired := GasEcRecover if *gas < gasRequired { @@ -47,7 +52,7 @@ func ecrecoverFunc(input []byte, gas *int64) (output []byte, err error) { return LeftPadBytes(hashed, 32), nil } -func sha256Func(input []byte, gas *int64) (output []byte, err error) { +func sha256Func(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) { // Deduct gas gasRequired := int64((len(input)+31)/32)*GasSha256Word + GasSha256Base if *gas < gasRequired { @@ -62,7 +67,7 @@ func sha256Func(input []byte, gas *int64) (output []byte, err error) { return hasher.Sum(nil), nil } -func ripemd160Func(input []byte, gas *int64) (output []byte, err error) { +func ripemd160Func(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) { // Deduct gas gasRequired := int64((len(input)+31)/32)*GasRipemd160Word + GasRipemd160Base if *gas < gasRequired { @@ -77,7 +82,7 @@ func ripemd160Func(input []byte, gas *int64) (output []byte, err error) { return LeftPadBytes(hasher.Sum(nil), 32), nil } -func identityFunc(input []byte, gas *int64) (output []byte, err error) { +func identityFunc(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) { // Deduct gas gasRequired := int64((len(input)+31)/32)*GasIdentityWord + GasIdentityBase if *gas < gasRequired { diff --git a/vm/snative.go b/vm/snative.go index 1de3161b..2fd3b0f9 100644 --- a/vm/snative.go +++ b/vm/snative.go @@ -7,70 +7,32 @@ import ( ptypes "github.com/tendermint/tendermint/permission/types" ) -type snativeInfo struct { - PermFlag ptypes.PermFlag - NArgs int - ArgsError error - Executable SNativeContract -} - -// Takes an appState so it can lookup/update accounts, -// and an input byte array containing at least one Word256 // TODO: ABI -type SNativeContract func(appState AppState, input []byte) (output []byte, err error) - //------------------------------------------------------------------------------------------------ // Registered SNative contracts -var RegisteredSNativeContracts = make(map[Word256]*snativeInfo) - func registerSNativeContracts() { - RegisteredSNativeContracts[LeftPadWord256([]byte("HasBase"))] = getSNativeInfo("HasBase") - RegisteredSNativeContracts[LeftPadWord256([]byte("SetBase"))] = getSNativeInfo("SetBase") - RegisteredSNativeContracts[LeftPadWord256([]byte("UnsetBase"))] = getSNativeInfo("UnsetBase") - RegisteredSNativeContracts[LeftPadWord256([]byte("SetGlobal"))] = getSNativeInfo("SetGlobal") - RegisteredSNativeContracts[LeftPadWord256([]byte("HasRole"))] = getSNativeInfo("HasRole") - RegisteredSNativeContracts[LeftPadWord256([]byte("AddRole"))] = getSNativeInfo("AddRole") - RegisteredSNativeContracts[LeftPadWord256([]byte("RmRole"))] = getSNativeInfo("RmRole") -} - -// sets the number of arguments, a friendly error message, and the snative function ("executable") -func getSNativeInfo(permString string) *snativeInfo { - permFlag, err := ptypes.SNativeStringToPermFlag(permString) - if err != nil { - PanicSanity(err) - } - si := &snativeInfo{PermFlag: permFlag} - var errS string - switch permFlag { - case ptypes.HasBase: - si.NArgs, errS, si.Executable = 2, "hasBase() takes two arguments (address, permFlag)", hasBasePerm - case ptypes.SetBase: - si.NArgs, errS, si.Executable = 3, "setBase() takes three arguments (address, permFlag, permission value)", setBasePerm - case ptypes.UnsetBase: - si.NArgs, errS, si.Executable = 2, "unsetBase() takes two arguments (address, permFlag)", unsetBasePerm - case ptypes.SetGlobal: - si.NArgs, errS, si.Executable = 2, "setGlobal() takes two arguments (permFlag, permission value)", setGlobalPerm - case ptypes.HasRole: - si.NArgs, errS, si.Executable = 2, "hasRole() takes two arguments (address, role)", hasRole - case ptypes.AddRole: - si.NArgs, errS, si.Executable = 2, "addRole() takes two arguments (address, role)", addRole - case ptypes.RmRole: - si.NArgs, errS, si.Executable = 2, "rmRole() takes two arguments (address, role)", rmRole - default: - PanicSanity(Fmt("should never happen. PermFlag: %b", permFlag)) - } - si.ArgsError = fmt.Errorf(errS) - return si + registeredNativeContracts[LeftPadWord256([]byte("has_base"))] = hasBasePerm + registeredNativeContracts[LeftPadWord256([]byte("set_base"))] = setBasePerm + registeredNativeContracts[LeftPadWord256([]byte("unset_base"))] = unsetBasePerm + registeredNativeContracts[LeftPadWord256([]byte("set_global"))] = setGlobalPerm + registeredNativeContracts[LeftPadWord256([]byte("has_role"))] = hasRole + registeredNativeContracts[LeftPadWord256([]byte("add_role"))] = addRole + registeredNativeContracts[LeftPadWord256([]byte("rm_role"))] = rmRole } //----------------------------------------------------------------------------- -// snative are native contracts that can access and manipulate the chain state -// (in particular the permissions values) +// snative are native contracts that can access and modify an account's permissions // TODO: catch errors, log em, return 0s to the vm (should some errors cause exceptions though?) -func hasBasePerm(appState AppState, args []byte) (output []byte, err error) { +func hasBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { + if !HasPermission(appState, caller, ptypes.HasBase) { + return nil, ErrInvalidPermission{caller.Address, "has_base"} + } + if len(args) != 2*32 { + return nil, fmt.Errorf("hasBasePerm() takes two arguments (address, permFlag)") + } addr, permNum := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { @@ -90,7 +52,13 @@ func hasBasePerm(appState AppState, args []byte) (output []byte, err error) { return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func setBasePerm(appState AppState, args []byte) (output []byte, err error) { +func setBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { + if !HasPermission(appState, caller, ptypes.SetBase) { + return nil, ErrInvalidPermission{caller.Address, "set_base"} + } + if len(args) != 3*32 { + return nil, fmt.Errorf("setBase() takes three arguments (address, permFlag, permission value)") + } addr, permNum, perm := returnThreeArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { @@ -109,7 +77,13 @@ func setBasePerm(appState AppState, args []byte) (output []byte, err error) { return perm.Bytes(), nil } -func unsetBasePerm(appState AppState, args []byte) (output []byte, err error) { +func unsetBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { + if !HasPermission(appState, caller, ptypes.UnsetBase) { + return nil, ErrInvalidPermission{caller.Address, "unset_base"} + } + if len(args) != 2*32 { + return nil, fmt.Errorf("unsetBase() takes two arguments (address, permFlag)") + } addr, permNum := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { @@ -127,7 +101,13 @@ func unsetBasePerm(appState AppState, args []byte) (output []byte, err error) { return permNum.Bytes(), nil } -func setGlobalPerm(appState AppState, args []byte) (output []byte, err error) { +func setGlobalPerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { + if !HasPermission(appState, caller, ptypes.SetGlobal) { + return nil, ErrInvalidPermission{caller.Address, "set_global"} + } + if len(args) != 2*32 { + return nil, fmt.Errorf("setGlobal() takes two arguments (permFlag, permission value)") + } permNum, perm := returnTwoArgs(args) vmAcc := appState.GetAccount(ptypes.GlobalPermissionsAddress256) if vmAcc == nil { @@ -146,7 +126,13 @@ func setGlobalPerm(appState AppState, args []byte) (output []byte, err error) { return perm.Bytes(), nil } -func hasRole(appState AppState, args []byte) (output []byte, err error) { +func hasRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { + if !HasPermission(appState, caller, ptypes.HasRole) { + return nil, ErrInvalidPermission{caller.Address, "has_role"} + } + if len(args) != 2*32 { + return nil, fmt.Errorf("hasRole() takes two arguments (address, role)") + } addr, role := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { @@ -163,7 +149,13 @@ func hasRole(appState AppState, args []byte) (output []byte, err error) { return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func addRole(appState AppState, args []byte) (output []byte, err error) { +func addRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { + if !HasPermission(appState, caller, ptypes.AddRole) { + return nil, ErrInvalidPermission{caller.Address, "add_role"} + } + if len(args) != 2*32 { + return nil, fmt.Errorf("addRole() takes two arguments (address, role)") + } addr, role := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { @@ -181,7 +173,13 @@ func addRole(appState AppState, args []byte) (output []byte, err error) { return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func rmRole(appState AppState, args []byte) (output []byte, err error) { +func rmRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { + if !HasPermission(appState, caller, ptypes.RmRole) { + return nil, ErrInvalidPermission{caller.Address, "rm_role"} + } + if len(args) != 2*32 { + return nil, fmt.Errorf("rmRole() takes two arguments (address, role)") + } addr, role := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { @@ -213,9 +211,7 @@ func (e ErrInvalidPermission) Error() string { // Checks if a permission flag is valid (a known base chain or snative permission) func ValidPermN(n ptypes.PermFlag) bool { - if n > ptypes.TopBasePermFlag && n < ptypes.FirstSNativePermFlag { - return false - } else if n > ptypes.TopSNativePermFlag { + if n > ptypes.TopPermFlag { return false } return true diff --git a/vm/vm.go b/vm/vm.go index 8ffd083e..64420699 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -108,33 +108,6 @@ func (vm *VM) fireEvent(exception *string, output *[]byte, caller, callee *Accou } } -// call an snative contract (includes event processing) -// addr and permFlag refer to the same snative's address and it's permFlag -func (vm *VM) callSNative(addr Word256, snInfo *snativeInfo, caller *Account, input []byte) (ret []byte, err error) { - exception := new(string) - // fire the post call event (including exception if applicable) - value, gas := int64(0), new(int64) - defer vm.fireEvent(exception, &ret, caller, &Account{Address: addr}, input, value, gas) - - if !HasPermission(vm.appState, caller, snInfo.PermFlag) { - err = ErrInvalidPermission{caller.Address, addr.TrimmedString()} - *exception = err.Error() - return - } - if len(input) != snInfo.NArgs*32 { - err = snInfo.ArgsError - *exception = err.Error() - return - } - // SNATIVE ACCESS - ret, err = snInfo.Executable(vm.appState, input) - // END SNATIVE ACCESS - if err != nil { - *exception = err.Error() - } - return -} - // CONTRACT appState is aware of caller and callee, so we can just mutate them. // value: To be transferred from caller to callee. Refunded upon error. // gas: Available gas. No refunds for gas. @@ -777,11 +750,16 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas // Begin execution var ret []byte var err error - if nativeContract := nativeContracts[addr]; nativeContract != nil { + if nativeContract := registeredNativeContracts[addr]; nativeContract != nil { // Native contract - ret, err = nativeContract(args, &gasLimit) - } else if snInfo, ok := RegisteredSNativeContracts[addr]; ok { - ret, err = vm.callSNative(addr, snInfo, callee, input) + ret, err = nativeContract(vm.appState, callee, args, &gasLimit) + + // for now we fire the Receive event. maybe later we'll fire more particulars + var exception string + if err != nil { + exception = err.Error() + } + vm.fireEvent(&exception, &ret, callee, &Account{Address: addr}, args, value, gas) } else { // EVM contract if ok = useGas(gas, GasGetAccount); !ok {