diff --git a/permission/types/permissions.go b/permission/types/permissions.go index 89a376ea..dd13a04e 100644 --- a/permission/types/permissions.go +++ b/permission/types/permissions.go @@ -79,6 +79,9 @@ func (p *BasePermissions) Unset(ty PermFlag) error { } func (p *BasePermissions) Copy() *BasePermissions { + if p == nil { + return nil + } return &BasePermissions{ Perms: p.Perms, SetBit: p.SetBit, @@ -140,6 +143,9 @@ func (aP *AccountPermissions) RmRole(role string) bool { } func (aP *AccountPermissions) Copy() *AccountPermissions { + if aP == nil { + return nil + } r := make([]string, len(aP.Roles)) copy(r, aP.Roles) return &AccountPermissions{ diff --git a/state/execution.go b/state/execution.go index 7e8458d8..9d3f4c6b 100644 --- a/state/execution.go +++ b/state/execution.go @@ -405,6 +405,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea // 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) } @@ -431,9 +432,16 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea } ) - // Maybe create a new callee account if - // this transaction is creating a new contract. + // get or create callee if !createAccount { + if outAcc == nil { + // check if its an snative + if _, ok := vm.RegisteredSNativeContracts[LeftPadWord256(tx.Address)]; ok { + // set the outAcc (simply a placeholder until we reach the call) + outAcc = &account.Account{Address: tx.Address} + } + } + if outAcc == nil || len(outAcc.Code) == 0 { // if you call an account that doesn't exist // or an account with no code then we take fees (sorry pal) @@ -456,13 +464,15 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea log.Debug(Fmt("Calling contract %X with code %X", callee.Address, callee.Code)) } else { callee = txCache.CreateAccount(caller) + txCache.UpdateAccount(callee) log.Debug(Fmt("Created new account %X", callee.Address)) code = tx.Data } log.Debug(Fmt("Code for this contract: %X", code)) - txCache.UpdateAccount(caller) // because we adjusted by input above, and bumped nonce maybe. - txCache.UpdateAccount(callee) // because we adjusted by input above. + txCache.UpdateAccount(caller) // because we bumped nonce + txCache.UpdateAccount(callee) // so the txCache knows about the callee and the transfer takes effect + vmach := vm.NewVM(txCache, params, caller.Address, account.HashSignBytes(_s.ChainID, tx)) vmach.SetFireable(evc) diff --git a/vm/snative.go b/vm/snative.go index 4e29ad23..741a522e 100644 --- a/vm/snative.go +++ b/vm/snative.go @@ -45,7 +45,7 @@ const ( TopSNativePermission ptypes.PermFlag = FirstSNativePerm << (NumSNativePermissions - 1) ) -var registeredSNativeContracts = map[Word256]ptypes.PermFlag{ +var RegisteredSNativeContracts = map[Word256]ptypes.PermFlag{ LeftPadWord256([]byte("hasBasePerm")): HasBasePerm, LeftPadWord256([]byte("setBasePerm")): SetBasePerm, LeftPadWord256([]byte("unsetBasePerm")): UnsetBasePerm, @@ -60,7 +60,7 @@ var registeredSNativeContracts = map[Word256]ptypes.PermFlag{ type SNativeContract func(acc *Account, input []byte) (output []byte, err error) func (vm *VM) SNativeContract(name Word256) SNativeContract { - flag := registeredSNativeContracts[name] + flag := RegisteredSNativeContracts[name] switch flag { case HasBasePerm: return vm.hasBasePerm diff --git a/vm/vm.go b/vm/vm.go index 3462eb8e..c7ce3611 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -117,6 +117,17 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value int64, gas } }() + // if code is empty, callee may be snative contract + if vm.snative && len(code) == 0 { + if snativeContract := vm.SNativeContract(callee.Address); snativeContract != nil { + output, err = snativeContract(caller, input) + if err != nil { + *exception = err.Error() + } + return + } + } + if err = transfer(caller, callee, value); err != nil { *exception = err.Error() return @@ -746,9 +757,6 @@ 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 snativeContract := vm.SNativeContract(addr); vm.snative && snativeContract != nil { - // Secure native contract (with access to chain state) - ret, err = snativeContract(callee, args) } else { // EVM contract if ok = useGas(gas, GasGetAccount); !ok { @@ -766,13 +774,15 @@ 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 { - // if we have not seen the account before, create it - // so we can send funds - if vm.perms && !vm.HasPermission(caller, ptypes.CreateAccount) { - return nil, ErrPermission{"create_account"} - } - acc = &Account{ - Address: addr, + if _, ok := RegisteredSNativeContracts[addr]; vm.snative && ok { + acc = &Account{Address: addr} + } else { + // if we have not seen the account before, create it + // so we can send funds + if vm.perms && !vm.HasPermission(caller, ptypes.CreateAccount) { + return nil, ErrPermission{"create_account"} + } + acc = &Account{Address: addr} } vm.appState.UpdateAccount(acc) }