diff --git a/state/execution.go b/state/execution.go index ddad8cfb4..71ae09c25 100644 --- a/state/execution.go +++ b/state/execution.go @@ -424,8 +424,8 @@ func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireab if outAcc == nil || len(outAcc.Code) == 0 { // check if its an snative - trimmedAddr := TrimmedString(tx.Address) - if _, unknownPermErr := ptypes.SNativeStringToPermFlag(string(trimmedAddr)); unknownPermErr == nil { + // 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") } diff --git a/vm/native.go b/vm/native.go index 27f2e8f4b..6f7223a21 100644 --- a/vm/native.go +++ b/vm/native.go @@ -11,6 +11,11 @@ import ( var nativeContracts = make(map[Word256]NativeContract) func init() { + registerNativeContracts() + registerSNativeContracts() +} + +func registerNativeContracts() { nativeContracts[Int64ToWord256(1)] = ecrecoverFunc nativeContracts[Int64ToWord256(2)] = sha256Func nativeContracts[Int64ToWord256(3)] = ripemd160Func diff --git a/vm/snative.go b/vm/snative.go index bd72d9dd3..1de3161b5 100644 --- a/vm/snative.go +++ b/vm/snative.go @@ -22,8 +22,24 @@ type SNativeContract func(appState AppState, input []byte) (output []byte, err e //------------------------------------------------------------------------------------------------ // 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(permFlag ptypes.PermFlag) *snativeInfo { +func getSNativeInfo(permString string) *snativeInfo { + permFlag, err := ptypes.SNativeStringToPermFlag(permString) + if err != nil { + PanicSanity(err) + } si := &snativeInfo{PermFlag: permFlag} var errS string switch permFlag { diff --git a/vm/vm.go b/vm/vm.go index a3934a9d0..8ffd083e9 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -110,17 +110,17 @@ 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, permFlag ptypes.PermFlag, caller *Account, input []byte, value int64, gas *int64) (ret []byte, err error) { +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, permFlag) { + if !HasPermission(vm.appState, caller, snInfo.PermFlag) { err = ErrInvalidPermission{caller.Address, addr.TrimmedString()} *exception = err.Error() return } - snInfo := getSNativeInfo(permFlag) if len(input) != snInfo.NArgs*32 { err = snInfo.ArgsError *exception = err.Error() @@ -780,6 +780,8 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas if nativeContract := nativeContracts[addr]; nativeContract != nil { // Native contract ret, err = nativeContract(args, &gasLimit) + } else if snInfo, ok := RegisteredSNativeContracts[addr]; ok { + ret, err = vm.callSNative(addr, snInfo, callee, input) } else { // EVM contract if ok = useGas(gas, GasGetAccount); !ok { @@ -797,23 +799,14 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas ret, err = vm.Call(callee, callee, acc.Code, args, value, gas) } else { if acc == nil { - // nil account means either the address is the name of an snative, - // or we're sending funds to a new account - - // trim the 0s off the address and check if its known - trimmedAddr := addr.TrimmedString() - if permFlag, unknownPermErr := ptypes.SNativeStringToPermFlag(trimmedAddr); unknownPermErr == nil { - ret, err = vm.callSNative(addr, permFlag, callee, input, value, gas) - } else { - // if we have not seen the account before, create it - if !HasPermission(vm.appState, caller, ptypes.CreateAccount) { - return nil, ErrPermission{"create_account"} - } - acc = &Account{Address: addr} - vm.appState.UpdateAccount(acc) - // send funds to new account - ret, err = vm.Call(callee, acc, acc.Code, args, value, gas) + // nil account means we're sending funds to a new account + if !HasPermission(vm.appState, caller, ptypes.CreateAccount) { + return nil, ErrPermission{"create_account"} } + acc = &Account{Address: addr} + vm.appState.UpdateAccount(acc) + // send funds to new account + ret, err = vm.Call(callee, acc, acc.Code, args, value, gas) } else { // call standard contract ret, err = vm.Call(callee, acc, acc.Code, args, value, gas)