From 7d36d348b51c930b2c399fbebe33a984512b268e Mon Sep 17 00:00:00 2001 From: zhugelianglongming Date: Wed, 22 Feb 2023 22:09:28 +0800 Subject: [PATCH 1/3] Remove redundant DB access for ACL --- kernel/permission/acl/acl.go | 88 +++ kernel/permission/acl/ak_2_account.go | 112 --- kernel/permission/acl/bucket/account.go | 37 + kernel/permission/acl/bucket/ak_2_account.go | 60 ++ .../acl/bucket/ak_2_account_test.go | 204 +++++ kernel/permission/acl/bucket/contract.go | 20 + kernel/permission/acl/contract.go | 147 ++-- kernel/permission/acl/contract_test.go | 709 ++++++++++++++++++ kernel/permission/acl/utils/def.go | 4 +- 9 files changed, 1173 insertions(+), 208 deletions(-) create mode 100644 kernel/permission/acl/acl.go delete mode 100644 kernel/permission/acl/ak_2_account.go create mode 100644 kernel/permission/acl/bucket/account.go create mode 100644 kernel/permission/acl/bucket/ak_2_account.go create mode 100644 kernel/permission/acl/bucket/ak_2_account_test.go create mode 100644 kernel/permission/acl/bucket/contract.go create mode 100644 kernel/permission/acl/contract_test.go diff --git a/kernel/permission/acl/acl.go b/kernel/permission/acl/acl.go new file mode 100644 index 00000000..fd0e2e9e --- /dev/null +++ b/kernel/permission/acl/acl.go @@ -0,0 +1,88 @@ +package acl + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/xuperchain/xupercore/kernel/permission/acl/utils" + pb "github.com/xuperchain/xupercore/protos" +) + +// ACL is a wrapper for pb.ACL, which helps to deal with it +type ACL struct { + pb.Acl +} + +func newACL(data []byte) (*ACL, error) { + acl := new(ACL) + + err := json.Unmarshal(data, &acl.Acl) + if err != nil { + return nil, fmt.Errorf("unmarshal args acl error: %v", err) + } + + if err := acl.check(); err != nil { + return nil, err + } + return acl, nil +} + +// mustGetAKs get AK list when caller make sure no error occure +// usually called after checked ACL +func (l *ACL) mustGetAKs() []string { + aks, _ := l.getAKs() + return aks +} + +// getAKs gets AK list under different permission rule, +// which works for AK2Account bucket +func (l *ACL) getAKs() ([]string, error) { + rule := l.GetPm().GetRule() + switch rule { + case pb.PermissionRule_SIGN_THRESHOLD: + aks := make([]string, 0, len(l.GetAksWeight())) + for ak := range l.GetAksWeight() { + aks = append(aks, ak) + } + return aks, nil + case pb.PermissionRule_SIGN_AKSET: + aks := make([]string, 0) + for _, akSets := range l.GetAkSets().GetSets() { + aks = append(aks, akSets.GetAks()...) + } + return aks, nil + default: + return nil, errors.New("permission rule is invalid") + } +} + +func (l *ACL) check() error { + + // check permission model + if l.GetPm() == nil { + return fmt.Errorf("valid acl failed, lack of argument of permission model") + } + + // check AK limitation + switch l.GetPm().GetRule() { + case pb.PermissionRule_SIGN_THRESHOLD: + aksWeight := l.GetAksWeight() + if aksWeight == nil || len(aksWeight) > utils.GetAkLimit() { + return fmt.Errorf("valid acl failed, aksWeight is nil or size of aksWeight is very big") + } + case pb.PermissionRule_SIGN_AKSET: + akSets := l.GetAkSets() + if akSets == nil { + return fmt.Errorf("valid acl failed, akSets is nil") + } + sets := akSets.GetSets() + if sets == nil || len(sets) > utils.GetAkLimit() { + return fmt.Errorf("valid acl failed, Sets is nil or size of Sets is very big") + } + default: + return fmt.Errorf("valid acl failed, permission model is not found") + } + + return nil +} diff --git a/kernel/permission/acl/ak_2_account.go b/kernel/permission/acl/ak_2_account.go deleted file mode 100644 index b3205458..00000000 --- a/kernel/permission/acl/ak_2_account.go +++ /dev/null @@ -1,112 +0,0 @@ -package acl - -import ( - "encoding/json" - "errors" - - "github.com/xuperchain/xupercore/kernel/contract" - "github.com/xuperchain/xupercore/kernel/permission/acl/utils" - pb "github.com/xuperchain/xupercore/protos" -) - -func updateThresholdWithDel(ctx contract.KContext, aksWeight map[string]float64, accountName string) error { - for address := range aksWeight { - key := utils.MakeAK2AccountKey(address, accountName) - err := ctx.Del(utils.GetAK2AccountBucket(), []byte(key)) - if err != nil { - return err - } - } - return nil -} - -func updateThresholdWithPut(ctx contract.KContext, aksWeight map[string]float64, accountName string) error { - for address := range aksWeight { - key := utils.MakeAK2AccountKey(address, accountName) - err := ctx.Put(utils.GetAK2AccountBucket(), []byte(key), []byte("true")) - if err != nil { - return err - } - } - return nil -} - -func updateAkSetWithDel(ctx contract.KContext, sets map[string]*pb.AkSet, accountName string) error { - for _, akSets := range sets { - for _, ak := range akSets.GetAks() { - key := utils.MakeAK2AccountKey(ak, accountName) - err := ctx.Del(utils.GetAK2AccountBucket(), []byte(key)) - if err != nil { - return err - } - } - } - return nil -} - -func updateAkSetWithPut(ctx contract.KContext, sets map[string]*pb.AkSet, accountName string) error { - for _, akSets := range sets { - for _, ak := range akSets.GetAks() { - key := utils.MakeAK2AccountKey(ak, accountName) - err := ctx.Put(utils.GetAK2AccountBucket(), []byte(key), []byte("true")) - if err != nil { - return err - } - } - } - return nil -} - -func updateForThreshold(ctx contract.KContext, aksWeight map[string]float64, accountName string, method string) error { - switch method { - case "Del": - return updateThresholdWithDel(ctx, aksWeight, accountName) - case "Put": - return updateThresholdWithPut(ctx, aksWeight, accountName) - default: - return errors.New("unexpected error, method only for Del or Put") - } -} - -func updateForAKSet(ctx contract.KContext, akSets *pb.AkSets, accountName string, method string) error { - sets := akSets.GetSets() - switch method { - case "Del": - return updateAkSetWithDel(ctx, sets, accountName) - case "Put": - return updateAkSetWithPut(ctx, sets, accountName) - default: - return errors.New("unexpected error, method only for Del or Put") - } -} - -func update(ctx contract.KContext, aclJSON []byte, accountName string, method string) error { - if aclJSON == nil { - return nil - } - acl := &pb.Acl{} - json.Unmarshal(aclJSON, acl) - akSets := acl.GetAkSets() - aksWeight := acl.GetAksWeight() - permissionRule := acl.GetPm().GetRule() - - switch permissionRule { - case pb.PermissionRule_SIGN_THRESHOLD: - return updateForThreshold(ctx, aksWeight, accountName, method) - case pb.PermissionRule_SIGN_AKSET: - return updateForAKSet(ctx, akSets, accountName, method) - default: - return errors.New("update ak to account reflection failed, permission model is not found") - } - return nil -} - -func UpdateAK2AccountReflection(ctx contract.KContext, aclOldJSON []byte, aclNewJSON []byte, accountName string) error { - if err := update(ctx, aclOldJSON, accountName, "Del"); err != nil { - return err - } - if err := update(ctx, aclNewJSON, accountName, "Put"); err != nil { - return err - } - return nil -} diff --git a/kernel/permission/acl/bucket/account.go b/kernel/permission/acl/bucket/account.go new file mode 100644 index 00000000..713c0ac1 --- /dev/null +++ b/kernel/permission/acl/bucket/account.go @@ -0,0 +1,37 @@ +package bucket + +import ( + "github.com/xuperchain/xupercore/kernel/contract" + "github.com/xuperchain/xupercore/kernel/contract/sandbox" + "github.com/xuperchain/xupercore/kernel/permission/acl/utils" +) + +// AccountBucket can access DB bucket used for account +type AccountBucket struct { + DB contract.XMState +} + +// IsExist returns account existence in bucket +func (b *AccountBucket) IsExist(account string) (bool, error) { + acl, err := b.GetAccountACL(account) + if err != nil { + if err == sandbox.ErrNotFound { + return false, nil + } + return false, err + } + return acl != nil, nil +} + +// GetAccountACL gets ACL by account name +func (b *AccountBucket) GetAccountACL(account string) ([]byte, error) { + return b.DB.Get(utils.GetAccountBucket(), []byte(account)) +} + +// SetAccountACL stores account's ACL in DB with: +// +// key: / +// value: ACL +func (b *AccountBucket) SetAccountACL(account string, acl []byte) error { + return b.DB.Put(utils.GetAccountBucket(), []byte(account), acl) +} diff --git a/kernel/permission/acl/bucket/ak_2_account.go b/kernel/permission/acl/bucket/ak_2_account.go new file mode 100644 index 00000000..47aec08b --- /dev/null +++ b/kernel/permission/acl/bucket/ak_2_account.go @@ -0,0 +1,60 @@ +package bucket + +import ( + "github.com/xuperchain/xupercore/kernel/contract" + "github.com/xuperchain/xupercore/kernel/permission/acl/utils" +) + +// AK2AccountBucket can access DB bucket used for AK -> Account reflection +type AK2AccountBucket struct { + DB contract.XMState +} + +// UpdateForAccount update bucket for an account about its related AK list +// duplication is allowed for input AK list. +func (b *AK2AccountBucket) UpdateForAccount(account string, oldAKs, newAKs []string) error { + + // deduplicate AK + deleteAKs, insertAKs := make(map[string]bool), make(map[string]bool) + for _, ak := range oldAKs { + deleteAKs[ak] = true + } + for _, ak := range newAKs { + insertAKs[ak] = true + } + + for _, ak := range oldAKs { + // remove unchanged AK, which to be inserted after deleted + if deleteAKs[ak] && insertAKs[ak] { + delete(deleteAKs, ak) + delete(insertAKs, ak) + } + } + + for ak := range deleteAKs { + if err := b.delete(ak, account); err != nil { + return err + } + } + for ak := range insertAKs { + if err := b.add(ak, account); err != nil { + return err + } + } + return nil +} + +// delete removes AK -> Account info from DB +func (b *AK2AccountBucket) delete(ak string, account string) error { + key := utils.MakeAK2AccountKey(ak, account) + return b.DB.Del(utils.GetAK2AccountBucket(), []byte(key)) +} + +// add stores AK -> Account info in DB with: +// +// key: / +// value: useless yet +func (b *AK2AccountBucket) add(ak string, account string) error { + key := utils.MakeAK2AccountKey(ak, account) + return b.DB.Put(utils.GetAK2AccountBucket(), []byte(key), []byte("true")) +} diff --git a/kernel/permission/acl/bucket/ak_2_account_test.go b/kernel/permission/acl/bucket/ak_2_account_test.go new file mode 100644 index 00000000..7ae568d4 --- /dev/null +++ b/kernel/permission/acl/bucket/ak_2_account_test.go @@ -0,0 +1,204 @@ +package bucket + +import ( + "errors" + "fmt" + "math/big" + "reflect" + "strings" + "testing" + + "github.com/xuperchain/xupercore/kernel/contract" + "github.com/xuperchain/xupercore/kernel/permission/acl/utils" + pb "github.com/xuperchain/xupercore/protos" +) + +var ( + bucketValue = []byte("true") + testAccount = "XC1111111111111111@xuper" +) + +type bucketData = map[string][]byte + +func TestAK2AccountBucket_UpdateForAccount(t *testing.T) { + type fields struct { + DB contract.XMState + } + type args struct { + account string + oldAKs []string + newAKs []string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + want bucketData + }{ + + { + name: "update with overlap", + fields: fields{ + DB: &mockContext{ + Data: bucketData{ + utils.MakeAK2AccountKey("AK1", testAccount): bucketValue, + utils.MakeAK2AccountKey("AK2", testAccount): bucketValue, + }, + }, + }, + args: args{ + account: testAccount, + oldAKs: []string{"AK1", "AK2"}, + newAKs: []string{"AK2", "AK3"}, + }, + want: bucketData{ + utils.MakeAK2AccountKey("AK2", testAccount): bucketValue, + utils.MakeAK2AccountKey("AK3", testAccount): bucketValue, + }, + }, + { + name: "add", + fields: fields{ + DB: &mockContext{Data: bucketData{}}, + }, + args: args{ + account: testAccount, + oldAKs: nil, + newAKs: []string{"AK1", "AK2"}, + }, + want: bucketData{ + utils.MakeAK2AccountKey("AK1", testAccount): bucketValue, + utils.MakeAK2AccountKey("AK2", testAccount): bucketValue, + }, + }, + { + name: "delete error", + fields: fields{ + DB: &mockContext{Data: bucketData{}}, + }, + args: args{ + account: testAccount, + oldAKs: []string{"AK_delete_error"}, + newAKs: []string{"AK1", "AK2"}, + }, + wantErr: true, + want: bucketData{}, + }, + { + name: "put error", + fields: fields{ + DB: &mockContext{Data: bucketData{}}, + }, + args: args{ + account: testAccount, + oldAKs: nil, + newAKs: []string{"AK1", "AK_put_error"}, + }, + wantErr: true, + want: bucketData{ + utils.MakeAK2AccountKey("AK1", testAccount): bucketValue, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := &AK2AccountBucket{ + DB: tt.fields.DB, + } + if err := b.UpdateForAccount(tt.args.account, tt.args.oldAKs, tt.args.newAKs); (err != nil) != tt.wantErr { + t.Errorf("AK2AccountBucket.UpdateForAccount() error = %v, wantErr %v", err, tt.wantErr) + return + } + got := tt.fields.DB.(*mockContext).Data + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("AccountBucket.UpdateForAccount(), DB = %v, want %v", got, tt.want) + } + }) + } +} + +type mockContext struct { + Data bucketData +} + +func (m mockContext) Args() map[string][]byte { + panic("implement me") +} + +func (m mockContext) Initiator() string { + panic("implement me") +} + +func (m mockContext) Caller() string { + panic("implement me") +} + +func (m mockContext) AuthRequire() []string { + panic("implement me") +} + +func (m mockContext) Get(bucket string, key []byte) ([]byte, error) { + panic("implement me") +} + +func (m mockContext) Select(bucket string, startKey []byte, endKey []byte) (contract.Iterator, error) { + panic("implement me") +} + +func (m mockContext) Put(bucket string, key, value []byte) error { + k := string(key) + if strings.HasPrefix(k, "AK_put_error") { + return errors.New(k) + } + m.Data[string(key)] = value + fmt.Println("Put: ", "bucket", bucket, "key", k, "value", string(value)) + return nil +} + +func (m mockContext) Del(bucket string, key []byte) error { + k := string(key) + if strings.HasPrefix(k, "AK_delete_error") { + return errors.New(k) + } + delete(m.Data, k) + fmt.Println("Delete: ", "bucket", bucket, "key", string(key)) + return nil +} + +func (m mockContext) Transfer(from string, to string, amount *big.Int) error { + panic("implement me") +} + +func (m mockContext) AddEvent(events ...*pb.ContractEvent) { + panic("implement me") +} + +func (m mockContext) Flush() error { + panic("implement me") +} + +func (m mockContext) RWSet() *contract.RWSet { + panic("implement me") +} + +func (m mockContext) UTXORWSet() *contract.UTXORWSet { + panic("implement me") +} + +func (m mockContext) AddResourceUsed(delta contract.Limits) { + panic("implement me") +} + +func (m mockContext) ResourceLimit() contract.Limits { + panic("implement me") +} + +// used for test verification +func (m mockContext) Call(module, contract, method string, args map[string][]byte) (*contract.Response, error) { + panic("implement me") +} + +func (m mockContext) EmitAsyncTask(event string, args interface{}) error { + panic("implement me") +} diff --git a/kernel/permission/acl/bucket/contract.go b/kernel/permission/acl/bucket/contract.go new file mode 100644 index 00000000..58cb7736 --- /dev/null +++ b/kernel/permission/acl/bucket/contract.go @@ -0,0 +1,20 @@ +package bucket + +import ( + "github.com/xuperchain/xupercore/kernel/contract" + "github.com/xuperchain/xupercore/kernel/permission/acl/utils" +) + +// ContractBucket can access DB bucket used for contract +type ContractBucket struct { + DB contract.XMState +} + +// SetMethodACL stores contract method's ACL in DB with: +// +// key: / +// value: ACL +func (b *ContractBucket) SetMethodACL(contract, method string, acl []byte) error { + key := utils.MakeContractMethodKey(contract, method) + return b.DB.Put(utils.GetContractBucket(), []byte(key), acl) +} diff --git a/kernel/permission/acl/contract.go b/kernel/permission/acl/contract.go index a38b4596..b59693f7 100644 --- a/kernel/permission/acl/contract.go +++ b/kernel/permission/acl/contract.go @@ -1,13 +1,11 @@ package acl import ( - "encoding/json" "fmt" "github.com/xuperchain/xupercore/kernel/contract" - "github.com/xuperchain/xupercore/kernel/contract/sandbox" + "github.com/xuperchain/xupercore/kernel/permission/acl/bucket" "github.com/xuperchain/xupercore/kernel/permission/acl/utils" - pb "github.com/xuperchain/xupercore/protos" ) type KernMethod struct { @@ -24,52 +22,49 @@ func NewKernContractMethod(bcName string, NewAccountResourceAmount int64) *KernM } func (t *KernMethod) NewAccount(ctx contract.KContext) (*contract.Response, error) { + + // check fee if ctx.ResourceLimit().XFee < t.NewAccountResourceAmount { return nil, fmt.Errorf("gas not enough, expect no less than %d", t.NewAccountResourceAmount) } + + // check ACK args := ctx.Args() - // json -> pb.Acl - accountName := args["account_name"] aclJSON := args["acl"] - aclBuf := &pb.Acl{} - err := json.Unmarshal(aclJSON, aclBuf) + acl, err := newACL(aclJSON) if err != nil { - return nil, fmt.Errorf("unmarshal args acl error: %v", err) + return nil, err } - if accountName == nil { - return nil, fmt.Errorf("Invoke NewAccount failed, warn: account name is empty") - } - accountStr := string(accountName) - if validErr := utils.ValidAccountNumber(accountStr); validErr != nil { + // check account name + accountNumber := string(args["account_name"]) + if validErr := utils.ValidAccountNumber(accountNumber); validErr != nil { return nil, validErr } - bcname := t.BcName - if bcname == "" { + bcName := t.BcName + if bcName == "" { return nil, fmt.Errorf("block name is empty") } - accountStr = utils.MakeAccountKey(bcname, accountStr) - - if validErr := validACL(aclBuf); validErr != nil { - return nil, validErr - } + accountName := utils.MakeAccountKey(bcName, accountNumber) - oldAccount, err := ctx.Get(utils.GetAccountBucket(), []byte(accountStr)) - if err != nil && err != sandbox.ErrNotFound { + // create account + accounts := bucket.AccountBucket{DB: ctx} + exist, err := accounts.IsExist(accountName) + if err != nil { return nil, err } - if oldAccount != nil { + if exist { return nil, fmt.Errorf("account already exists: %s", accountName) } - err = ctx.Put(utils.GetAccountBucket(), []byte(accountStr), aclJSON) + err = accounts.SetAccountACL(accountName, aclJSON) if err != nil { return nil, err } // add ak -> account reflection - err = UpdateAK2AccountReflection(ctx, nil, aclJSON, accountStr) - if err != nil { + ak2Account := &bucket.AK2AccountBucket{DB: ctx} + if err := ak2Account.UpdateForAccount(accountName, nil, acl.mustGetAKs()); err != nil { return nil, err } @@ -86,36 +81,41 @@ func (t *KernMethod) NewAccount(ctx contract.KContext) (*contract.Response, erro } func (t *KernMethod) SetAccountACL(ctx contract.KContext) (*contract.Response, error) { + + // check fee if ctx.ResourceLimit().XFee < t.NewAccountResourceAmount/1000 { return nil, fmt.Errorf("gas not enough, expect no less than %d", t.NewAccountResourceAmount/1000) } + + // check new ACL args := ctx.Args() - // json -> pb.Acl - accountName := args["account_name"] aclJSON := args["acl"] - aclBuf := &pb.Acl{} - err := json.Unmarshal(aclJSON, aclBuf) + aclNew, err := newACL(aclJSON) if err != nil { - return nil, fmt.Errorf("unmarshal args acl error: %v", err) - } - - if validErr := validACL(aclBuf); validErr != nil { - return nil, validErr + return nil, err } - data, err := ctx.Get(utils.GetAccountBucket(), accountName) + // get old ACL + accountName := string(args["account_name"]) + accounts := bucket.AccountBucket{DB: ctx} + data, err := accounts.GetAccountACL(accountName) if err != nil { return nil, err } - // delete ak -> account reflection - // add ak -> account reflection - aclOldJSON := data - err = UpdateAK2AccountReflection(ctx, aclOldJSON, aclJSON, string(accountName)) + aclOld, err := newACL(data) + if err != nil { + return nil, fmt.Errorf("parse old ACL fail: %s", err) + } + + // update ak -> account reflection + ak2Account := &bucket.AK2AccountBucket{DB: ctx} + err = ak2Account.UpdateForAccount(accountName, aclOld.mustGetAKs(), aclNew.mustGetAKs()) if err != nil { return nil, err } - err = ctx.Put(utils.GetAccountBucket(), accountName, aclJSON) + // update account ACL + err = accounts.SetAccountACL(accountName, aclJSON) if err != nil { return nil, err } @@ -133,31 +133,28 @@ func (t *KernMethod) SetAccountACL(ctx contract.KContext) (*contract.Response, e } func (t *KernMethod) SetMethodACL(ctx contract.KContext) (*contract.Response, error) { + + // check fee if ctx.ResourceLimit().XFee < t.NewAccountResourceAmount/1000 { return nil, fmt.Errorf("gas not enough, expect no less than %d", t.NewAccountResourceAmount/1000) } + + // check args args := ctx.Args() - contractNameBuf := args["contract_name"] - methodNameBuf := args["method_name"] - if contractNameBuf == nil || methodNameBuf == nil { + contractName := string(args["contract_name"]) + methodName := string(args["method_name"]) + if contractName == "" || methodName == "" { return nil, fmt.Errorf("set method acl failed, contract name is nil or method name is nil") } - - // json -> pb.Acl - contractName := string(contractNameBuf) - methodName := string(methodNameBuf) aclJSON := args["acl"] - aclBuf := &pb.Acl{} - err := json.Unmarshal(aclJSON, aclBuf) + _, err := newACL(aclJSON) if err != nil { - return nil, fmt.Errorf("unmarshal args acl error: %v", err) + return nil, err } - if validErr := validACL(aclBuf); validErr != nil { - return nil, validErr - } - key := utils.MakeContractMethodKey(contractName, methodName) - err = ctx.Put(utils.GetContractBucket(), []byte(key), aclJSON) + // update contract method ACL + contracts := bucket.ContractBucket{DB: ctx} + err = contracts.SetMethodACL(contractName, methodName, aclJSON) if err != nil { return nil, err } @@ -173,41 +170,3 @@ func (t *KernMethod) SetMethodACL(ctx contract.KContext) (*contract.Response, er Body: aclJSON, }, nil } - -func validACL(acl *pb.Acl) error { - // param absence check - if acl == nil { - return fmt.Errorf("valid acl failed, arg of acl is nil") - } - - // permission model check - if permissionModel := acl.GetPm(); permissionModel != nil { - permissionRule := permissionModel.GetRule() - akSets := acl.GetAkSets() - aksWeight := acl.GetAksWeight() - if akSets == nil && aksWeight == nil { - return fmt.Errorf("invoke NewAccount failed, permission model is not valid") - } - // aks limitation check - if permissionRule == pb.PermissionRule_SIGN_THRESHOLD { - if aksWeight == nil || len(aksWeight) > utils.GetAkLimit() { - return fmt.Errorf("valid acl failed, aksWeight is nil or size of aksWeight is very big") - } - } else if permissionRule == pb.PermissionRule_SIGN_AKSET { - if akSets != nil { - sets := akSets.GetSets() - if sets == nil || len(sets) > utils.GetAkLimit() { - return fmt.Errorf("valid acl failed, Sets is nil or size of Sets is very big") - } - } else { - return fmt.Errorf("valid acl failed, akSets is nil") - } - } else { - return fmt.Errorf("valid acl failed, permission model is not found") - } - } else { - return fmt.Errorf("valid acl failed, lack of argument of permission model") - } - - return nil -} diff --git a/kernel/permission/acl/contract_test.go b/kernel/permission/acl/contract_test.go new file mode 100644 index 00000000..53402f9d --- /dev/null +++ b/kernel/permission/acl/contract_test.go @@ -0,0 +1,709 @@ +package acl + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "reflect" + "strings" + "testing" + + "github.com/xuperchain/xupercore/kernel/contract" + "github.com/xuperchain/xupercore/kernel/contract/sandbox" + "github.com/xuperchain/xupercore/kernel/permission/acl/utils" + pb "github.com/xuperchain/xupercore/protos" +) + +var ( + testAccountNumber = []byte("1111111111111111") + testBcName = "xuper" + testAccountName = utils.MakeAccountKey(testBcName, string(testAccountNumber)) + valueTrue = []byte("true") +) + +type bucketData map[string][]byte +type argsData map[string][]byte + +type mockContext struct { + resourceLimit contract.Limits + args argsData + + // DB data + accounts bucketData + ak2account bucketData + contract bucketData +} + +func (m mockContext) getDBData() map[string]bucketData { + tmp := map[string]bucketData{ + utils.GetAccountBucket(): m.accounts, + utils.GetAK2AccountBucket(): m.ak2account, + utils.GetContractBucket(): m.contract, + } + data := make(map[string]bucketData) + for key, value := range tmp { + if value != nil { + data[key] = value + } + } + return data +} + +func (m mockContext) Args() map[string][]byte { + return m.args +} + +func (m mockContext) Initiator() string { + panic("implement me") +} + +func (m mockContext) Caller() string { + panic("implement me") +} + +func (m mockContext) AuthRequire() []string { + panic("implement me") +} + +func (m mockContext) Get(bucket string, key []byte) ([]byte, error) { + k := string(key) + if k == "" { + return nil, sandbox.ErrNotFound + } else if k == "XC9999999999999999@xuper" { + return nil, errors.New("DB get error") + } + switch bucket { + case utils.GetAccountBucket(): + return m.accounts[k], nil + default: + return nil, errors.New("unexpected bucket") + } +} + +func (m mockContext) Select(bucket string, startKey []byte, endKey []byte) (contract.Iterator, error) { + panic("implement me") +} + +func (m mockContext) Put(bucket string, key, value []byte) error { + k := string(key) + fmt.Println("Put: ", "bucket", bucket, "key", k, "value", string(value)) + + switch bucket { + case utils.GetAccountBucket(): + if k == "XC9999999999999998@xuper" { + return errors.New("DB put account error") + } + m.accounts[k] = value + case utils.GetAK2AccountBucket(): + if strings.HasSuffix(k, "XC9999999999999997@xuper") { + return errors.New("DB put ak2account error") + } + m.ak2account[k] = value + case utils.GetContractBucket(): + if strings.HasPrefix(k, "contractPutError") { + return errors.New("DB put contract error") + } + m.contract[k] = value + default: + return errors.New("unexpected bucket") + } + return nil +} + +func (m mockContext) Del(bucket string, key []byte) error { + k := string(key) + fmt.Println("Delete: ", "bucket", bucket, "key", k) + + switch bucket { + case utils.GetAK2AccountBucket(): + delete(m.ak2account, k) + default: + return errors.New("unexpected bucket") + } + return nil +} + +func (m mockContext) Transfer(from string, to string, amount *big.Int) error { + panic("implement me") +} + +func (m mockContext) AddEvent(events ...*pb.ContractEvent) { + panic("implement me") +} + +func (m mockContext) Flush() error { + panic("implement me") +} + +func (m mockContext) RWSet() *contract.RWSet { + panic("implement me") +} + +func (m mockContext) UTXORWSet() *contract.UTXORWSet { + panic("implement me") +} + +func (m mockContext) AddResourceUsed(_ contract.Limits) {} + +func (m mockContext) ResourceLimit() contract.Limits { + return m.resourceLimit +} + +func (m mockContext) Call(module, contract, method string, args map[string][]byte) (*contract.Response, error) { + panic("implement me") +} + +func (m mockContext) EmitAsyncTask(event string, args interface{}) error { + panic("implement me") +} + +func mockACL(rule pb.PermissionRule) []byte { + var acl *pb.Acl + switch rule { + case pb.PermissionRule_SIGN_THRESHOLD: + acl = &pb.Acl{ + Pm: &pb.PermissionModel{ + Rule: rule, + }, + AksWeight: map[string]float64{ + "Threshold_AK_1": 0.5, + "Threshold_AK_2": 0.4, + "Overlap_AK": 0.1, + }, + } + case pb.PermissionRule_SIGN_AKSET: + acl = &pb.Acl{ + Pm: &pb.PermissionModel{ + Rule: rule, + }, + AkSets: &pb.AkSets{ + Sets: map[string]*pb.AkSet{ + "set_1": { + Aks: []string{"AkSet_AK_1", "AkSet_AK_2"}, + }, + "set_2": { + Aks: []string{"AkSet_AK_2", "AkSet_AK_3"}, + }, + "set_3": { + Aks: []string{"Overlap_AK"}, + }, + }, + }, + } + } + data, err := json.Marshal(acl) + if err != nil { + return nil + } + return data +} + +//func TestUpdateAK2AccountReflection(t *testing.T) { +// type args struct { +// ctx contract.KContext +// aclOldJSON []byte +// aclNewJSON []byte +// accountName string +// } +// tests := []struct { +// name string +// args args +// wantErr bool +// }{ +// { +// name: "update between different rules", +// args: args{ +// ctx: new(mockContext), +// aclOldJSON: mockACL(pb.PermissionRule_SIGN_THRESHOLD), +// aclNewJSON: mockACL(pb.PermissionRule_SIGN_AKSET), +// accountName: "account", +// }, +// }, +// { +// name: "add", +// args: args{ +// ctx: new(mockContext), +// aclOldJSON: nil, +// aclNewJSON: mockACL(pb.PermissionRule_SIGN_THRESHOLD), +// accountName: "account", +// }, +// }, +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// if err := UpdateAK2AccountReflection(tt.args.ctx, tt.args.aclOldJSON, tt.args.aclNewJSON, tt.args.accountName); (err != nil) != tt.wantErr { +// t.Errorf("UpdateAK2AccountReflection() error = %v, wantErr %v", err, tt.wantErr) +// } +// }) +// } +//} + +//func TestAK2AccountBucket_UpdateForAccount(t *testing.T) { +// type fields struct { +// DB contract.XMState +// } +// type args struct { +// account string +// oldAKs []string +// newAKs []string +// } +// tests := []struct { +// name string +// fields fields +// args args +// wantErr bool +// }{ +// +// { +// name: "update with overlap", +// fields: fields{ +// DB: new(mockContext), +// }, +// args: args{ +// account: "account", +// oldAKs: []string{"ak1", "ak2"}, +// newAKs: []string{"ak2", "ak3"}, +// }, +// }, +// { +// name: "add", +// fields: fields{ +// DB: new(mockContext), +// }, +// args: args{ +// account: "account", +// oldAKs: nil, +// newAKs: []string{"ak1", "ak2"}, +// }, +// }, +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// b := &AK2AccountBucket{ +// DB: tt.fields.DB, +// } +// if err := b.UpdateForAccount(tt.args.account, tt.args.oldAKs, tt.args.newAKs); (err != nil) != tt.wantErr { +// t.Errorf("AK2AccountBucket.UpdateForAccount() error = %v, wantErr %v", err, tt.wantErr) +// } +// }) +// } +//} + +func TestKernMethod_NewAccount(t *testing.T) { + type fields struct { + BcName string + NewAccountResourceAmount int64 + } + type args struct { + ctx contract.KContext + } + tests := []struct { + name string + fields fields + args args + want *contract.Response + wantDB map[string]bucketData + wantErr bool + }{ + { + name: "fee not enough", + fields: fields{NewAccountResourceAmount: 1000}, + args: args{ + mockContext{ + resourceLimit: contract.Limits{XFee: 0}, + }, + }, + wantErr: true, + }, + { + name: "invalid ACL", + args: args{ + mockContext{ + args: argsData{ + "account_name": testAccountNumber, + }, + }, + }, + wantErr: true, + }, + { + name: "invalid account name", + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_THRESHOLD), + }, + }, + }, + wantErr: true, + }, + { + name: "no blockchain name", + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_THRESHOLD), + "account_name": testAccountNumber, + }, + }, + }, + wantErr: true, + }, + { + name: "DB get error", + fields: fields{BcName: testBcName}, + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_THRESHOLD), + "account_name": []byte("9999999999999999"), + }, + }, + }, + wantErr: true, + }, + { + name: "account exist", + fields: fields{BcName: testBcName}, + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_THRESHOLD), + "account_name": testAccountNumber, + }, + accounts: bucketData{ + testAccountName: mockACL(pb.PermissionRule_SIGN_THRESHOLD), + }, + }, + }, + wantErr: true, + }, + { + name: "DB put account error", + fields: fields{BcName: testBcName}, + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_THRESHOLD), + "account_name": []byte("9999999999999998"), + }, + }, + }, + wantErr: true, + }, + { + name: "update ak2account error", + fields: fields{BcName: testBcName}, + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_THRESHOLD), + "account_name": []byte("9999999999999997"), + }, + accounts: bucketData{}, + }, + }, + wantErr: true, + }, + { + name: "create account succeed", + fields: fields{BcName: testBcName}, + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_THRESHOLD), + "account_name": testAccountNumber, + }, + accounts: bucketData{}, + ak2account: bucketData{}, + }, + }, + want: &contract.Response{ + Status: contract.StatusOK, + Message: "success", + Body: mockACL(pb.PermissionRule_SIGN_THRESHOLD), + }, + wantDB: map[string]bucketData{ + utils.GetAccountBucket(): { + testAccountName: mockACL(pb.PermissionRule_SIGN_THRESHOLD), + }, + utils.GetAK2AccountBucket(): { + utils.MakeAK2AccountKey("Threshold_AK_1", testAccountName): valueTrue, + utils.MakeAK2AccountKey("Threshold_AK_2", testAccountName): valueTrue, + utils.MakeAK2AccountKey("Overlap_AK", testAccountName): valueTrue, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := &KernMethod{ + BcName: tt.fields.BcName, + NewAccountResourceAmount: tt.fields.NewAccountResourceAmount, + } + got, err := tr.NewAccount(tt.args.ctx) + t.Logf("err: %s", err) + if (err != nil) != tt.wantErr { + t.Errorf("KernMethod.NewAccount() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("KernMethod.NewAccount() = %v, want %v", got, tt.want) + } + dbData := tt.args.ctx.(mockContext).getDBData() + if !tt.wantErr && !reflect.DeepEqual(dbData, tt.wantDB) { + t.Errorf("KernMethod.NewAccount(), DB = %v, want %v", dbData, tt.wantDB) + } + }) + } +} + +func TestKernMethod_SetAccountACL(t *testing.T) { + type fields struct { + BcName string + NewAccountResourceAmount int64 + } + type args struct { + ctx contract.KContext + } + tests := []struct { + name string + fields fields + args args + want *contract.Response + wantDB map[string]bucketData + wantErr bool + }{ + + { + name: "fee not enough", + fields: fields{NewAccountResourceAmount: 1000}, + args: args{ + mockContext{ + resourceLimit: contract.Limits{XFee: 0}, + }, + }, + wantErr: true, + }, + { + name: "invalid ACL", + args: args{ + mockContext{ + args: argsData{ + "account_name": []byte(testAccountName), + }, + }, + }, + wantErr: true, + }, + { + name: "account name not exist", + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_AKSET), + }, + }, + }, + wantErr: true, + }, + { + name: "DB get error", + fields: fields{BcName: testBcName}, + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_AKSET), + "account_name": []byte("XC9999999999999999@xuper"), + }, + }, + }, + wantErr: true, + }, + { + name: "update ak2account error", + fields: fields{BcName: testBcName}, + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_AKSET), + "account_name": []byte("XC9999999999999997@xuper"), + }, + accounts: bucketData{ + "XC9999999999999997@xuper": mockACL(pb.PermissionRule_SIGN_THRESHOLD), + }, + }, + }, + wantErr: true, + }, + { + name: "update account ACL: threshold -> akSets", + fields: fields{BcName: testBcName}, + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_AKSET), + "account_name": []byte(testAccountName), + }, + accounts: bucketData{ + testAccountName: mockACL(pb.PermissionRule_SIGN_THRESHOLD), + }, + ak2account: bucketData{ + utils.MakeAK2AccountKey("Threshold_AK_1", testAccountName): valueTrue, + utils.MakeAK2AccountKey("Threshold_AK_2", testAccountName): valueTrue, + utils.MakeAK2AccountKey("Overlap_AK", testAccountName): valueTrue, + }, + }, + }, + want: &contract.Response{ + Status: contract.StatusOK, + Message: "success", + Body: mockACL(pb.PermissionRule_SIGN_AKSET), + }, + wantDB: map[string]bucketData{ + utils.GetAccountBucket(): { + testAccountName: mockACL(pb.PermissionRule_SIGN_AKSET), + }, + utils.GetAK2AccountBucket(): { + utils.MakeAK2AccountKey("AkSet_AK_1", testAccountName): valueTrue, + utils.MakeAK2AccountKey("AkSet_AK_2", testAccountName): valueTrue, + utils.MakeAK2AccountKey("AkSet_AK_3", testAccountName): valueTrue, + utils.MakeAK2AccountKey("Overlap_AK", testAccountName): valueTrue, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := &KernMethod{ + BcName: tt.fields.BcName, + NewAccountResourceAmount: tt.fields.NewAccountResourceAmount, + } + got, err := tr.SetAccountACL(tt.args.ctx) + t.Logf("err: %s", err) + if (err != nil) != tt.wantErr { + t.Errorf("KernMethod.SetAccountACL() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("KernMethod.SetAccountACL() = %v, want %v", got, tt.want) + } + dbData := tt.args.ctx.(mockContext).getDBData() + if !tt.wantErr && !reflect.DeepEqual(dbData, tt.wantDB) { + t.Errorf("KernMethod.NewAccount(), DB = %v, want %v", dbData, tt.wantDB) + } + }) + } +} + +func TestKernMethod_SetMethodACL(t *testing.T) { + type fields struct { + BcName string + NewAccountResourceAmount int64 + } + type args struct { + ctx contract.KContext + } + tests := []struct { + name string + fields fields + args args + want *contract.Response + wantDB map[string]bucketData + wantErr bool + }{ + { + name: "fee not enough", + fields: fields{NewAccountResourceAmount: 1000}, + args: args{ + mockContext{ + resourceLimit: contract.Limits{XFee: 0}, + }, + }, + wantErr: true, + }, + { + name: "invalid method", + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_AKSET), + }, + }, + }, + wantErr: true, + }, + { + name: "invalid ACL", + args: args{ + mockContext{ + args: argsData{ + "contract_name": []byte("contract"), + "method_name": []byte("method"), + }, + }, + }, + wantErr: true, + }, + { + name: "update contract error", + fields: fields{BcName: testBcName}, + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_THRESHOLD), + "contract_name": []byte("contractPutError"), + "method_name": []byte("method"), + }, + }, + }, + wantErr: true, + }, + { + name: "update method ACL succeed", + fields: fields{BcName: testBcName}, + args: args{ + mockContext{ + args: argsData{ + "acl": mockACL(pb.PermissionRule_SIGN_THRESHOLD), + "contract_name": []byte("contract"), + "method_name": []byte("method"), + }, + contract: bucketData{}, + }, + }, + want: &contract.Response{ + Status: contract.StatusOK, + Message: "success", + Body: mockACL(pb.PermissionRule_SIGN_THRESHOLD), + }, + wantDB: map[string]bucketData{ + utils.GetContractBucket(): { + utils.MakeContractMethodKey("contract", "method"): mockACL(pb.PermissionRule_SIGN_THRESHOLD), + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := &KernMethod{ + BcName: tt.fields.BcName, + NewAccountResourceAmount: tt.fields.NewAccountResourceAmount, + } + got, err := tr.SetMethodACL(tt.args.ctx) + t.Logf("err: %s", err) + if (err != nil) != tt.wantErr { + t.Errorf("KernMethod.SetMethodACL() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("KernMethod.SetMethodACL() = %v, want %v", got, tt.want) + } + dbData := tt.args.ctx.(mockContext).getDBData() + if !tt.wantErr && !reflect.DeepEqual(dbData, tt.wantDB) { + t.Errorf("KernMethod.NewAccount(), DB = %v, want %v", dbData, tt.wantDB) + } + }) + } +} diff --git a/kernel/permission/acl/utils/def.go b/kernel/permission/acl/utils/def.go index b6e80dde..79d09b7f 100644 --- a/kernel/permission/acl/utils/def.go +++ b/kernel/permission/acl/utils/def.go @@ -43,8 +43,8 @@ func MakeContractMethodKey(contractName string, methodName string) string { } // MakeAccountKey generate account key using blockchain name and account number -func MakeAccountKey(bcname string, accountName string) string { - return accountPrefix + accountName + accountBcnameSep + bcname +func MakeAccountKey(bcName string, number string) string { + return accountPrefix + number + accountBcnameSep + bcName } // MakeAK2AccountKey generate key mixed ak with account as prefix key From bbbec220fc1dba73ce1f6dc50883d9ac9b356c47 Mon Sep 17 00:00:00 2001 From: zhugelianglongming Date: Thu, 23 Feb 2023 11:20:26 +0800 Subject: [PATCH 2/3] Fix random output by random for map --- kernel/permission/acl/bucket/account_test.go | 109 ++++++++++++++ .../acl/bucket/ak_2_account_test.go | 110 +------------- .../acl/bucket/context_mock_test.go | 135 ++++++++++++++++++ kernel/permission/acl/bucket/contract_test.go | 64 +++++++++ kernel/permission/acl/contract_test.go | 91 ------------ 5 files changed, 315 insertions(+), 194 deletions(-) create mode 100644 kernel/permission/acl/bucket/account_test.go create mode 100644 kernel/permission/acl/bucket/context_mock_test.go create mode 100644 kernel/permission/acl/bucket/contract_test.go diff --git a/kernel/permission/acl/bucket/account_test.go b/kernel/permission/acl/bucket/account_test.go new file mode 100644 index 00000000..a7c215ac --- /dev/null +++ b/kernel/permission/acl/bucket/account_test.go @@ -0,0 +1,109 @@ +package bucket + +import ( + "reflect" + "testing" + + "github.com/xuperchain/xupercore/kernel/contract" +) + +func TestAccountBucket_IsExist(t *testing.T) { + type args struct { + account string + } + tests := []struct { + name string + args args + want bool + wantErr bool + }{ + { + name: "not exist", + args: args{ + account: "Account_get_not_found", + }, + want: false, + }, + { + name: "other error", + args: args{ + account: "Account_get_error_other", + }, + wantErr: true, + }, + { + name: "exist", + args: args{ + account: "Account_exist", + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := &AccountBucket{ + DB: mockContext{}, + } + got, err := b.IsExist(tt.args.account) + if (err != nil) != tt.wantErr { + t.Errorf("AccountBucket.IsExist() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("AccountBucket.IsExist() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAccountBucket_SetAccountACL(t *testing.T) { + type fields struct { + DB contract.XMState + } + type args struct { + account string + acl []byte + } + tests := []struct { + name string + fields fields + args args + wantErr bool + want bucketData + }{ + { + name: "success", + args: args{ + account: "Account_succ", + acl: []byte("acl"), + }, + want: bucketData{ + "Account_succ": []byte("acl"), + }, + }, + { + name: "fail", + args: args{ + account: "Account_put_error", + acl: []byte("acl"), + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := &AccountBucket{ + DB: mockContext{ + Account: bucketData{}, + }, + } + if err := b.SetAccountACL(tt.args.account, tt.args.acl); (err != nil) != tt.wantErr { + t.Errorf("AccountBucket.SetAccountACL() error = %v, wantErr %v", err, tt.wantErr) + } + got := b.DB.(mockContext).Account + if !tt.wantErr && !reflect.DeepEqual(got, tt.want){ + t.Errorf("AccountBucket.SetAccountACL(), DB = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/kernel/permission/acl/bucket/ak_2_account_test.go b/kernel/permission/acl/bucket/ak_2_account_test.go index 7ae568d4..baf4ee34 100644 --- a/kernel/permission/acl/bucket/ak_2_account_test.go +++ b/kernel/permission/acl/bucket/ak_2_account_test.go @@ -1,16 +1,11 @@ package bucket import ( - "errors" - "fmt" - "math/big" "reflect" - "strings" "testing" "github.com/xuperchain/xupercore/kernel/contract" "github.com/xuperchain/xupercore/kernel/permission/acl/utils" - pb "github.com/xuperchain/xupercore/protos" ) var ( @@ -18,8 +13,6 @@ var ( testAccount = "XC1111111111111111@xuper" ) -type bucketData = map[string][]byte - func TestAK2AccountBucket_UpdateForAccount(t *testing.T) { type fields struct { DB contract.XMState @@ -41,7 +34,7 @@ func TestAK2AccountBucket_UpdateForAccount(t *testing.T) { name: "update with overlap", fields: fields{ DB: &mockContext{ - Data: bucketData{ + AK2Account: bucketData{ utils.MakeAK2AccountKey("AK1", testAccount): bucketValue, utils.MakeAK2AccountKey("AK2", testAccount): bucketValue, }, @@ -60,7 +53,7 @@ func TestAK2AccountBucket_UpdateForAccount(t *testing.T) { { name: "add", fields: fields{ - DB: &mockContext{Data: bucketData{}}, + DB: &mockContext{AK2Account: bucketData{}}, }, args: args{ account: testAccount, @@ -75,7 +68,7 @@ func TestAK2AccountBucket_UpdateForAccount(t *testing.T) { { name: "delete error", fields: fields{ - DB: &mockContext{Data: bucketData{}}, + DB: &mockContext{AK2Account: bucketData{}}, }, args: args{ account: testAccount, @@ -83,12 +76,11 @@ func TestAK2AccountBucket_UpdateForAccount(t *testing.T) { newAKs: []string{"AK1", "AK2"}, }, wantErr: true, - want: bucketData{}, }, { name: "put error", fields: fields{ - DB: &mockContext{Data: bucketData{}}, + DB: &mockContext{AK2Account: bucketData{}}, }, args: args{ account: testAccount, @@ -96,9 +88,6 @@ func TestAK2AccountBucket_UpdateForAccount(t *testing.T) { newAKs: []string{"AK1", "AK_put_error"}, }, wantErr: true, - want: bucketData{ - utils.MakeAK2AccountKey("AK1", testAccount): bucketValue, - }, }, } for _, tt := range tests { @@ -110,95 +99,10 @@ func TestAK2AccountBucket_UpdateForAccount(t *testing.T) { t.Errorf("AK2AccountBucket.UpdateForAccount() error = %v, wantErr %v", err, tt.wantErr) return } - got := tt.fields.DB.(*mockContext).Data - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("AccountBucket.UpdateForAccount(), DB = %v, want %v", got, tt.want) + got := tt.fields.DB.(*mockContext).AK2Account + if !tt.wantErr && !reflect.DeepEqual(got, tt.want) { + t.Errorf("AK2AccountBucket.UpdateForAccount(), DB = %v, want %v", got, tt.want) } }) } } - -type mockContext struct { - Data bucketData -} - -func (m mockContext) Args() map[string][]byte { - panic("implement me") -} - -func (m mockContext) Initiator() string { - panic("implement me") -} - -func (m mockContext) Caller() string { - panic("implement me") -} - -func (m mockContext) AuthRequire() []string { - panic("implement me") -} - -func (m mockContext) Get(bucket string, key []byte) ([]byte, error) { - panic("implement me") -} - -func (m mockContext) Select(bucket string, startKey []byte, endKey []byte) (contract.Iterator, error) { - panic("implement me") -} - -func (m mockContext) Put(bucket string, key, value []byte) error { - k := string(key) - if strings.HasPrefix(k, "AK_put_error") { - return errors.New(k) - } - m.Data[string(key)] = value - fmt.Println("Put: ", "bucket", bucket, "key", k, "value", string(value)) - return nil -} - -func (m mockContext) Del(bucket string, key []byte) error { - k := string(key) - if strings.HasPrefix(k, "AK_delete_error") { - return errors.New(k) - } - delete(m.Data, k) - fmt.Println("Delete: ", "bucket", bucket, "key", string(key)) - return nil -} - -func (m mockContext) Transfer(from string, to string, amount *big.Int) error { - panic("implement me") -} - -func (m mockContext) AddEvent(events ...*pb.ContractEvent) { - panic("implement me") -} - -func (m mockContext) Flush() error { - panic("implement me") -} - -func (m mockContext) RWSet() *contract.RWSet { - panic("implement me") -} - -func (m mockContext) UTXORWSet() *contract.UTXORWSet { - panic("implement me") -} - -func (m mockContext) AddResourceUsed(delta contract.Limits) { - panic("implement me") -} - -func (m mockContext) ResourceLimit() contract.Limits { - panic("implement me") -} - -// used for test verification -func (m mockContext) Call(module, contract, method string, args map[string][]byte) (*contract.Response, error) { - panic("implement me") -} - -func (m mockContext) EmitAsyncTask(event string, args interface{}) error { - panic("implement me") -} diff --git a/kernel/permission/acl/bucket/context_mock_test.go b/kernel/permission/acl/bucket/context_mock_test.go new file mode 100644 index 00000000..5d4bbc8a --- /dev/null +++ b/kernel/permission/acl/bucket/context_mock_test.go @@ -0,0 +1,135 @@ +package bucket + +import ( + "errors" + "fmt" + "github.com/xuperchain/xupercore/kernel/contract/sandbox" + "github.com/xuperchain/xupercore/kernel/permission/acl/utils" + "math/big" + "strings" + + "github.com/xuperchain/xupercore/kernel/contract" + pb "github.com/xuperchain/xupercore/protos" +) + +type bucketData = map[string][]byte + +type mockContext struct { + AK2Account bucketData + Account bucketData + Contract bucketData +} + +func (m mockContext) Args() map[string][]byte { + panic("implement me") +} + +func (m mockContext) Initiator() string { + panic("implement me") +} + +func (m mockContext) Caller() string { + panic("implement me") +} + +func (m mockContext) AuthRequire() []string { + panic("implement me") +} + +func (m mockContext) Get(bucket string, key []byte) ([]byte, error) { + k := string(key) + switch bucket { + case utils.GetAccountBucket(): + if k == "Account_get_not_found" { + return nil, sandbox.ErrNotFound + } else if k == "Account_get_error_other" { + return nil, errors.New(k) + } + default: + return nil, errors.New("unexpected bucket") + } + fmt.Println("Get: ", "bucket", bucket, "key") + return key, nil +} + +func (m mockContext) Select(bucket string, startKey []byte, endKey []byte) (contract.Iterator, error) { + panic("implement me") +} + +func (m mockContext) Put(bucket string, key, value []byte) error { + k := string(key) + switch bucket { + case utils.GetAK2AccountBucket(): + if strings.HasPrefix(k, "AK_put_error") { + return errors.New(k) + } + m.AK2Account[k] = value + case utils.GetAccountBucket(): + if k == "Account_put_error" { + return errors.New(k) + } + m.Account[k] = value + case utils.GetContractBucket(): + if strings.HasPrefix(k,"Contract_put_error") { + return errors.New(k) + } + m.Contract[k] = value + default: + return errors.New("unexpected bucket") + } + fmt.Println("Put: ", "bucket", bucket, "key", k, "value", string(value)) + return nil +} + +func (m mockContext) Del(bucket string, key []byte) error { + k := string(key) + switch bucket { + case utils.GetAK2AccountBucket(): + + if strings.HasPrefix(k, "AK_delete_error") { + return errors.New(k) + } + delete(m.AK2Account, k) + default: + return errors.New("unexpected bucket") + } + fmt.Println("Delete: ", "bucket", bucket, "key", string(key)) + return nil +} + +func (m mockContext) Transfer(from string, to string, amount *big.Int) error { + panic("implement me") +} + +func (m mockContext) AddEvent(events ...*pb.ContractEvent) { + panic("implement me") +} + +func (m mockContext) Flush() error { + panic("implement me") +} + +func (m mockContext) RWSet() *contract.RWSet { + panic("implement me") +} + +func (m mockContext) UTXORWSet() *contract.UTXORWSet { + panic("implement me") +} + +func (m mockContext) AddResourceUsed(delta contract.Limits) { + panic("implement me") +} + +func (m mockContext) ResourceLimit() contract.Limits { + panic("implement me") +} + +// used for test verification +func (m mockContext) Call(module, contract, method string, args map[string][]byte) (*contract.Response, error) { + panic("implement me") +} + +func (m mockContext) EmitAsyncTask(event string, args interface{}) error { + panic("implement me") +} diff --git a/kernel/permission/acl/bucket/contract_test.go b/kernel/permission/acl/bucket/contract_test.go new file mode 100644 index 00000000..412f425b --- /dev/null +++ b/kernel/permission/acl/bucket/contract_test.go @@ -0,0 +1,64 @@ +package bucket + +import ( + "github.com/xuperchain/xupercore/kernel/permission/acl/utils" + "reflect" + "testing" + + "github.com/xuperchain/xupercore/kernel/contract" +) + +func TestContractBucket_SetMethodACL(t *testing.T) { + type fields struct { + DB contract.XMState + } + type args struct { + contract string + method string + acl []byte + } + tests := []struct { + name string + fields fields + args args + wantErr bool + want bucketData + }{ + { + name: "success", + args: args{ + contract: "contract", + method: "method", + acl: []byte("acl"), + }, + want: bucketData{ + utils.MakeContractMethodKey("contract", "method"): []byte("acl"), + }, + }, + { + name: "fail", + args: args{ + contract: "Contract_put_error", + method: "method", + acl: []byte("acl"), + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := &ContractBucket{ + DB: mockContext{ + Contract: bucketData{}, + }, + } + if err := b.SetMethodACL(tt.args.contract, tt.args.method, tt.args.acl); (err != nil) != tt.wantErr { + t.Errorf("ContractBucket.SetMethodACL() error = %v, wantErr %v", err, tt.wantErr) + } + got := b.DB.(mockContext).Contract + if !tt.wantErr && !reflect.DeepEqual(got, tt.want) { + t.Errorf("ContractBucket.SetMethodACL(), DB = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/kernel/permission/acl/contract_test.go b/kernel/permission/acl/contract_test.go index 53402f9d..538f906d 100644 --- a/kernel/permission/acl/contract_test.go +++ b/kernel/permission/acl/contract_test.go @@ -199,97 +199,6 @@ func mockACL(rule pb.PermissionRule) []byte { return data } -//func TestUpdateAK2AccountReflection(t *testing.T) { -// type args struct { -// ctx contract.KContext -// aclOldJSON []byte -// aclNewJSON []byte -// accountName string -// } -// tests := []struct { -// name string -// args args -// wantErr bool -// }{ -// { -// name: "update between different rules", -// args: args{ -// ctx: new(mockContext), -// aclOldJSON: mockACL(pb.PermissionRule_SIGN_THRESHOLD), -// aclNewJSON: mockACL(pb.PermissionRule_SIGN_AKSET), -// accountName: "account", -// }, -// }, -// { -// name: "add", -// args: args{ -// ctx: new(mockContext), -// aclOldJSON: nil, -// aclNewJSON: mockACL(pb.PermissionRule_SIGN_THRESHOLD), -// accountName: "account", -// }, -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// if err := UpdateAK2AccountReflection(tt.args.ctx, tt.args.aclOldJSON, tt.args.aclNewJSON, tt.args.accountName); (err != nil) != tt.wantErr { -// t.Errorf("UpdateAK2AccountReflection() error = %v, wantErr %v", err, tt.wantErr) -// } -// }) -// } -//} - -//func TestAK2AccountBucket_UpdateForAccount(t *testing.T) { -// type fields struct { -// DB contract.XMState -// } -// type args struct { -// account string -// oldAKs []string -// newAKs []string -// } -// tests := []struct { -// name string -// fields fields -// args args -// wantErr bool -// }{ -// -// { -// name: "update with overlap", -// fields: fields{ -// DB: new(mockContext), -// }, -// args: args{ -// account: "account", -// oldAKs: []string{"ak1", "ak2"}, -// newAKs: []string{"ak2", "ak3"}, -// }, -// }, -// { -// name: "add", -// fields: fields{ -// DB: new(mockContext), -// }, -// args: args{ -// account: "account", -// oldAKs: nil, -// newAKs: []string{"ak1", "ak2"}, -// }, -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// b := &AK2AccountBucket{ -// DB: tt.fields.DB, -// } -// if err := b.UpdateForAccount(tt.args.account, tt.args.oldAKs, tt.args.newAKs); (err != nil) != tt.wantErr { -// t.Errorf("AK2AccountBucket.UpdateForAccount() error = %v, wantErr %v", err, tt.wantErr) -// } -// }) -// } -//} - func TestKernMethod_NewAccount(t *testing.T) { type fields struct { BcName string From 719cb03fad9f6d2bdc8f39120f3e3c11817877fb Mon Sep 17 00:00:00 2001 From: zhugelianglongming Date: Thu, 23 Feb 2023 11:28:43 +0800 Subject: [PATCH 3/3] Gofmt --- kernel/permission/acl/bucket/account_test.go | 2 +- kernel/permission/acl/bucket/context_mock_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/permission/acl/bucket/account_test.go b/kernel/permission/acl/bucket/account_test.go index a7c215ac..4d6a1d9e 100644 --- a/kernel/permission/acl/bucket/account_test.go +++ b/kernel/permission/acl/bucket/account_test.go @@ -101,7 +101,7 @@ func TestAccountBucket_SetAccountACL(t *testing.T) { t.Errorf("AccountBucket.SetAccountACL() error = %v, wantErr %v", err, tt.wantErr) } got := b.DB.(mockContext).Account - if !tt.wantErr && !reflect.DeepEqual(got, tt.want){ + if !tt.wantErr && !reflect.DeepEqual(got, tt.want) { t.Errorf("AccountBucket.SetAccountACL(), DB = %v, want %v", got, tt.want) } }) diff --git a/kernel/permission/acl/bucket/context_mock_test.go b/kernel/permission/acl/bucket/context_mock_test.go index 5d4bbc8a..c129c136 100644 --- a/kernel/permission/acl/bucket/context_mock_test.go +++ b/kernel/permission/acl/bucket/context_mock_test.go @@ -70,7 +70,7 @@ func (m mockContext) Put(bucket string, key, value []byte) error { } m.Account[k] = value case utils.GetContractBucket(): - if strings.HasPrefix(k,"Contract_put_error") { + if strings.HasPrefix(k, "Contract_put_error") { return errors.New(k) } m.Contract[k] = value