Browse Source

SNativeTx -> PermissionTx, consolidate permissions and native contracts

pull/119/head
Ethan Buchman 9 years ago
parent
commit
31b9d8ee27
11 changed files with 233 additions and 305 deletions
  1. +55
    -25
      permission/types/permissions.go
  2. +20
    -93
      permission/types/snatives.go
  3. +12
    -19
      state/execution.go
  4. +38
    -38
      state/permissions_test.go
  5. +2
    -2
      types/events.go
  6. +11
    -12
      types/tx.go
  7. +5
    -5
      types/tx_test.go
  8. +8
    -8
      types/tx_utils.go
  9. +15
    -10
      vm/native.go
  10. +58
    -62
      vm/snative.go
  11. +9
    -31
      vm/vm.go

+ 55
- 25
permission/types/permissions.go View File

@ -17,19 +17,29 @@ type PermFlag uint64
// Base permission references are like unix (the index is already bit shifted) // Base permission references are like unix (the index is already bit shifted)
const ( 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.
TopBasePermFlag PermFlag = 1 << (NumBasePermissions - 1)
AllBasePermFlags PermFlag = TopBasePermFlag | (TopBasePermFlag - 1)
AllPermFlags PermFlag = AllBasePermFlags | AllSNativePermFlags
DefaultBasePermFlags PermFlag = Send | Call | CreateContract | CreateAccount | Bond | Name
// chain permissions
Root PermFlag = 1 << iota // 1
Send // 2
Call // 4
CreateContract // 8
CreateAccount // 16
Bond // 32
Name // 64
// 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 ( var (
@ -39,7 +49,7 @@ var (
} }
DefaultAccountPermissions = AccountPermissions{ DefaultAccountPermissions = AccountPermissions{
Base: BasePermissions{ Base: BasePermissions{
Perms: DefaultBasePermFlags,
Perms: DefaultPermFlags,
SetBit: AllPermFlags, SetBit: AllPermFlags,
}, },
Roles: []string{}, Roles: []string{},
@ -154,16 +164,8 @@ func (aP *AccountPermissions) RmRole(role string) bool {
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// string utilities // 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 { switch pf {
case Root: case Root:
perm = "root" perm = "root"
@ -179,13 +181,27 @@ func BasePermFlagToString(pf PermFlag) (perm string) {
perm = "bond" perm = "bond"
case Name: case Name:
perm = "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: default:
perm = "#-UNKNOWN-#" perm = "#-UNKNOWN-#"
} }
return return
} }
func BasePermStringToFlag(perm string) (pf PermFlag, err error) {
func PermStringToFlag(perm string) (pf PermFlag, err error) {
switch perm { switch perm {
case "root": case "root":
pf = Root pf = Root
@ -201,6 +217,20 @@ func BasePermStringToFlag(perm string) (pf PermFlag, err error) {
pf = Bond pf = Bond
case "name": case "name":
pf = 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: default:
err = fmt.Errorf("Unknown permission %s", perm) err = fmt.Errorf("Unknown permission %s", perm)
} }


+ 20
- 93
permission/types/snatives.go View File

@ -1,66 +1,40 @@
package types package types
import ( import (
"fmt"
"github.com/tendermint/tendermint/binary" "github.com/tendermint/tendermint/binary"
) )
//--------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------
// snative permissions
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)
)
// PermissionsTx.PermArgs interface and argument encoding
//---------------------------------------------------------------------------------------------------
// 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 // 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 PermFlag() PermFlag
} }
const ( 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 // for binary.readReflect
var _ = binary.RegisterInterface( 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 { type HasBaseArgs struct {
@ -126,50 +100,3 @@ type RmRoleArgs struct {
func (*RmRoleArgs) PermFlag() PermFlag { func (*RmRoleArgs) PermFlag() PermFlag {
return RmRole 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
}

+ 12
- 19
state/execution.go View File

@ -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 // 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 // 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 // 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) outAcc = blockCache.GetAccount(tx.Address)
} }
@ -423,10 +422,9 @@ func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireab
if !createAccount { if !createAccount {
if outAcc == nil || len(outAcc.Code) == 0 { 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 // 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 return nil
case *types.SNativeTx:
case *types.PermissionsTx:
var inAcc *acm.Account var inAcc *acm.Account
// Validate input // Validate input
@ -804,10 +802,10 @@ func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireab
return types.ErrTxInvalidAddress return types.ErrTxInvalidAddress
} }
permFlag := tx.SNative.PermFlag()
permFlag := tx.PermArgs.PermFlag()
// check permission // 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" // 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 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 var permAcc *acm.Account
switch args := tx.SNative.(type) {
switch args := tx.PermArgs.(type) {
case *ptypes.HasBaseArgs: case *ptypes.HasBaseArgs:
// this one doesn't make sense from txs // this one doesn't make sense from txs
return fmt.Errorf("HasBase is for contracts, not humans. Just look at the blockchain") 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) return fmt.Errorf("Role (%s) does not exist for account %X", args.Role, args.Address)
} }
default: 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? // 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 { if evc != nil {
evc.FireEvent(types.EventStringAccInput(tx.Input.Address), tx) 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 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 // Get permission on an account or fall back to global value
func HasPermission(state AccountGetter, acc *acm.Account, perm ptypes.PermFlag) bool { 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") 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 return true
} }
func hasSNativePermission(state AccountGetter, acc *acm.Account, permFlag ptypes.PermFlag) bool {
return HasPermission(state, acc, permFlag)
}

+ 38
- 38
state/permissions_test.go View File

@ -867,7 +867,7 @@ func TestSNativeCALL(t *testing.T) {
fmt.Println("\n#### HasBase") fmt.Println("\n#### HasBase")
// 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) testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data)
testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error {
// return value should be true or false as a 32 byte array... // 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") fmt.Println("\n#### SetBase")
// 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) testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data)
testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) 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 { testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error {
// return value should be true or false as a 32 byte array... // return value should be true or false as a 32 byte array...
if !IsZeros(ret) { if !IsZeros(ret) {
@ -890,9 +890,9 @@ func TestSNativeCALL(t *testing.T) {
} }
return nil 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 }) 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 { testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error {
// return value should be true or false as a 32 byte array... // return value should be true or false as a 32 byte array...
if !IsZeros(ret[:31]) || ret[31] != byte(1) { if !IsZeros(ret[:31]) || ret[31] != byte(1) {
@ -903,10 +903,10 @@ func TestSNativeCALL(t *testing.T) {
fmt.Println("\n#### UnsetBase") fmt.Println("\n#### UnsetBase")
// 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) testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data)
testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) 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 { testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error {
if !IsZeros(ret) { if !IsZeros(ret) {
return fmt.Errorf("Expected 0. Got %X", ret) return fmt.Errorf("Expected 0. Got %X", ret)
@ -916,10 +916,10 @@ func TestSNativeCALL(t *testing.T) {
fmt.Println("\n#### SetGlobal") fmt.Println("\n#### SetGlobal")
// SetGlobalPerm // 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) testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data)
testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) 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 { testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error {
// return value should be true or false as a 32 byte array... // return value should be true or false as a 32 byte array...
if !IsZeros(ret[:31]) || ret[31] != byte(1) { if !IsZeros(ret[:31]) || ret[31] != byte(1) {
@ -930,7 +930,7 @@ func TestSNativeCALL(t *testing.T) {
fmt.Println("\n#### HasRole") fmt.Println("\n#### HasRole")
// HasRole // HasRole
snativeAddress, data = snativeRoleTestInputCALL("HasRole", user[3], "bumble")
snativeAddress, data = snativeRoleTestInputCALL("has_role", user[3], "bumble")
testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data)
testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error {
if !IsZeros(ret[:31]) || ret[31] != byte(1) { if !IsZeros(ret[:31]) || ret[31] != byte(1) {
@ -941,17 +941,17 @@ func TestSNativeCALL(t *testing.T) {
fmt.Println("\n#### AddRole") fmt.Println("\n#### AddRole")
// 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 { testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error {
if !IsZeros(ret) { if !IsZeros(ret) {
return fmt.Errorf("Expected 0. Got %X", ret) return fmt.Errorf("Expected 0. Got %X", ret)
} }
return nil return nil
}) })
snativeAddress, data = snativeRoleTestInputCALL("AddRole", user[3], "chuck")
snativeAddress, data = snativeRoleTestInputCALL("add_role", user[3], "chuck")
testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data)
testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) 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 { testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error {
if !IsZeros(ret[:31]) || ret[31] != byte(1) { if !IsZeros(ret[:31]) || ret[31] != byte(1) {
return fmt.Errorf("Expected 1. Got %X", ret) return fmt.Errorf("Expected 1. Got %X", ret)
@ -961,10 +961,10 @@ func TestSNativeCALL(t *testing.T) {
fmt.Println("\n#### RmRole") fmt.Println("\n#### RmRole")
// RmRole // RmRole
snativeAddress, data = snativeRoleTestInputCALL("RmRole", user[3], "chuck")
snativeAddress, data = snativeRoleTestInputCALL("rm_role", user[3], "chuck")
testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data)
testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error { return nil }) 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 { testSNativeCALLExpectPass(t, blockCache, doug, snativeAddress, data, func(ret []byte) error {
if !IsZeros(ret) { if !IsZeros(ret) {
return fmt.Errorf("Expected 0. Got %X", ret) return fmt.Errorf("Expected 0. Got %X", ret)
@ -988,14 +988,14 @@ func TestSNativeTx(t *testing.T) {
fmt.Println("\n#### SetBase") fmt.Println("\n#### SetBase")
// SetBase // SetBase
snativeArgs := snativePermTestInputTx("SetBase", user[3], ptypes.Bond, false)
snativeArgs := snativePermTestInputTx("set_base", user[3], ptypes.Bond, false)
testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectFail(t, blockCache, snativeArgs)
testSNativeTxExpectPass(t, blockCache, ptypes.SetBase, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.SetBase, snativeArgs)
acc := blockCache.GetAccount(user[3].Address) acc := blockCache.GetAccount(user[3].Address)
if v, _ := acc.Permissions.Base.Get(ptypes.Bond); v { if v, _ := acc.Permissions.Base.Get(ptypes.Bond); v {
t.Fatal("expected permission to be set false") 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) testSNativeTxExpectPass(t, blockCache, ptypes.SetBase, snativeArgs)
acc = blockCache.GetAccount(user[3].Address) acc = blockCache.GetAccount(user[3].Address)
if v, _ := acc.Permissions.Base.Get(ptypes.CreateContract); !v { if v, _ := acc.Permissions.Base.Get(ptypes.CreateContract); !v {
@ -1004,7 +1004,7 @@ func TestSNativeTx(t *testing.T) {
fmt.Println("\n#### UnsetBase") fmt.Println("\n#### UnsetBase")
// UnsetBase // UnsetBase
snativeArgs = snativePermTestInputTx("UnsetBase", user[3], ptypes.CreateContract, false)
snativeArgs = snativePermTestInputTx("unset_base", user[3], ptypes.CreateContract, false)
testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectFail(t, blockCache, snativeArgs)
testSNativeTxExpectPass(t, blockCache, ptypes.UnsetBase, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.UnsetBase, snativeArgs)
acc = blockCache.GetAccount(user[3].Address) acc = blockCache.GetAccount(user[3].Address)
@ -1014,7 +1014,7 @@ func TestSNativeTx(t *testing.T) {
fmt.Println("\n#### SetGlobal") fmt.Println("\n#### SetGlobal")
// SetGlobalPerm // SetGlobalPerm
snativeArgs = snativePermTestInputTx("SetGlobal", user[3], ptypes.CreateContract, true)
snativeArgs = snativePermTestInputTx("set_global", user[3], ptypes.CreateContract, true)
testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectFail(t, blockCache, snativeArgs)
testSNativeTxExpectPass(t, blockCache, ptypes.SetGlobal, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.SetGlobal, snativeArgs)
acc = blockCache.GetAccount(ptypes.GlobalPermissionsAddress) acc = blockCache.GetAccount(ptypes.GlobalPermissionsAddress)
@ -1024,7 +1024,7 @@ func TestSNativeTx(t *testing.T) {
fmt.Println("\n#### AddRole") fmt.Println("\n#### AddRole")
// AddRole // AddRole
snativeArgs = snativeRoleTestInputTx("AddRole", user[3], "chuck")
snativeArgs = snativeRoleTestInputTx("add_role", user[3], "chuck")
testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectFail(t, blockCache, snativeArgs)
testSNativeTxExpectPass(t, blockCache, ptypes.AddRole, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.AddRole, snativeArgs)
acc = blockCache.GetAccount(user[3].Address) acc = blockCache.GetAccount(user[3].Address)
@ -1034,7 +1034,7 @@ func TestSNativeTx(t *testing.T) {
fmt.Println("\n#### RmRole") fmt.Println("\n#### RmRole")
// RmRole // RmRole
snativeArgs = snativeRoleTestInputTx("RmRole", user[3], "chuck")
snativeArgs = snativeRoleTestInputTx("rm_role", user[3], "chuck")
testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectFail(t, blockCache, snativeArgs)
testSNativeTxExpectPass(t, blockCache, ptypes.RmRole, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.RmRole, snativeArgs)
acc = blockCache.GetAccount(user[3].Address) 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) { func testSNativeCALL(t *testing.T, expectPass bool, blockCache *BlockCache, doug *acm.Account, snativeAddress, data []byte, f func([]byte) error) {
if expectPass { if expectPass {
perm, err := ptypes.SNativeStringToPermFlag(TrimmedString(snativeAddress))
perm, err := ptypes.PermStringToFlag(TrimmedString(snativeAddress))
if err != nil { if err != nil {
t.Fatal(err) 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) 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) 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 { if expectPass {
acc := blockCache.GetAccount(user[0].Address) acc := blockCache.GetAccount(user[0].Address)
acc.Permissions.Base.Set(perm, true) acc.Permissions.Base.Set(perm, true)
blockCache.UpdateAccount(acc) blockCache.UpdateAccount(acc)
} }
tx, _ := types.NewSNativeTx(blockCache, user[0].PubKey, snativeArgs)
tx, _ := types.NewPermissionsTx(blockCache, user[0].PubKey, snativeArgs)
tx.Sign(chainID, user[0]) tx.Sign(chainID, user[0])
err := ExecTx(blockCache, tx, true, nil) err := ExecTx(blockCache, tx, true, nil)
if expectPass { 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) { func snativePermTestInputCALL(name string, user *acm.PrivAccount, perm ptypes.PermFlag, val bool) (addr []byte, data []byte) {
addr = LeftPadWord256([]byte(name)).Postfix(20) addr = LeftPadWord256([]byte(name)).Postfix(20)
switch name { switch name {
case "HasBase", "UnsetBase":
case "has_base", "unset_base":
data = LeftPadBytes(user.Address, 32) data = LeftPadBytes(user.Address, 32)
data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...) data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...)
case "SetBase":
case "set_base":
data = LeftPadBytes(user.Address, 32) data = LeftPadBytes(user.Address, 32)
data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...) data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...)
data = append(data, boolToWord256(val).Bytes()...) data = append(data, boolToWord256(val).Bytes()...)
case "SetGlobal":
case "set_global":
data = Uint64ToWord256(uint64(perm)).Bytes() data = Uint64ToWord256(uint64(perm)).Bytes()
data = append(data, boolToWord256(val).Bytes()...) data = append(data, boolToWord256(val).Bytes()...)
} }
return 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 { switch name {
case "HasBase":
case "has_base":
snativeArgs = &ptypes.HasBaseArgs{user.Address, perm} snativeArgs = &ptypes.HasBaseArgs{user.Address, perm}
case "UnsetBase":
case "unset_base":
snativeArgs = &ptypes.UnsetBaseArgs{user.Address, perm} snativeArgs = &ptypes.UnsetBaseArgs{user.Address, perm}
case "SetBase":
case "set_base":
snativeArgs = &ptypes.SetBaseArgs{user.Address, perm, val} snativeArgs = &ptypes.SetBaseArgs{user.Address, perm, val}
case "SetGlobal":
case "set_global":
snativeArgs = &ptypes.SetGlobalArgs{perm, val} snativeArgs = &ptypes.SetGlobalArgs{perm, val}
} }
return return
@ -1206,13 +1206,13 @@ func snativeRoleTestInputCALL(name string, user *acm.PrivAccount, role string) (
return 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 { switch name {
case "HasRole":
case "has_role":
snativeArgs = &ptypes.HasRoleArgs{user.Address, role} snativeArgs = &ptypes.HasRoleArgs{user.Address, role}
case "AddRole":
case "add_role":
snativeArgs = &ptypes.AddRoleArgs{user.Address, role} snativeArgs = &ptypes.AddRoleArgs{user.Address, role}
case "RmRole":
case "rm_role":
snativeArgs = &ptypes.RmRoleArgs{user.Address, role} snativeArgs = &ptypes.RmRoleArgs{user.Address, role}
} }
return return


+ 2
- 2
types/events.go View File

@ -22,8 +22,8 @@ func EventStringLogEvent(addr []byte) string {
return fmt.Sprintf("Log/%X", addr) 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 { func EventStringBond() string {


+ 11
- 12
types/tx.go View File

@ -47,7 +47,7 @@ Validation Txs:
- DupeoutTx Validator dupes out (equivocates) - DupeoutTx Validator dupes out (equivocates)
Admin Txs: Admin Txs:
- SNativeTx (CapTx ?)
- PermissionsTx
*/ */
type Tx interface { type Tx interface {
@ -68,7 +68,7 @@ const (
TxTypeDupeout = byte(0x14) TxTypeDupeout = byte(0x14)
// Admin transactions // Admin transactions
TxTypeSNative = byte(0x20)
TxTypePermissions = byte(0x20)
) )
// for binary.readReflect // for binary.readReflect
@ -81,7 +81,7 @@ var _ = binary.RegisterInterface(
binary.ConcreteType{&UnbondTx{}, TxTypeUnbond}, binary.ConcreteType{&UnbondTx{}, TxTypeUnbond},
binary.ConcreteType{&RebondTx{}, TxTypeRebond}, binary.ConcreteType{&RebondTx{}, TxTypeRebond},
binary.ConcreteType{&DupeoutTx{}, TxTypeDupeout}, 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(`{"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) binary.WriteTo([]byte(`","input":`), w, n, err)
tx.Input.WriteSignBytes(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) 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)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------


+ 5
- 5
types/tx_test.go View File

@ -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{ Input: &TxInput{
Address: []byte("input1"), Address: []byte("input1"),
Amount: 12345, Amount: 12345,
Sequence: 250, Sequence: 250,
}, },
SNative: &ptypes.SetBaseArgs{
PermArgs: &ptypes.SetBaseArgs{
Address: []byte("address1"), Address: []byte("address1"),
Permission: 1, Permission: 1,
Value: true, Value: true,
}, },
} }
signBytes := acm.SignBytes(chainID, snativeTx)
signBytes := acm.SignBytes(chainID, permsTx)
signStr := string(signBytes) 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")) config.GetString("chain_id"))
if signStr != expected { if signStr != expected {
t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr) t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)


+ 8
- 8
types/tx_utils.go View File

@ -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() addr := from.Address()
acc := st.GetAccount(addr) acc := st.GetAccount(addr)
if acc == nil { if acc == nil {
@ -235,10 +235,10 @@ func NewSNativeTx(st AccountGetter, from acm.PubKey, snativeArgs ptypes.SNativeA
} }
nonce := acc.Sequence + 1 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() addr := from.Address()
input := &TxInput{ input := &TxInput{
Address: addr, Address: addr,
@ -248,13 +248,13 @@ func NewSNativeTxWithNonce(from acm.PubKey, snativeArgs ptypes.SNativeArgs, nonc
PubKey: from, 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.PubKey = privAccount.PubKey
tx.Input.Signature = privAccount.Sign(chainID, tx) tx.Input.Signature = privAccount.Sign(chainID, tx)
} }

+ 15
- 10
vm/native.go View File

@ -8,7 +8,12 @@ import (
"github.com/tendermint/tendermint/vm/sha3" "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() { func init() {
registerNativeContracts() registerNativeContracts()
@ -16,17 +21,17 @@ func init() {
} }
func registerNativeContracts() { 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 // Deduct gas
gasRequired := GasEcRecover gasRequired := GasEcRecover
if *gas < gasRequired { if *gas < gasRequired {
@ -47,7 +52,7 @@ func ecrecoverFunc(input []byte, gas *int64) (output []byte, err error) {
return LeftPadBytes(hashed, 32), nil 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 // Deduct gas
gasRequired := int64((len(input)+31)/32)*GasSha256Word + GasSha256Base gasRequired := int64((len(input)+31)/32)*GasSha256Word + GasSha256Base
if *gas < gasRequired { if *gas < gasRequired {
@ -62,7 +67,7 @@ func sha256Func(input []byte, gas *int64) (output []byte, err error) {
return hasher.Sum(nil), nil 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 // Deduct gas
gasRequired := int64((len(input)+31)/32)*GasRipemd160Word + GasRipemd160Base gasRequired := int64((len(input)+31)/32)*GasRipemd160Word + GasRipemd160Base
if *gas < gasRequired { if *gas < gasRequired {
@ -77,7 +82,7 @@ func ripemd160Func(input []byte, gas *int64) (output []byte, err error) {
return LeftPadBytes(hasher.Sum(nil), 32), nil 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 // Deduct gas
gasRequired := int64((len(input)+31)/32)*GasIdentityWord + GasIdentityBase gasRequired := int64((len(input)+31)/32)*GasIdentityWord + GasIdentityBase
if *gas < gasRequired { if *gas < gasRequired {


+ 58
- 62
vm/snative.go View File

@ -7,70 +7,32 @@ import (
ptypes "github.com/tendermint/tendermint/permission/types" 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 // TODO: ABI
type SNativeContract func(appState AppState, input []byte) (output []byte, err error)
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
// Registered SNative contracts // Registered SNative contracts
var RegisteredSNativeContracts = make(map[Word256]*snativeInfo)
func registerSNativeContracts() { 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?) // 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) addr, permNum := returnTwoArgs(args)
vmAcc := appState.GetAccount(addr) vmAcc := appState.GetAccount(addr)
if vmAcc == nil { if vmAcc == nil {
@ -90,7 +52,13 @@ func hasBasePerm(appState AppState, args []byte) (output []byte, err error) {
return LeftPadWord256([]byte{permInt}).Bytes(), nil 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) addr, permNum, perm := returnThreeArgs(args)
vmAcc := appState.GetAccount(addr) vmAcc := appState.GetAccount(addr)
if vmAcc == nil { if vmAcc == nil {
@ -109,7 +77,13 @@ func setBasePerm(appState AppState, args []byte) (output []byte, err error) {
return perm.Bytes(), nil 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) addr, permNum := returnTwoArgs(args)
vmAcc := appState.GetAccount(addr) vmAcc := appState.GetAccount(addr)
if vmAcc == nil { if vmAcc == nil {
@ -127,7 +101,13 @@ func unsetBasePerm(appState AppState, args []byte) (output []byte, err error) {
return permNum.Bytes(), nil 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) permNum, perm := returnTwoArgs(args)
vmAcc := appState.GetAccount(ptypes.GlobalPermissionsAddress256) vmAcc := appState.GetAccount(ptypes.GlobalPermissionsAddress256)
if vmAcc == nil { if vmAcc == nil {
@ -146,7 +126,13 @@ func setGlobalPerm(appState AppState, args []byte) (output []byte, err error) {
return perm.Bytes(), nil 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) addr, role := returnTwoArgs(args)
vmAcc := appState.GetAccount(addr) vmAcc := appState.GetAccount(addr)
if vmAcc == nil { if vmAcc == nil {
@ -163,7 +149,13 @@ func hasRole(appState AppState, args []byte) (output []byte, err error) {
return LeftPadWord256([]byte{permInt}).Bytes(), nil 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) addr, role := returnTwoArgs(args)
vmAcc := appState.GetAccount(addr) vmAcc := appState.GetAccount(addr)
if vmAcc == nil { if vmAcc == nil {
@ -181,7 +173,13 @@ func addRole(appState AppState, args []byte) (output []byte, err error) {
return LeftPadWord256([]byte{permInt}).Bytes(), nil 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) addr, role := returnTwoArgs(args)
vmAcc := appState.GetAccount(addr) vmAcc := appState.GetAccount(addr)
if vmAcc == nil { 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) // Checks if a permission flag is valid (a known base chain or snative permission)
func ValidPermN(n ptypes.PermFlag) bool { 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 false
} }
return true return true


+ 9
- 31
vm/vm.go View File

@ -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. // 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. // value: To be transferred from caller to callee. Refunded upon error.
// gas: Available gas. No refunds for gas. // 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 // Begin execution
var ret []byte var ret []byte
var err error var err error
if nativeContract := nativeContracts[addr]; nativeContract != nil {
if nativeContract := registeredNativeContracts[addr]; nativeContract != nil {
// Native contract // 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 { } else {
// EVM contract // EVM contract
if ok = useGas(gas, GasGetAccount); !ok { if ok = useGas(gas, GasGetAccount); !ok {


Loading…
Cancel
Save