mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 06:42:16 +00:00
CreateAccount permission through SendTx and CALL (to unknown accounts)
This commit is contained in:
parent
d78a39ade3
commit
f75b6aff74
@ -16,6 +16,7 @@ func GetAccount(address []byte) (*acm.Account, error) {
|
|||||||
cache := mempoolReactor.Mempool.GetCache()
|
cache := mempoolReactor.Mempool.GetCache()
|
||||||
account := cache.GetAccount(address)
|
account := cache.GetAccount(address)
|
||||||
if account == nil {
|
if account == nil {
|
||||||
|
// XXX: shouldn't we return "account not found"?
|
||||||
account = &acm.Account{
|
account = &acm.Account{
|
||||||
Address: address,
|
Address: address,
|
||||||
PubKey: nil,
|
PubKey: nil,
|
||||||
|
@ -165,6 +165,7 @@ func getOrMakeOutputs(state AccountGetter, accounts map[string]*account.Account,
|
|||||||
accounts = make(map[string]*account.Account)
|
accounts = make(map[string]*account.Account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var checkedCreatePerms bool
|
||||||
for _, out := range outs {
|
for _, out := range outs {
|
||||||
// Account shouldn't be duplicated
|
// Account shouldn't be duplicated
|
||||||
if _, ok := accounts[string(out.Address)]; ok {
|
if _, ok := accounts[string(out.Address)]; ok {
|
||||||
@ -173,6 +174,12 @@ func getOrMakeOutputs(state AccountGetter, accounts map[string]*account.Account,
|
|||||||
acc := state.GetAccount(out.Address)
|
acc := state.GetAccount(out.Address)
|
||||||
// output account may be nil (new)
|
// output account may be nil (new)
|
||||||
if acc == nil {
|
if acc == nil {
|
||||||
|
if !checkedCreatePerms {
|
||||||
|
if !hasCreateAccountPermission(state, accounts) {
|
||||||
|
return nil, fmt.Errorf("At least one input does not have permission to create accounts")
|
||||||
|
}
|
||||||
|
checkedCreatePerms = true
|
||||||
|
}
|
||||||
acc = &account.Account{
|
acc = &account.Account{
|
||||||
Address: out.Address,
|
Address: out.Address,
|
||||||
PubKey: nil,
|
PubKey: nil,
|
||||||
@ -312,6 +319,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add outputs to accounts map
|
// add outputs to accounts map
|
||||||
|
// if any outputs don't exist, all inputs must have CreateAccount perm
|
||||||
accounts, err = getOrMakeOutputs(blockCache, accounts, tx.Outputs)
|
accounts, err = getOrMakeOutputs(blockCache, accounts, tx.Outputs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -364,7 +372,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
|
|||||||
|
|
||||||
createAccount := len(tx.Address) == 0
|
createAccount := len(tx.Address) == 0
|
||||||
if createAccount {
|
if createAccount {
|
||||||
if !hasCreatePermission(blockCache, inAcc) {
|
if !hasCreateContractPermission(blockCache, inAcc) {
|
||||||
return fmt.Errorf("Account %X does not have Create permission", tx.Input.Address)
|
return fmt.Errorf("Account %X does not have Create permission", tx.Input.Address)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -634,6 +642,17 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add outputs to accounts map
|
||||||
|
// if any outputs don't exist, all inputs must have CreateAccount perm
|
||||||
|
// though outputs aren't created until unbonding/release time
|
||||||
|
canCreate := hasCreateAccountPermission(blockCache, accounts)
|
||||||
|
for _, out := range tx.UnbondTo {
|
||||||
|
acc := blockCache.GetAccount(out.Address)
|
||||||
|
if acc == nil && !canCreate {
|
||||||
|
return fmt.Errorf("At least one input does not have permission to create accounts")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bondAcc := blockCache.GetAccount(tx.PubKey.Address())
|
bondAcc := blockCache.GetAccount(tx.PubKey.Address())
|
||||||
if !hasBondPermission(blockCache, bondAcc) {
|
if !hasBondPermission(blockCache, bondAcc) {
|
||||||
return fmt.Errorf("The bonder does not have permission to bond")
|
return fmt.Errorf("The bonder does not have permission to bond")
|
||||||
@ -821,10 +840,19 @@ func hasCallPermission(state AccountGetter, acc *account.Account) bool {
|
|||||||
return HasPermission(state, acc, ptypes.Call)
|
return HasPermission(state, acc, ptypes.Call)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasCreatePermission(state AccountGetter, acc *account.Account) bool {
|
func hasCreateContractPermission(state AccountGetter, acc *account.Account) bool {
|
||||||
return HasPermission(state, acc, ptypes.CreateContract)
|
return HasPermission(state, acc, ptypes.CreateContract)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasCreateAccountPermission(state AccountGetter, accs map[string]*account.Account) bool {
|
||||||
|
for _, acc := range accs {
|
||||||
|
if !HasPermission(state, acc, ptypes.CreateAccount) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func hasBondPermission(state AccountGetter, acc *account.Account) bool {
|
func hasBondPermission(state AccountGetter, acc *account.Account) bool {
|
||||||
return HasPermission(state, acc, ptypes.Bond)
|
return HasPermission(state, acc, ptypes.Bond)
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
To Test:
|
Permission Tests:
|
||||||
|
|
||||||
- SendTx:
|
- SendTx:
|
||||||
x - 1 input, no perm, call perm, create perm
|
x - 1 input, no perm, call perm, create perm
|
||||||
@ -46,7 +46,19 @@ x - 1 bonder with perm, input with send
|
|||||||
x - 1 bonder with perm, input with bond
|
x - 1 bonder with perm, input with bond
|
||||||
x - 2 inputs, one with perm one without
|
x - 2 inputs, one with perm one without
|
||||||
|
|
||||||
- SendTx for new account ? CALL for new account?
|
- SendTx for new account
|
||||||
|
x - 1 input, 1 unknown ouput, input with send, not create (fail)
|
||||||
|
x - 1 input, 1 unknown ouput, input with send and create (pass)
|
||||||
|
x - 2 inputs, 1 unknown ouput, both inputs with send, one with create, one without (fail)
|
||||||
|
x - 2 inputs, 1 known output, 1 unknown ouput, one input with create, one without (fail)
|
||||||
|
x - 2 inputs, 1 unknown ouput, both inputs with send, both inputs with create (pass )
|
||||||
|
x - 2 inputs, 1 known output, 1 unknown ouput, both inputs with create, (pass)
|
||||||
|
|
||||||
|
|
||||||
|
- CALL for new account
|
||||||
|
x - unknown output, without create (fail)
|
||||||
|
x - unknown output, with create (pass)
|
||||||
|
|
||||||
|
|
||||||
- Gendoug:
|
- Gendoug:
|
||||||
- base: has,set,unset
|
- base: has,set,unset
|
||||||
@ -55,7 +67,7 @@ x - 2 inputs, one with perm one without
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// keys
|
// keys
|
||||||
var user = makeUsers(5)
|
var user = makeUsers(10)
|
||||||
|
|
||||||
func makeUsers(n int) []*account.PrivAccount {
|
func makeUsers(n int) []*account.PrivAccount {
|
||||||
accounts := []*account.PrivAccount{}
|
accounts := []*account.PrivAccount{}
|
||||||
@ -151,6 +163,22 @@ func TestSendFails(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simple send tx to unknown account without create_account perm should fail
|
||||||
|
acc := blockCache.GetAccount(user[3].Address)
|
||||||
|
acc.Permissions.Base.Set(ptypes.Send, true)
|
||||||
|
blockCache.UpdateAccount(acc)
|
||||||
|
tx = types.NewSendTx()
|
||||||
|
if err := tx.AddInput(blockCache, user[3].PubKey, 5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tx.AddOutput(user[6].Address, 5)
|
||||||
|
tx.SignInput(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) {
|
func TestCallFails(t *testing.T) {
|
||||||
@ -621,6 +649,146 @@ func TestBondPermission(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateAccountPermission(t *testing.T) {
|
||||||
|
stateDB := dbm.GetDB("state")
|
||||||
|
genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse)
|
||||||
|
genDoc.Accounts[0].Permissions.Base.Set(ptypes.Send, true) // give the 0 account permission
|
||||||
|
genDoc.Accounts[1].Permissions.Base.Set(ptypes.Send, true) // give the 0 account permission
|
||||||
|
genDoc.Accounts[0].Permissions.Base.Set(ptypes.CreateAccount, true) // give the 0 account permission
|
||||||
|
st := MakeGenesisState(stateDB, &genDoc)
|
||||||
|
blockCache := NewBlockCache(st)
|
||||||
|
|
||||||
|
//----------------------------------------------------------
|
||||||
|
// SendTx to unknown account
|
||||||
|
|
||||||
|
// A single input, having the permission, should succeed
|
||||||
|
tx := types.NewSendTx()
|
||||||
|
if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tx.AddOutput(user[6].Address, 5)
|
||||||
|
tx.SignInput(0, user[0])
|
||||||
|
if err := ExecTx(blockCache, tx, true, nil); err != nil {
|
||||||
|
t.Fatal("Transaction failed", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two inputs, both with send, one with create, one without, should fail
|
||||||
|
tx = types.NewSendTx()
|
||||||
|
if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tx.AddOutput(user[7].Address, 10)
|
||||||
|
tx.SignInput(0, user[0])
|
||||||
|
tx.SignInput(1, user[1])
|
||||||
|
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||||
|
t.Fatal("Expected error")
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two inputs, both with send, one with create, one without, two ouputs (one known, one unknown) should fail
|
||||||
|
tx = types.NewSendTx()
|
||||||
|
if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tx.AddOutput(user[7].Address, 4)
|
||||||
|
tx.AddOutput(user[4].Address, 6)
|
||||||
|
tx.SignInput(0, user[0])
|
||||||
|
tx.SignInput(1, user[1])
|
||||||
|
if err := ExecTx(blockCache, tx, true, nil); err == nil {
|
||||||
|
t.Fatal("Expected error")
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two inputs, both with send, both with create, should pass
|
||||||
|
acc := blockCache.GetAccount(user[1].Address)
|
||||||
|
acc.Permissions.Base.Set(ptypes.CreateAccount, true)
|
||||||
|
blockCache.UpdateAccount(acc)
|
||||||
|
tx = types.NewSendTx()
|
||||||
|
if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tx.AddOutput(user[7].Address, 10)
|
||||||
|
tx.SignInput(0, user[0])
|
||||||
|
tx.SignInput(1, user[1])
|
||||||
|
if err := ExecTx(blockCache, tx, true, nil); err != nil {
|
||||||
|
t.Fatal("Unexpected error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two inputs, both with send, both with create, two outputs (one known, one unknown) should pass
|
||||||
|
tx = types.NewSendTx()
|
||||||
|
if err := tx.AddInput(blockCache, user[0].PubKey, 5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tx.AddInput(blockCache, user[1].PubKey, 5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tx.AddOutput(user[7].Address, 7)
|
||||||
|
tx.AddOutput(user[4].Address, 3)
|
||||||
|
tx.SignInput(0, user[0])
|
||||||
|
tx.SignInput(1, user[1])
|
||||||
|
if err := ExecTx(blockCache, tx, true, nil); err != nil {
|
||||||
|
t.Fatal("Unexpected error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------
|
||||||
|
// CALL to unknown account
|
||||||
|
|
||||||
|
acc = blockCache.GetAccount(user[0].Address)
|
||||||
|
acc.Permissions.Base.Set(ptypes.Call, true)
|
||||||
|
blockCache.UpdateAccount(acc)
|
||||||
|
|
||||||
|
// call to contract that calls unknown account - without create_account perm
|
||||||
|
// create contract that calls the simple contract
|
||||||
|
contractCode := callContractCode(user[9].Address)
|
||||||
|
caller1ContractAddr := NewContractAddress(user[4].Address, 101)
|
||||||
|
caller1Acc := &account.Account{
|
||||||
|
Address: caller1ContractAddr,
|
||||||
|
Balance: 0,
|
||||||
|
Code: contractCode,
|
||||||
|
Sequence: 0,
|
||||||
|
StorageRoot: Zero256.Bytes(),
|
||||||
|
Permissions: ptypes.NewAccountPermissions(),
|
||||||
|
}
|
||||||
|
blockCache.UpdateAccount(caller1Acc)
|
||||||
|
|
||||||
|
// A single input, having the permission, but the contract doesn't have permission
|
||||||
|
txCall, _ := types.NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100)
|
||||||
|
txCall.Sign(user[0])
|
||||||
|
|
||||||
|
// we need to subscribe to the Receive event to detect the exception
|
||||||
|
_, exception := execTxWaitEvent(t, blockCache, txCall, types.EventStringAccReceive(caller1ContractAddr)) //
|
||||||
|
if exception == "" {
|
||||||
|
t.Fatal("Expected exception")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: for a contract to be able to CreateAccount, it must be able to call
|
||||||
|
// NOTE: for a user to be able to CreateAccount, it must be able to send!
|
||||||
|
caller1Acc.Permissions.Base.Set(ptypes.CreateAccount, true)
|
||||||
|
caller1Acc.Permissions.Base.Set(ptypes.Call, true)
|
||||||
|
blockCache.UpdateAccount(caller1Acc)
|
||||||
|
// A single input, having the permission, but the contract doesn't have permission
|
||||||
|
txCall, _ = types.NewCallTx(blockCache, user[0].PubKey, caller1ContractAddr, nil, 100, 10000, 100)
|
||||||
|
txCall.Sign(user[0])
|
||||||
|
|
||||||
|
// we need to subscribe to the Receive event to detect the exception
|
||||||
|
_, exception = execTxWaitEvent(t, blockCache, txCall, types.EventStringAccReceive(caller1ContractAddr)) //
|
||||||
|
if exception != "" {
|
||||||
|
t.Fatal("Unexpected exception", exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
// helpers
|
// helpers
|
||||||
|
|
||||||
|
5
vm/vm.go
5
vm/vm.go
@ -701,7 +701,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas
|
|||||||
|
|
||||||
case CREATE: // 0xF0
|
case CREATE: // 0xF0
|
||||||
if vm.perms && !vm.HasPermission(callee, ptypes.CreateContract) {
|
if vm.perms && !vm.HasPermission(callee, ptypes.CreateContract) {
|
||||||
return nil, ErrPermission{"create"}
|
return nil, ErrPermission{"create_contract"}
|
||||||
}
|
}
|
||||||
contractValue := stack.Pop64()
|
contractValue := stack.Pop64()
|
||||||
offset, size := stack.Pop64(), stack.Pop64()
|
offset, size := stack.Pop64(), stack.Pop64()
|
||||||
@ -780,6 +780,9 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas
|
|||||||
if acc == nil {
|
if acc == nil {
|
||||||
// if we have not seen the account before, create it
|
// if we have not seen the account before, create it
|
||||||
// so we can send funds
|
// so we can send funds
|
||||||
|
if vm.perms && !vm.HasPermission(caller, ptypes.CreateAccount) {
|
||||||
|
return nil, ErrPermission{"create_account"}
|
||||||
|
}
|
||||||
acc = &Account{
|
acc = &Account{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user