From 2ec3d0611f8c7aa607c4247f3dd9f038bbf900db Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 20 May 2015 19:20:06 -0400 Subject: [PATCH] bond perm and tests --- permission/types/permissions.go | 27 +-- state/execution.go | 36 ++-- state/make_txs.go | 44 +++++ state/permissions_test.go | 288 +++++++++++++++++--------------- vm/snative.go | 81 +++++++-- vm/vm.go | 4 +- 6 files changed, 301 insertions(+), 179 deletions(-) diff --git a/permission/types/permissions.go b/permission/types/permissions.go index 2ddaa4804..7e31e5ec7 100644 --- a/permission/types/permissions.go +++ b/permission/types/permissions.go @@ -15,19 +15,20 @@ var ( ) // A particular permission -type PermFlag uint16 +type PermFlag uint64 // Base permission references are like unix (the index is already bit shifted) const ( - Send PermFlag = 1 << iota // 1 - Call // 2 - Create // 4 - Bond // 8 - Root // 16 + Root PermFlag = 1 << iota // 1 + Send // 2 + Call // 4 + CreateContract // 8 + CreateAccount // 16 + Bond // 32 - DefaultBBPB = Send | Call | Create | Bond + DefaultBBPB = Send | Call | CreateContract | CreateAccount | Bond - NumBasePermissions uint = 5 + NumBasePermissions uint = 6 TopBasePermission PermFlag = 1 << (NumBasePermissions - 1) AllSet PermFlag = 1< TopBasePermission { - return false, ErrInvalidPermission(ty) - } if p.SetBit&ty == 0 { return false, ErrValueNotSet(ty) } @@ -62,10 +60,6 @@ func (p *BasePermissions) Get(ty PermFlag) (bool, error) { // Set a permission bit. Will set the permission's set bit to true. func (p *BasePermissions) Set(ty PermFlag, value bool) error { - if ty > TopBasePermission { - return ErrInvalidPermission(ty) - } - p.SetBit |= ty if value { p.Perms |= ty @@ -77,9 +71,6 @@ func (p *BasePermissions) Set(ty PermFlag, value bool) error { // Set the permission's set bit to false func (p *BasePermissions) Unset(ty PermFlag) error { - if ty > TopBasePermission { - return ErrInvalidPermission(ty) - } p.SetBit &= ^ty return nil } diff --git a/state/execution.go b/state/execution.go index fd06234f3..f2a6d2373 100644 --- a/state/execution.go +++ b/state/execution.go @@ -634,7 +634,12 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea return err } - if !hasBondPermission(blockCache, accounts) { + bondAcc := blockCache.GetAccount(tx.PubKey.Address()) + if !hasBondPermission(blockCache, bondAcc) { + return fmt.Errorf("The bonder does not have permission to bond") + } + + if !hasBondOrSendPermission(blockCache, accounts) { return fmt.Errorf("At least one input lacks permission to bond") } @@ -786,18 +791,23 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea } //--------------------------------------------------------------- -// TODO: for debug log the failed accounts // Get permission on an account or fall back to global value func HasPermission(state AccountGetter, acc *account.Account, perm ptypes.PermFlag) bool { + if acc == nil { + // TODO + // this needs to fall back to global or do some other specific things + // eg. a bondAcc may be nil and so can only bond if global bonding is true + } + v, err := acc.Permissions.Base.Get(perm) - fmt.Printf("has permission? %x %v %b %v %v\n", acc.Address, acc.Permissions, perm, v, err) if _, ok := err.(ptypes.ErrValueNotSet); ok { return HasPermission(state, state.GetAccount(ptypes.GlobalPermissionsAddress), perm) } return v } +// TODO: for debug log the failed accounts func hasSendPermission(state AccountGetter, accs map[string]*account.Account) bool { for _, acc := range accs { if !HasPermission(state, acc, ptypes.Send) { @@ -808,23 +818,23 @@ func hasSendPermission(state AccountGetter, accs map[string]*account.Account) bo } func hasCallPermission(state AccountGetter, acc *account.Account) bool { - if !HasPermission(state, acc, ptypes.Call) { - return false - } - return true + return HasPermission(state, acc, ptypes.Call) } func hasCreatePermission(state AccountGetter, acc *account.Account) bool { - if !HasPermission(state, acc, ptypes.Create) { - return false - } - return true + return HasPermission(state, acc, ptypes.CreateContract) } -func hasBondPermission(state AccountGetter, accs map[string]*account.Account) bool { +func hasBondPermission(state AccountGetter, acc *account.Account) bool { + return HasPermission(state, acc, ptypes.Bond) +} + +func hasBondOrSendPermission(state AccountGetter, accs map[string]*account.Account) bool { for _, acc := range accs { if !HasPermission(state, acc, ptypes.Bond) { - return false + if !HasPermission(state, acc, ptypes.Send) { + return false + } } } return true diff --git a/state/make_txs.go b/state/make_txs.go index 01f361d4d..3cd821145 100644 --- a/state/make_txs.go +++ b/state/make_txs.go @@ -81,3 +81,47 @@ func SignCallTx(tx *types.CallTx, privAccount *account.PrivAccount) { tx.Input.PubKey = privAccount.PubKey tx.Input.Signature = privAccount.Sign(tx) } + +//---------------------------------------------------------------------------- +// BondTx interface for adding inputs/outputs and adding signatures + +func NewBondTx() *types.BondTx { + return &types.BondTx{ + Inputs: []*types.TxInput{}, + UnbondTo: []*types.TxOutput{}, + } +} + +func BondTxAddInput(st AccountGetter, tx *types.BondTx, 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 BondTxAddOutput(tx *types.BondTx, addr []byte, amt uint64) error { + tx.UnbondTo = append(tx.UnbondTo, &types.TxOutput{ + Address: addr, + Amount: amt, + }) + return nil +} + +func SignBondTx(tx *types.BondTx, 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 +} diff --git a/state/permissions_test.go b/state/permissions_test.go index 55edf1b18..b82cc98b6 100644 --- a/state/permissions_test.go +++ b/state/permissions_test.go @@ -39,9 +39,12 @@ x - contract runs create but has perm x - contract runs call with empty address (has call and create perm) - BondTx - - 1 input, no perm - - 1 input, perm - - 2 inputs, one with perm one without +x - 1 input, no perm +x - 1 input, perm +x - 1 bonder with perm, input without send or bond +x - 1 bonder with perm, input with send +x - 1 bonder with perm, input with bond +x - 2 inputs, one with perm one without - SendTx for new account ? CALL for new account? @@ -103,7 +106,7 @@ func TestSendFails(t *testing.T) { genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) genDoc.Accounts[1].Permissions.Base.Set(ptypes.Send, true) genDoc.Accounts[2].Permissions.Base.Set(ptypes.Call, true) - genDoc.Accounts[3].Permissions.Base.Set(ptypes.Create, true) + genDoc.Accounts[3].Permissions.Base.Set(ptypes.CreateContract, true) st := MakeGenesisState(stateDB, &genDoc) blockCache := NewBlockCache(st) @@ -111,12 +114,12 @@ func TestSendFails(t *testing.T) { // send txs // simple send tx should fail - tx := NewSendTx() - if err := SendTxAddInput(blockCache, tx, user[0].PubKey, 5); err != nil { + tx := types.NewSendTx() + if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil { t.Fatal(err) } - SendTxAddOutput(tx, user[1].Address, 5) - SignSendTx(tx, 0, user[0]) + tx.AddOutput(user[1].Address, 5) + tx.SignInput(0, user[0]) if err := ExecTx(blockCache, tx, true, nil); err == nil { t.Fatal("Expected error") } else { @@ -124,12 +127,12 @@ func TestSendFails(t *testing.T) { } // simple send tx with call perm should fail - tx = NewSendTx() - if err := SendTxAddInput(blockCache, tx, user[2].PubKey, 5); err != nil { + tx = types.NewSendTx() + if err := tx.AddInput(blockCache, user[2].PubKey, 5); err != nil { t.Fatal(err) } - SendTxAddOutput(tx, user[4].Address, 5) - SignSendTx(tx, 0, user[2]) + tx.AddOutput(user[4].Address, 5) + tx.SignInput(0, user[2]) if err := ExecTx(blockCache, tx, true, nil); err == nil { t.Fatal("Expected error") } else { @@ -137,12 +140,12 @@ func TestSendFails(t *testing.T) { } // simple send tx with create perm should fail - tx = NewSendTx() - if err := SendTxAddInput(blockCache, tx, user[3].PubKey, 5); err != nil { + tx = types.NewSendTx() + if err := tx.AddInput(blockCache, user[3].PubKey, 5); err != nil { t.Fatal(err) } - SendTxAddOutput(tx, user[4].Address, 5) - SignSendTx(tx, 0, user[3]) + tx.AddOutput(user[4].Address, 5) + tx.SignInput(0, user[3]) if err := ExecTx(blockCache, tx, true, nil); err == nil { t.Fatal("Expected error") } else { @@ -155,7 +158,7 @@ func TestCallFails(t *testing.T) { genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) genDoc.Accounts[1].Permissions.Base.Set(ptypes.Send, true) genDoc.Accounts[2].Permissions.Base.Set(ptypes.Call, true) - genDoc.Accounts[3].Permissions.Base.Set(ptypes.Create, true) + genDoc.Accounts[3].Permissions.Base.Set(ptypes.CreateContract, true) st := MakeGenesisState(stateDB, &genDoc) blockCache := NewBlockCache(st) @@ -163,8 +166,8 @@ func TestCallFails(t *testing.T) { // call txs // simple call tx should fail - tx, _ := NewCallTx(blockCache, user[0].PubKey, user[4].Address, nil, 100, 100, 100) - SignCallTx(tx, user[0]) + tx, _ := types.NewCallTx(blockCache, user[0].PubKey, user[4].Address, nil, 100, 100, 100) + tx.Sign(user[0]) if err := ExecTx(blockCache, tx, true, nil); err == nil { t.Fatal("Expected error") } else { @@ -172,8 +175,8 @@ func TestCallFails(t *testing.T) { } // 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]) + tx, _ = types.NewCallTx(blockCache, user[1].PubKey, user[4].Address, nil, 100, 100, 100) + tx.Sign(user[1]) if err := ExecTx(blockCache, tx, true, nil); err == nil { t.Fatal("Expected error") } else { @@ -181,8 +184,8 @@ func TestCallFails(t *testing.T) { } // 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]) + tx, _ = types.NewCallTx(blockCache, user[3].PubKey, user[4].Address, nil, 100, 100, 100) + tx.Sign(user[3]) if err := ExecTx(blockCache, tx, true, nil); err == nil { t.Fatal("Expected error") } else { @@ -193,8 +196,8 @@ func TestCallFails(t *testing.T) { // create txs // simple call create tx should fail - tx, _ = NewCallTx(blockCache, user[0].PubKey, nil, nil, 100, 100, 100) - SignCallTx(tx, user[0]) + tx, _ = types.NewCallTx(blockCache, user[0].PubKey, nil, nil, 100, 100, 100) + tx.Sign(user[0]) if err := ExecTx(blockCache, tx, true, nil); err == nil { t.Fatal("Expected error") } else { @@ -202,8 +205,8 @@ func TestCallFails(t *testing.T) { } // simple call create tx with send perm should fail - tx, _ = NewCallTx(blockCache, user[1].PubKey, nil, nil, 100, 100, 100) - SignCallTx(tx, user[1]) + tx, _ = types.NewCallTx(blockCache, user[1].PubKey, nil, nil, 100, 100, 100) + tx.Sign(user[1]) if err := ExecTx(blockCache, tx, true, nil); err == nil { t.Fatal("Expected error") } else { @@ -211,8 +214,8 @@ func TestCallFails(t *testing.T) { } // simple call create tx with call perm should fail - tx, _ = NewCallTx(blockCache, user[2].PubKey, nil, nil, 100, 100, 100) - SignCallTx(tx, user[2]) + tx, _ = types.NewCallTx(blockCache, user[2].PubKey, nil, nil, 100, 100, 100) + tx.Sign(user[2]) if err := ExecTx(blockCache, tx, true, nil); err == nil { t.Fatal("Expected error") } else { @@ -280,8 +283,8 @@ func TestCallPermission(t *testing.T) { 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]) + tx, _ := types.NewCallTx(blockCache, user[0].PubKey, simpleContractAddr, nil, 100, 100, 100) + tx.Sign(user[0]) if err := ExecTx(blockCache, tx, true, nil); err != nil { t.Fatal("Transaction failed", err) } @@ -304,8 +307,8 @@ func TestCallPermission(t *testing.T) { 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]) + tx, _ = types.NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100) + tx.Sign(user[0]) // we need to subscribe to the Receive event to detect the exception _, exception := execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(caller1ContractAddr)) // @@ -320,8 +323,8 @@ func TestCallPermission(t *testing.T) { // A single input, having the permission, and the contract has permission caller1Acc.Permissions.Base.Set(ptypes.Call, true) blockCache.UpdateAccount(caller1Acc) - tx, _ = NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100) - SignCallTx(tx, user[0]) + tx, _ = types.NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100) + tx.Sign(user[0]) // we need to subscribe to the Receive event to detect the exception _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(caller1ContractAddr)) // @@ -350,8 +353,8 @@ func TestCallPermission(t *testing.T) { blockCache.UpdateAccount(caller1Acc) blockCache.UpdateAccount(caller2Acc) - tx, _ = NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100) - SignCallTx(tx, user[0]) + tx, _ = types.NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100) + tx.Sign(user[0]) // we need to subscribe to the Receive event to detect the exception _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(caller1ContractAddr)) // @@ -368,8 +371,8 @@ func TestCallPermission(t *testing.T) { caller1Acc.Permissions.Base.Set(ptypes.Call, true) blockCache.UpdateAccount(caller1Acc) - tx, _ = NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100) - SignCallTx(tx, user[0]) + tx, _ = types.NewCallTx(blockCache, user[0].PubKey, caller2ContractAddr, nil, 100, 10000, 100) + tx.Sign(user[0]) // we need to subscribe to the Receive event to detect the exception _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(caller1ContractAddr)) // @@ -381,8 +384,8 @@ func TestCallPermission(t *testing.T) { func TestCreatePermission(t *testing.T) { stateDB := dbm.GetDB("state") genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - genDoc.Accounts[0].Permissions.Base.Set(ptypes.Create, true) // give the 0 account permission - genDoc.Accounts[0].Permissions.Base.Set(ptypes.Call, true) // give the 0 account permission + genDoc.Accounts[0].Permissions.Base.Set(ptypes.CreateContract, true) // give the 0 account permission + genDoc.Accounts[0].Permissions.Base.Set(ptypes.Call, true) // give the 0 account permission st := MakeGenesisState(stateDB, &genDoc) blockCache := NewBlockCache(st) @@ -394,8 +397,8 @@ func TestCreatePermission(t *testing.T) { createCode := wrapContractForCreate(contractCode) // A single input, having the permission, should succeed - tx, _ := NewCallTx(blockCache, user[0].PubKey, nil, createCode, 100, 100, 100) - SignCallTx(tx, user[0]) + tx, _ := types.NewCallTx(blockCache, user[0].PubKey, nil, createCode, 100, 100, 100) + tx.Sign(user[0]) if err := ExecTx(blockCache, tx, true, nil); err != nil { t.Fatal("Transaction failed", err) } @@ -419,8 +422,8 @@ func TestCreatePermission(t *testing.T) { createFactoryCode := wrapContractForCreate(factoryCode) // A single input, having the permission, should succeed - tx, _ = NewCallTx(blockCache, user[0].PubKey, nil, createFactoryCode, 100, 100, 100) - SignCallTx(tx, user[0]) + tx, _ = types.NewCallTx(blockCache, user[0].PubKey, nil, createFactoryCode, 100, 100, 100) + tx.Sign(user[0]) if err := ExecTx(blockCache, tx, true, nil); err != nil { t.Fatal("Transaction failed", err) } @@ -439,8 +442,8 @@ func TestCreatePermission(t *testing.T) { fmt.Println("###### CALL THE FACTORY (FAIL)") // A single input, having the permission, should succeed - tx, _ = NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 100, 100) - SignCallTx(tx, user[0]) + tx, _ = types.NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 100, 100) + tx.Sign(user[0]) // we need to subscribe to the Receive event to detect the exception _, exception := execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(contractAddr)) // if exception == "" { @@ -451,12 +454,12 @@ func TestCreatePermission(t *testing.T) { // call the contract (should PASS) fmt.Println("###### CALL THE FACTORY (PASS)") - contractAcc.Permissions.Base.Set(ptypes.Create, true) + contractAcc.Permissions.Base.Set(ptypes.CreateContract, true) blockCache.UpdateAccount(contractAcc) // A single input, having the permission, should succeed - tx, _ = NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 100, 100) - SignCallTx(tx, user[0]) + tx, _ = types.NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 100, 100) + tx.Sign(user[0]) // we need to subscribe to the Receive event to detect the exception _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(contractAddr)) // if exception != "" { @@ -478,12 +481,12 @@ func TestCreatePermission(t *testing.T) { Permissions: ptypes.NewAccountPermissions(), } contractAcc.Permissions.Base.Set(ptypes.Call, true) - contractAcc.Permissions.Base.Set(ptypes.Create, true) + contractAcc.Permissions.Base.Set(ptypes.CreateContract, true) blockCache.UpdateAccount(contractAcc) // this should call the 0 address but not create ... - tx, _ = NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 10000, 100) - SignCallTx(tx, user[0]) + tx, _ = types.NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 10000, 100) + tx.Sign(user[0]) // we need to subscribe to the Receive event to detect the exception _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(zeroAddr)) // if exception != "" { @@ -500,109 +503,122 @@ func TestBondPermission(t *testing.T) { genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) st := MakeGenesisState(stateDB, &genDoc) blockCache := NewBlockCache(st) + var bondAcc *account.Account //------------------------------ - // a bond tx from someone without bond perm should fail - tx, _ := NewCallTx(blockCache, user[0].PubKey, nil, createCode, 100, 100, 100) - SignCallTx(tx, user[0]) - if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Transaction failed", err) - } - // ensure the contract is there - contractAddr := NewContractAddress(tx.Input.Address, uint64(tx.Input.Sequence)) - contractAcc := blockCache.GetAccount(contractAddr) - if contractAcc == nil { - t.Fatalf("failed to create contract %X", contractAddr) + // one bonder without permission should fail + tx, _ := types.NewBondTx(user[1].PubKey) + if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil { + t.Fatal(err) } - if bytes.Compare(contractAcc.Code, contractCode) != 0 { - t.Fatalf("contract does not have correct code. Got %X, expected %X", contractAcc.Code, contractCode) + tx.AddOutput(user[1].Address, 5) + tx.SignInput(0, user[1]) + tx.SignBond(user[1]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) } //------------------------------ - // create contract that uses the CREATE op - fmt.Println("##### CREATE FACTORY") - - contractCode = []byte{0x60} - createCode = wrapContractForCreate(contractCode) - factoryCode := createContractCode() - createFactoryCode := wrapContractForCreate(factoryCode) - - // A single input, having the permission, should succeed - tx, _ = NewCallTx(blockCache, user[0].PubKey, nil, createFactoryCode, 100, 100, 100) - SignCallTx(tx, user[0]) + // one bonder with permission should pass + bondAcc = blockCache.GetAccount(user[1].Address) + bondAcc.Permissions.Base.Set(ptypes.Bond, true) + blockCache.UpdateAccount(bondAcc) if err := ExecTx(blockCache, tx, true, nil); err != nil { - t.Fatal("Transaction failed", err) + t.Fatal("Unexpected error", err) } - // ensure the contract is there - contractAddr = NewContractAddress(tx.Input.Address, uint64(tx.Input.Sequence)) - contractAcc = blockCache.GetAccount(contractAddr) - if contractAcc == nil { - t.Fatalf("failed to create contract %X", contractAddr) + + // reset state (we can only bond with an account once ..) + genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) + st = MakeGenesisState(stateDB, &genDoc) + blockCache = NewBlockCache(st) + bondAcc = blockCache.GetAccount(user[1].Address) + bondAcc.Permissions.Base.Set(ptypes.Bond, true) + blockCache.UpdateAccount(bondAcc) + //------------------------------ + // one bonder with permission and an input without send should fail + tx, _ = types.NewBondTx(user[1].PubKey) + if err := tx.AddInput(blockCache, user[2].PubKey, 5); err != nil { + t.Fatal(err) } - if bytes.Compare(contractAcc.Code, factoryCode) != 0 { - t.Fatalf("contract does not have correct code. Got %X, expected %X", contractAcc.Code, factoryCode) + tx.AddOutput(user[1].Address, 5) + tx.SignInput(0, user[2]) + tx.SignBond(user[1]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") + } else { + fmt.Println(err) } + // reset state (we can only bond with an account once ..) + genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) + st = MakeGenesisState(stateDB, &genDoc) + blockCache = NewBlockCache(st) + bondAcc = blockCache.GetAccount(user[1].Address) + bondAcc.Permissions.Base.Set(ptypes.Bond, true) + blockCache.UpdateAccount(bondAcc) //------------------------------ - // call the contract (should FAIL) - fmt.Println("###### CALL THE FACTORY (FAIL)") - - // A single input, having the permission, should succeed - tx, _ = NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 100, 100) - SignCallTx(tx, user[0]) - // we need to subscribe to the Receive event to detect the exception - _, exception := execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(contractAddr)) // - if exception == "" { - t.Fatal("expected exception") + // one bonder with permission and an input with send should pass + sendAcc := blockCache.GetAccount(user[2].Address) + sendAcc.Permissions.Base.Set(ptypes.Send, true) + blockCache.UpdateAccount(sendAcc) + tx, _ = types.NewBondTx(user[1].PubKey) + if err := tx.AddInput(blockCache, user[2].PubKey, 5); err != nil { + t.Fatal(err) + } + tx.AddOutput(user[1].Address, 5) + tx.SignInput(0, user[2]) + tx.SignBond(user[1]) + if err := ExecTx(blockCache, tx, true, nil); err != nil { + t.Fatal("Unexpected error", err) } + // reset state (we can only bond with an account once ..) + genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) + st = MakeGenesisState(stateDB, &genDoc) + blockCache = NewBlockCache(st) + bondAcc = blockCache.GetAccount(user[1].Address) + bondAcc.Permissions.Base.Set(ptypes.Bond, true) + blockCache.UpdateAccount(bondAcc) //------------------------------ - // call the contract (should PASS) - fmt.Println("###### CALL THE FACTORY (PASS)") - - contractAcc.Permissions.Base.Set(ptypes.Create, true) - blockCache.UpdateAccount(contractAcc) - - // A single input, having the permission, should succeed - tx, _ = NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 100, 100) - SignCallTx(tx, user[0]) - // we need to subscribe to the Receive event to detect the exception - _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(contractAddr)) // - if exception != "" { - t.Fatal("unexpected exception", exception) + // one bonder with permission and an input with bond should pass + sendAcc.Permissions.Base.Set(ptypes.Bond, true) + blockCache.UpdateAccount(sendAcc) + tx, _ = types.NewBondTx(user[1].PubKey) + if err := tx.AddInput(blockCache, user[2].PubKey, 5); err != nil { + t.Fatal(err) } - - //-------------------------------- - fmt.Println("##### CALL to empty address") - zeroAddr := LeftPadBytes([]byte{}, 20) - code := callContractCode(zeroAddr) - - contractAddr = NewContractAddress(user[0].Address, 110) - contractAcc = &account.Account{ - Address: contractAddr, - Balance: 1000, - Code: code, - Sequence: 0, - StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + tx.AddOutput(user[1].Address, 5) + tx.SignInput(0, user[2]) + tx.SignBond(user[1]) + if err := ExecTx(blockCache, tx, true, nil); err != nil { + t.Fatal("Unexpected error", err) } - contractAcc.Permissions.Base.Set(ptypes.Call, true) - contractAcc.Permissions.Base.Set(ptypes.Create, true) - blockCache.UpdateAccount(contractAcc) - // this should call the 0 address but not create ... - tx, _ = NewCallTx(blockCache, user[0].PubKey, contractAddr, createCode, 100, 10000, 100) - SignCallTx(tx, user[0]) - // we need to subscribe to the Receive event to detect the exception - _, exception = execTxWaitEvent(t, blockCache, tx, types.EventStringAccReceive(zeroAddr)) // - if exception != "" { - t.Fatal("unexpected exception", exception) + // reset state (we can only bond with an account once ..) + genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) + st = MakeGenesisState(stateDB, &genDoc) + blockCache = NewBlockCache(st) + bondAcc = blockCache.GetAccount(user[1].Address) + bondAcc.Permissions.Base.Set(ptypes.Bond, true) + blockCache.UpdateAccount(bondAcc) + //------------------------------ + // one bonder with permission and an input from that bonder and an input without send or bond should fail + tx, _ = types.NewBondTx(user[1].PubKey) + if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil { + t.Fatal(err) } - zeroAcc := blockCache.GetAccount(zeroAddr) - if len(zeroAcc.Code) != 0 { - t.Fatal("the zero account was given code from a CALL!") + if err := tx.AddInput(blockCache, user[2].PubKey, 5); err != nil { + t.Fatal(err) + } + tx.AddOutput(user[1].Address, 5) + tx.SignInput(0, user[1]) + tx.SignInput(1, user[2]) + tx.SignBond(user[1]) + if err := ExecTx(blockCache, tx, true, nil); err == nil { + t.Fatal("Expected error") } - } //------------------------------------------------------------------------------------- diff --git a/vm/snative.go b/vm/snative.go index a29f26e3d..0d9d02503 100644 --- a/vm/snative.go +++ b/vm/snative.go @@ -7,7 +7,35 @@ import ( ptypes "github.com/tendermint/tendermint/permission/types" ) -type SNativeContract func(input []byte) (output []byte, err error) +// Checks if a permission flag is valid (a known base chain or snative permission) +func ValidPermN(n ptypes.PermFlag) bool { + if n > ptypes.TopBasePermission && n < FirstSNativePerm { + return false + } else if n > TopSNativePermission { + return false + } + return true +} + +const ( + // first 32 bits of BasePermission are for chain, second 32 are for snative + FirstSNativePerm ptypes.PermFlag = 1 << 32 + + HasBasePerm ptypes.PermFlag = FirstSNativePerm << iota + SetBasePerm + UnsetBasePerm + SetGlobalPerm + ClearBasePerm + HasRole + AddRole + RmRole + + // XXX: must be adjusted if snative's added/removed + NumSNativePermissions uint = 8 + TopSNativePermission ptypes.PermFlag = FirstSNativePerm << (NumSNativePermissions - 1) +) + +type SNativeContract func(acc *Account, input []byte) (output []byte, err error) //----------------------------------------------------------------------------- // snative are native contracts that can access and manipulate the chain state @@ -15,7 +43,10 @@ type SNativeContract func(input []byte) (output []byte, err error) // TODO: catch errors, log em, return 0s to the vm (should some errors cause exceptions though?) -func (vm *VM) hasBasePerm(args []byte) (output []byte, err error) { +func (vm *VM) hasBasePerm(acc *Account, args []byte) (output []byte, err error) { + if !vm.HasPermission(acc, HasBasePerm) { + return nil, fmt.Errorf("acc %X does not have permission to call snative.HasBasePerm") + } if len(args) != 2*32 { return nil, fmt.Errorf("hasBasePerm() takes two arguments (address, permission number)") } @@ -26,7 +57,10 @@ func (vm *VM) hasBasePerm(args []byte) (output []byte, err error) { if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } - permN := ptypes.PermFlag(Uint64FromWord256(permNum)) + permN := ptypes.PermFlag(Uint64FromWord256(permNum)) // already shifted + if !ValidPermN(permN) { + return nil, ptypes.ErrInvalidPermission(permN) + } var permInt byte if vm.HasPermission(vmAcc, permN) { permInt = 0x1 @@ -36,7 +70,10 @@ func (vm *VM) hasBasePerm(args []byte) (output []byte, err error) { return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func (vm *VM) setBasePerm(args []byte) (output []byte, err error) { +func (vm *VM) setBasePerm(acc *Account, args []byte) (output []byte, err error) { + if !vm.HasPermission(acc, SetBasePerm) { + return nil, fmt.Errorf("acc %X does not have permission to call snative.SetBasePerm") + } if len(args) != 3*32 { return nil, fmt.Errorf("setBasePerm() takes three arguments (address, permission number, permission value)") } @@ -49,6 +86,9 @@ func (vm *VM) setBasePerm(args []byte) (output []byte, err error) { return nil, fmt.Errorf("Unknown account %X", addr) } permN := ptypes.PermFlag(Uint64FromWord256(permNum)) + if !ValidPermN(permN) { + return nil, ptypes.ErrInvalidPermission(permN) + } permV := !perm.IsZero() if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { return nil, err @@ -57,7 +97,10 @@ func (vm *VM) setBasePerm(args []byte) (output []byte, err error) { return perm.Bytes(), nil } -func (vm *VM) unsetBasePerm(args []byte) (output []byte, err error) { +func (vm *VM) unsetBasePerm(acc *Account, args []byte) (output []byte, err error) { + if !vm.HasPermission(acc, UnsetBasePerm) { + return nil, fmt.Errorf("acc %X does not have permission to call snative.UnsetBasePerm") + } if len(args) != 2*32 { return nil, fmt.Errorf("unsetBasePerm() takes two arguments (address, permission number)") } @@ -69,6 +112,9 @@ func (vm *VM) unsetBasePerm(args []byte) (output []byte, err error) { return nil, fmt.Errorf("Unknown account %X", addr) } permN := ptypes.PermFlag(Uint64FromWord256(permNum)) + if !ValidPermN(permN) { + return nil, ptypes.ErrInvalidPermission(permN) + } if err = vmAcc.Permissions.Base.Unset(permN); err != nil { return nil, err } @@ -76,7 +122,7 @@ func (vm *VM) unsetBasePerm(args []byte) (output []byte, err error) { return permNum.Bytes(), nil } -func (vm *VM) setGlobalPerm(args []byte) (output []byte, err error) { +func (vm *VM) setGlobalPerm(acc *Account, args []byte) (output []byte, err error) { if len(args) != 2*32 { return nil, fmt.Errorf("setGlobalPerm() takes three arguments (permission number, permission value)") } @@ -88,6 +134,9 @@ func (vm *VM) setGlobalPerm(args []byte) (output []byte, err error) { panic("cant find the global permissions account") } permN := ptypes.PermFlag(Uint64FromWord256(permNum)) + if !ValidPermN(permN) { + return nil, ptypes.ErrInvalidPermission(permN) + } permV := !perm.IsZero() if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { return nil, err @@ -97,11 +146,17 @@ func (vm *VM) setGlobalPerm(args []byte) (output []byte, err error) { } // TODO: needs access to an iterator ... -func (vm *VM) clearPerm(args []byte) (output []byte, err error) { +func (vm *VM) clearPerm(acc *Account, args []byte) (output []byte, err error) { + if !vm.HasPermission(acc, ClearBasePerm) { + return nil, fmt.Errorf("acc %X does not have permission to call snative.ClearBasePerm") + } return nil, nil } -func (vm *VM) hasRole(args []byte) (output []byte, err error) { +func (vm *VM) hasRole(acc *Account, args []byte) (output []byte, err error) { + if !vm.HasPermission(acc, HasRole) { + return nil, fmt.Errorf("acc %X does not have permission to call snative.HasRole") + } if len(args) != 2*32 { return nil, fmt.Errorf("hasRole() takes two arguments (address, role)") } @@ -122,7 +177,10 @@ func (vm *VM) hasRole(args []byte) (output []byte, err error) { return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func (vm *VM) addRole(args []byte) (output []byte, err error) { +func (vm *VM) addRole(acc *Account, args []byte) (output []byte, err error) { + if !vm.HasPermission(acc, AddRole) { + return nil, fmt.Errorf("acc %X does not have permission to call snative.AddRole") + } if len(args) != 2*32 { return nil, fmt.Errorf("addRole() takes two arguments (address, role)") } @@ -143,7 +201,10 @@ func (vm *VM) addRole(args []byte) (output []byte, err error) { return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func (vm *VM) rmRole(args []byte) (output []byte, err error) { +func (vm *VM) rmRole(acc *Account, args []byte) (output []byte, err error) { + if !vm.HasPermission(acc, RmRole) { + return nil, fmt.Errorf("acc %X does not have permission to call snative.RmRole") + } if len(args) != 2*32 { return nil, fmt.Errorf("rmRole() takes two arguments (address, role)") } diff --git a/vm/vm.go b/vm/vm.go index 375e28496..c28760c7b 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -700,7 +700,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas dbg.Printf(" => %v\n", log) case CREATE: // 0xF0 - if vm.perms && !vm.HasPermission(callee, ptypes.Create) { + if vm.perms && !vm.HasPermission(callee, ptypes.CreateContract) { return nil, ErrPermission{"create"} } contractValue := stack.Pop64() @@ -760,7 +760,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas } else if snativeContract := vm.snativeContracts[addr]; vm.doug && snativeContract != nil { // This is Doug and we're calling a snative contract // TODO: Doug contract should have all permissions - ret, err = snativeContract(args) + ret, err = snativeContract(callee, args) } else { // EVM contract if ok = useGas(gas, GasGetAccount); !ok {