diff --git a/state/execution.go b/state/execution.go index a5f9f6de2..1dcade791 100644 --- a/state/execution.go +++ b/state/execution.go @@ -461,13 +461,19 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea // NOTE: Call() transfers the value from caller to callee iff call succeeds. if isDoug { - // we need to bind a copy of the accounts tree (in the txCache) + // we need to bind a copy of the accounts tree (from the txCache) // so the gendoug can make a native call to create accounts and update // permissions setupDoug(vmach, txCache, _s) } - // set the contracts permissions - vmach.SetPermissions(inAcc.Permissions.Send, inAcc.Permissions.Call, inAcc.Permissions.Create) + // set the contract's permissions + // (the vm doesn't know about account.Account or account.Permissions + vmach.SetPermissionsGetter(func(vmAcc *vm.Account) (bool, bool, bool) { + stAcc := toStateAccount(vmAcc) + p := stAcc.Permissions + return p.Send, p.Call, p.Create + }) + vmach.SetPermissions(callee) ret, err := vmach.Call(caller, callee, code, tx.Data, value, &gas) exception := "" @@ -853,7 +859,6 @@ func setupDoug(vmach *vm.VM, txCache *TxCache, _s *State) { permInt = 0x0 } return LeftPadWord256([]byte{permInt}).Bytes(), nil - } // set takes (address, permissionNum, permissionValue Word256), returns the permission value diff --git a/state/make_txs.go b/state/make_txs.go new file mode 100644 index 000000000..01f361d4d --- /dev/null +++ b/state/make_txs.go @@ -0,0 +1,83 @@ +package state + +import ( + "fmt" + "github.com/tendermint/tendermint/account" + "github.com/tendermint/tendermint/types" +) + +//---------------------------------------------------------------------------- +// SendTx interface for adding inputs/outputs and adding signatures + +func NewSendTx() *types.SendTx { + return &types.SendTx{ + Inputs: []*types.TxInput{}, + Outputs: []*types.TxOutput{}, + } +} + +func SendTxAddInput(st AccountGetter, tx *types.SendTx, pubkey account.PubKey, amt uint64) error { + addr := pubkey.Address() + acc := st.GetAccount(addr) + if acc == nil { + return fmt.Errorf("Invalid address %X from pubkey %X", addr, pubkey) + } + + tx.Inputs = append(tx.Inputs, &types.TxInput{ + Address: addr, + Amount: amt, + Sequence: uint(acc.Sequence) + 1, + Signature: account.SignatureEd25519{}, + PubKey: pubkey, + }) + return nil +} + +func SendTxAddOutput(tx *types.SendTx, addr []byte, amt uint64) error { + tx.Outputs = append(tx.Outputs, &types.TxOutput{ + Address: addr, + Amount: amt, + }) + return nil +} + +func SignSendTx(tx *types.SendTx, i int, privAccount *account.PrivAccount) error { + if i >= len(tx.Inputs) { + return fmt.Errorf("Index %v is greater than number of inputs (%v)", i, len(tx.Inputs)) + } + tx.Inputs[i].PubKey = privAccount.PubKey + tx.Inputs[i].Signature = privAccount.Sign(tx) + return nil +} + +//---------------------------------------------------------------------------- +// CallTx interface for creating tx + +func NewCallTx(st AccountGetter, from account.PubKey, to, data []byte, amt, gasLimit, fee uint64) (*types.CallTx, error) { + addr := from.Address() + acc := st.GetAccount(addr) + if acc == nil { + return nil, fmt.Errorf("Invalid address %X from pubkey %X", addr, from) + } + + input := &types.TxInput{ + Address: addr, + Amount: amt, + Sequence: uint(acc.Sequence) + 1, + Signature: account.SignatureEd25519{}, + PubKey: from, + } + + return &types.CallTx{ + Input: input, + Address: to, + GasLimit: gasLimit, + Fee: fee, + Data: data, + }, nil +} + +func SignCallTx(tx *types.CallTx, privAccount *account.PrivAccount) { + tx.Input.PubKey = privAccount.PubKey + tx.Input.Signature = privAccount.Sign(tx) +} diff --git a/state/permissions_test.go b/state/permissions_test.go new file mode 100644 index 000000000..77c0ab6be --- /dev/null +++ b/state/permissions_test.go @@ -0,0 +1,552 @@ +package state + +import ( + "fmt" + "strconv" + "testing" + "time" + + "github.com/tendermint/tendermint/account" + . "github.com/tendermint/tendermint/common" + dbm "github.com/tendermint/tendermint/db" + "github.com/tendermint/tendermint/events" + "github.com/tendermint/tendermint/types" +) + +/* +To Test: + +- SendTx: +x - 1 input, no perm, call perm, create perm +x - 1 input, perm +x - 2 inputs, one with perm one without + +- CallTx +x - 1 input, no perm, send perm, create perm +x - 1 input, perm +x - contract runs call but doesn't have call perm +x - contract runs call and has call perm +x - contract runs call (with perm), runs contract that runs call (without perm) +x - contract runs call (with perm), runs contract that runs call (with perm) + +- CallTx (Create) +x - 1 input, no perm, send perm, call perm + - 1 input, perm + - contract runs create but doesn't have create perm + - contract runs create but has perm + - contract runs call with empty address (has call perm, not create perm) + - contract runs call with empty address (has call perm, and create perm) + +- BondTx + - 1 input, no perm + - 1 input, perm + - 2 inputs, one with perm one without + +- Gendoug: get/set/add/rm +*/ + +// keys +var ( + user = makeUsers(5) +) + +func makeUsers(n int) []*account.PrivAccount { + accounts := []*account.PrivAccount{} + for i := 0; i < n; i++ { + secret := []byte("mysecret" + strconv.Itoa(i)) + user := account.GenPrivAccountFromSecret(secret) + accounts = append(accounts, user) + } + return accounts +} + +var ( + PermsAllFalse = account.Permissions{ + Send: false, + Call: false, + Create: false, + Bond: false, + } +) + +func newBaseGenDoc(globalPerm, accountPerm account.Permissions) GenesisDoc { + genAccounts := []GenesisAccount{} + for _, u := range user[:5] { + accPerm := accountPerm + genAccounts = append(genAccounts, GenesisAccount{ + Address: u.Address, + Amount: 1000000, + Permissions: &accPerm, + }) + } + + return GenesisDoc{ + GenesisTime: time.Now(), + Params: &GenesisParams{ + GlobalPermissions: &globalPerm, + }, + Accounts: genAccounts, + Validators: []GenesisValidator{ + GenesisValidator{ + PubKey: user[0].PubKey.(account.PubKeyEd25519), + Amount: 10, + UnbondTo: []GenesisAccount{ + GenesisAccount{ + Address: user[0].Address, + }, + }, + }, + }, + } +} + +func TestSendFails(t *testing.T) { + stateDB := dbm.GetDB("state") + genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) + genDoc.Accounts[1].Permissions.Send = true + genDoc.Accounts[2].Permissions.Call = true + genDoc.Accounts[3].Permissions.Create = true + st := MakeGenesisState(stateDB, &genDoc) + blockCache := NewBlockCache(st) + + //------------------- + // send txs + + // simple send tx should fail + tx := NewSendTx() + if err := SendTxAddInput(blockCache, tx, user[0].PubKey, 5); err != nil { + t.Fatal(err) + } + SendTxAddOutput(tx, user[1].Address, 5) + SignSendTx(tx, 0, user[0]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) + } + + // simple send tx with call perm should fail + tx = NewSendTx() + if err := SendTxAddInput(blockCache, tx, user[2].PubKey, 5); err != nil { + t.Fatal(err) + } + SendTxAddOutput(tx, user[4].Address, 5) + SignSendTx(tx, 0, user[2]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) + } + + // simple send tx with create perm should fail + tx = NewSendTx() + if err := SendTxAddInput(blockCache, tx, user[3].PubKey, 5); err != nil { + t.Fatal(err) + } + SendTxAddOutput(tx, user[4].Address, 5) + SignSendTx(tx, 0, user[3]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) + } +} + +func TestCallFails(t *testing.T) { + stateDB := dbm.GetDB("state") + genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) + genDoc.Accounts[1].Permissions.Send = true + genDoc.Accounts[2].Permissions.Call = true + genDoc.Accounts[3].Permissions.Create = true + st := MakeGenesisState(stateDB, &genDoc) + blockCache := NewBlockCache(st) + + //------------------- + // call txs + + // simple call tx should fail + tx, _ := NewCallTx(blockCache, user[0].PubKey, user[4].Address, nil, 100, 100, 100) + SignCallTx(tx, user[0]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) + } + + // simple call tx with send permission should fail + tx, _ = NewCallTx(blockCache, user[1].PubKey, user[4].Address, nil, 100, 100, 100) + SignCallTx(tx, user[1]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) + } + + // simple call tx with create permission should fail + tx, _ = NewCallTx(blockCache, user[3].PubKey, user[4].Address, nil, 100, 100, 100) + SignCallTx(tx, user[3]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) + } + + //------------------- + // create txs + + // simple call create tx should fail + tx, _ = NewCallTx(blockCache, user[0].PubKey, nil, nil, 100, 100, 100) + SignCallTx(tx, user[0]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) + } + + // simple call create tx with send perm should fail + tx, _ = NewCallTx(blockCache, user[1].PubKey, nil, nil, 100, 100, 100) + SignCallTx(tx, user[1]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) + } + + // simple call create tx with call perm should fail + tx, _ = NewCallTx(blockCache, user[2].PubKey, nil, nil, 100, 100, 100) + SignCallTx(tx, user[2]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) + } +} + +func TestSendPermission(t *testing.T) { + stateDB := dbm.GetDB("state") + genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) + genDoc.Accounts[0].Permissions.Send = true // give the 0 account permission + st := MakeGenesisState(stateDB, &genDoc) + blockCache := NewBlockCache(st) + + // A single input, having the permission, should succeed + tx := NewSendTx() + if err := SendTxAddInput(blockCache, tx, user[0].PubKey, 5); err != nil { + t.Fatal(err) + } + SendTxAddOutput(tx, user[1].Address, 5) + SignSendTx(tx, 0, user[0]) + if err := ExecTx(blockCache, tx, true, nil); err != nil { + t.Fatal("Transaction failed", err) + } + + // Two inputs, one with permission, one without, should fail + tx = NewSendTx() + if err := SendTxAddInput(blockCache, tx, user[0].PubKey, 5); err != nil { + t.Fatal(err) + } + if err := SendTxAddInput(blockCache, tx, user[1].PubKey, 5); err != nil { + t.Fatal(err) + } + SendTxAddOutput(tx, user[2].Address, 10) + SignSendTx(tx, 0, user[0]) + SignSendTx(tx, 1, user[1]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) + } +} + +// convenience function for contract that calls a given address +func callContractCode(contractAddr []byte) []byte { + gas1, gas2 := byte(0x1), byte(0x1) + value := byte(0x1) + inOff, inSize := byte(0x0), byte(0x0) // no call data + retOff, retSize := byte(0x0), byte(0x20) + // this is the code we want to run (call a contract and return) + contractCode := []byte{0x60, retSize, 0x60, retOff, 0x60, inSize, 0x60, inOff, 0x60, value, 0x73} + contractCode = append(contractCode, contractAddr...) + contractCode = append(contractCode, []byte{0x61, gas1, gas2, 0xf1, 0x60, 0x20, 0x60, 0x0, 0xf3}...) + return contractCode +} + +func TestCallPermission(t *testing.T) { + stateDB := dbm.GetDB("state") + genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) + genDoc.Accounts[0].Permissions.Call = true // give the 0 account permission + st := MakeGenesisState(stateDB, &genDoc) + blockCache := NewBlockCache(st) + + //------------------------------ + // call to simple contract + fmt.Println("##### SIMPLE CONTRACT") + + // create simple contract + simpleContractAddr := NewContractAddress(user[0].Address, 100) + simpleAcc := &account.Account{ + Address: simpleContractAddr, + Balance: 0, + Code: []byte{0x60}, + Sequence: 0, + StorageRoot: Zero256.Bytes(), + } + st.UpdateAccount(simpleAcc) + + // A single input, having the permission, should succeed + tx, _ := NewCallTx(blockCache, user[0].PubKey, simpleContractAddr, nil, 100, 100, 100) + SignCallTx(tx, user[0]) + if err := ExecTx(blockCache, tx, true, nil); err != nil { + t.Fatal("Transaction failed", err) + } + + //---------------------------------------------------------- + // call to contract that calls simple contract - without perm + fmt.Println("##### CALL TO SIMPLE CONTRACT (FAIL)") + + // create contract that calls the simple contract + contractCode := callContractCode(simpleContractAddr) + caller1ContractAddr := NewContractAddress(user[0].Address, 101) + caller1Acc := &account.Account{ + Address: caller1ContractAddr, + Balance: 0, + Code: contractCode, + Sequence: 0, + StorageRoot: Zero256.Bytes(), + } + blockCache.UpdateAccount(caller1Acc) + + // A single input, having the permission, but the contract doesn't have permission + tx, _ = NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100) + SignCallTx(tx, user[0]) + + // we need to subscribe to the Receive event to detect the exception + exception := execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) // + if exception == "" { + t.Fatal("Expected exception") + } + + //---------------------------------------------------------- + // call to contract that calls simple contract - with perm + fmt.Println("##### CALL TO SIMPLE CONTRACT (PASS)") + + // A single input, having the permission, and the contract has permission + caller1Acc.Permissions.Call = true + blockCache.UpdateAccount(caller1Acc) + tx, _ = NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100) + SignCallTx(tx, user[0]) + + // we need to subscribe to the Receive event to detect the exception + exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) // + if exception != "" { + t.Fatal("Unexpected exception:", exception) + } + + //---------------------------------------------------------- + // call to contract that calls contract that calls simple contract - without perm + // caller1Contract calls simpleContract. caller2Contract calls caller1Contract. + // caller1Contract does not have call perms, but caller2Contract does. + fmt.Println("##### CALL TO CONTRACT CALLING SIMPLE CONTRACT (FAIL)") + + contractCode2 := callContractCode(caller1ContractAddr) + caller2ContractAddr := NewContractAddress(user[0].Address, 102) + caller2Acc := &account.Account{ + Address: caller2ContractAddr, + Balance: 1000, + Code: contractCode2, + Sequence: 0, + StorageRoot: Zero256.Bytes(), + } + caller1Acc.Permissions.Call = false + caller2Acc.Permissions.Call = true + blockCache.UpdateAccount(caller1Acc) + blockCache.UpdateAccount(caller2Acc) + + tx, _ = NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100) + SignCallTx(tx, user[0]) + + // we need to subscribe to the Receive event to detect the exception + exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) // + if exception == "" { + t.Fatal("Expected exception") + } + + //---------------------------------------------------------- + // call to contract that calls contract that calls simple contract - without perm + // caller1Contract calls simpleContract. caller2Contract calls caller1Contract. + // both caller1 and caller2 have permission + fmt.Println("##### CALL TO CONTRACT CALLING SIMPLE CONTRACT (PASS)") + + caller1Acc.Permissions.Call = true + blockCache.UpdateAccount(caller1Acc) + + tx, _ = NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100) + SignCallTx(tx, user[0]) + + // we need to subscribe to the Receive event to detect the exception + exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) // + if exception != "" { + t.Fatal("Unexpected exception", exception) + } +} + +// run ExecTx and wait for the Receive event on given addr +// returns error/exception +func execTxWaitEvent(t *testing.T, blockCache *BlockCache, tx types.Tx, addr []byte) string { + evsw := new(events.EventSwitch) + evsw.Start() + ch := make(chan interface{}) + evsw.AddListenerForEvent("test", types.EventStringAccReceive(addr), func(msg interface{}) { + ch <- msg + }) + evc := events.NewEventCache(evsw) + go func() { + if err := ExecTx(blockCache, tx, true, evc); err != nil { + ch <- err.Error() + } + evc.Flush() + }() + msg := <-ch + switch ev := msg.(type) { + case types.EventMsgCallTx: + return ev.Exception + case types.EventMsgCall: + return ev.Exception + case string: + return ev + } + return "" + +} + +/* +- CallTx (Create) +x - 1 input, no perm, send perm, call perm + - CallTx to create new contract, perm + - contract runs create but doesn't have create perm + - contract runs create but has perm + - contract runs call with empty address (has call perm, not create perm) + - contract runs call with empty address (has call perm, and create perm) + - contract runs call (with perm), runs contract that runs create (without perm) + - contract runs call (with perm), runs contract that runs create (with perm) +*/ + +// TODO: this is just a copy of CALL... +func TestCreatePermission(t *testing.T) { + stateDB := dbm.GetDB("state") + genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) + genDoc.Accounts[0].Permissions.Call = true // give the 0 account permission + st := MakeGenesisState(stateDB, &genDoc) + blockCache := NewBlockCache(st) + + //------------------------------ + // create simple contract + fmt.Println("##### SIMPLE CONTRACT") + + // create simple contract + simpleContractAddr := NewContractAddress(user[0].Address, 100) + simpleAcc := &account.Account{ + Address: simpleContractAddr, + Balance: 0, + Code: []byte{0x60}, + Sequence: 0, + StorageRoot: Zero256.Bytes(), + } + st.UpdateAccount(simpleAcc) + + // A single input, having the permission, should succeed + tx, _ := NewCallTx(blockCache, user[0].PubKey, simpleContractAddr, nil, 100, 100, 100) + SignCallTx(tx, user[0]) + if err := ExecTx(blockCache, tx, true, nil); err != nil { + t.Fatal("Transaction failed", err) + } + + //---------------------------------------------------------- + // call to contract that calls simple contract - without perm + fmt.Println("##### CALL TO SIMPLE CONTRACT (FAIL)") + + // create contract that calls the simple contract + contractCode := callContractCode(simpleContractAddr) + caller1ContractAddr := NewContractAddress(user[0].Address, 101) + caller1Acc := &account.Account{ + Address: caller1ContractAddr, + Balance: 0, + Code: contractCode, + Sequence: 0, + StorageRoot: Zero256.Bytes(), + } + blockCache.UpdateAccount(caller1Acc) + + // A single input, having the permission, but the contract doesn't have permission + tx, _ = NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100) + SignCallTx(tx, user[0]) + + // we need to subscribe to the Receive event to detect the exception + exception := execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) // + if exception == "" { + t.Fatal("Expected exception") + } + + //---------------------------------------------------------- + // call to contract that calls simple contract - with perm + fmt.Println("##### CALL TO SIMPLE CONTRACT (PASS)") + + // A single input, having the permission, and the contract has permission + caller1Acc.Permissions.Call = true + blockCache.UpdateAccount(caller1Acc) + tx, _ = NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100) + SignCallTx(tx, user[0]) + + // we need to subscribe to the Receive event to detect the exception + exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) // + if exception != "" { + t.Fatal("Unexpected exception:", exception) + } + + //---------------------------------------------------------- + // call to contract that calls contract that calls simple contract - without perm + // caller1Contract calls simpleContract. caller2Contract calls caller1Contract. + // caller1Contract does not have call perms, but caller2Contract does. + fmt.Println("##### CALL TO CONTRACT CALLING SIMPLE CONTRACT (FAIL)") + + contractCode2 := callContractCode(caller1ContractAddr) + caller2ContractAddr := NewContractAddress(user[0].Address, 102) + caller2Acc := &account.Account{ + Address: caller2ContractAddr, + Balance: 1000, + Code: contractCode2, + Sequence: 0, + StorageRoot: Zero256.Bytes(), + } + caller1Acc.Permissions.Call = false + caller2Acc.Permissions.Call = true + blockCache.UpdateAccount(caller1Acc) + blockCache.UpdateAccount(caller2Acc) + + tx, _ = NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100) + SignCallTx(tx, user[0]) + + // we need to subscribe to the Receive event to detect the exception + exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) // + if exception == "" { + t.Fatal("Expected exception") + } + + //---------------------------------------------------------- + // call to contract that calls contract that calls simple contract - without perm + // caller1Contract calls simpleContract. caller2Contract calls caller1Contract. + // both caller1 and caller2 have permission + fmt.Println("##### CALL TO CONTRACT CALLING SIMPLE CONTRACT (PASS)") + + caller1Acc.Permissions.Call = true + blockCache.UpdateAccount(caller1Acc) + + tx, _ = NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100) + SignCallTx(tx, user[0]) + + // we need to subscribe to the Receive event to detect the exception + exception = execTxWaitEvent(t, blockCache, tx, caller1ContractAddr) // + if exception != "" { + t.Fatal("Unexpected exception", exception) + } +} diff --git a/vm/vm.go b/vm/vm.go index cafe56f85..94579636b 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -63,16 +63,21 @@ type VM struct { doug bool // is this the gendoug contract sendPerm, callPerm, createPerm bool // this contract's permissions + + getPerms func(*Account) (bool, bool, bool) // gets permissions out of an account (wraps toStateFunction to get perms out of Other) } func NewVM(appState AppState, params Params, origin Word256, txid []byte) *VM { return &VM{ - appState: appState, - params: params, - origin: origin, - callDepth: 0, - txid: txid, - doug: false, + appState: appState, + params: params, + origin: origin, + callDepth: 0, + txid: txid, + doug: false, + sendPerm: true, + callPerm: true, + createPerm: true, } } @@ -86,8 +91,19 @@ func (vm *VM) EnableDoug() { vm.doug = true } +func (vm *VM) SetPermissionsGetter(getPerms func(acc *Account) (bool, bool, bool)) { + vm.getPerms = getPerms +} + +func (vm *VM) SetPermissions(acc *Account) { + if vm.getPerms != nil { + send, call, create := vm.getPerms(acc) + vm.setPermissions(send, call, create) + } +} + // set the contract's generic permissions -func (vm *VM) SetPermissions(send, call, create bool) { +func (vm *VM) setPermissions(send, call, create bool) { // TODO: distinction between send and call not defined at the VM yet (it's all through a CALL!) vm.sendPerm = send vm.callPerm = call @@ -744,6 +760,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas ret, err = nativeContract(args, &gasLimit) } else if dougContract := dougContracts[addr]; vm.doug && dougContract != nil { // This is Doug and we're calling a doug contract + // TODO: Doug contract should have all permissions ret, err = dougContract(args, &gasLimit) } else { // EVM contract @@ -769,14 +786,17 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas } vm.appState.UpdateAccount(acc) } + // copy permissions and set permissions for new contract + send, call, create := vm.sendPerm, vm.callPerm, vm.createPerm + vm.SetPermissions(acc) ret, err = vm.Call(callee, acc, acc.Code, args, value, gas) + vm.sendPerm, vm.callPerm, vm.createPerm = send, call, create } } // Push result if err != nil { dbg.Printf("error on call: %s", err.Error()) - // TODO: fire event stack.Push(Zero256) } else { stack.Push(One256)