mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 06:42:16 +00:00
bond perm and tests
This commit is contained in:
parent
8cdb0b91e6
commit
2ec3d0611f
@ -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<<NumBasePermissions - 1
|
||||
)
|
||||
@ -51,9 +52,6 @@ func NewBasePermissions() *BasePermissions {
|
||||
// ErrValueNotSet is returned if the permission's set bit is off,
|
||||
// and should be caught by caller so the global permission can be fetched
|
||||
func (p *BasePermissions) Get(ty PermFlag) (bool, error) {
|
||||
if ty > 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
|
||||
}
|
||||
|
@ -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,24 +818,24 @@ 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) {
|
||||
if !HasPermission(state, acc, ptypes.Send) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,7 +384,7 @@ 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.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])
|
||||
// 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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
//------------------------------
|
||||
// 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)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
if bytes.Compare(contractAcc.Code, contractCode) != 0 {
|
||||
t.Fatalf("contract does not have correct code. Got %X, expected %X", contractAcc.Code, contractCode)
|
||||
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)
|
||||
//------------------------------
|
||||
// create contract that uses the CREATE op
|
||||
fmt.Println("##### CREATE FACTORY")
|
||||
// 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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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])
|
||||
// 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 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("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)
|
||||
}
|
||||
if bytes.Compare(contractAcc.Code, factoryCode) != 0 {
|
||||
t.Fatalf("contract does not have correct code. Got %X, expected %X", contractAcc.Code, factoryCode)
|
||||
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 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 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)
|
||||
}
|
||||
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 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)
|
||||
}
|
||||
|
||||
//--------------------------------
|
||||
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(),
|
||||
if err := tx.AddInput(blockCache, user[2].PubKey, 5); err != nil {
|
||||
t.Fatal(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)
|
||||
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")
|
||||
}
|
||||
zeroAcc := blockCache.GetAccount(zeroAddr)
|
||||
if len(zeroAcc.Code) != 0 {
|
||||
t.Fatal("the zero account was given code from a CALL!")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
@ -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)")
|
||||
}
|
||||
|
4
vm/vm.go
4
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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user