-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added prefix for DARC storage #262
Conversation
Random thought @jbsv (and @nkcr if you're still peeking 😜): Right now, we're fixing #258 by adding yet another custom prefix for each contract. Any thoughts ? |
I was thinking about that while reading #260 and wondering if there could be a generic way to add the prefix.
(I'm still starting to learn more about dela, so excuse me if my lack of knowledge makes this comment not really useful.) |
- Contracts have a hardcoded (4-bytes) access key - Access keys are also used as storage prefix
You're absolutely right @lanterno. We avoided solution n° 2 as we want the K/V store to be as unaware of its usage as possible. We could add an adapter that implements this prefixing abstraction, but for now we went with just solving n° 1, which is pretty simple and self-sufficient. |
Pull Request Test Coverage Report for Build 5832016077
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apart from some minor changes, LGTM
Kudos, SonarCloud Quality Gate passed! |
Scoping the store accesses with a contract prefix is a reasonable solution. Ultimately, it would be nice to have an access control by sotre elements (i.e. keys) instead of by contracts. It would give more flexibility such as the ability to share keys between contracts. Also, the check could be done at the module level (the "native" smart contract) instead of individually from each contract definition. Something along those lines: // execution/native/native.go
// Execute implements execution.Service. It uses the executor to process the
// incoming transaction and return the result.
func (ns *Service) Execute(snap store.Snapshot, step execution.Step) (execution.Result, error) {
name := string(step.Current.GetArg(ContractArg))
contract := ns.contracts[name]
if contract == nil {
return execution.Result{}, xerrors.Errorf("unknown contract '%s'", name)
}
res := execution.Result{
Accepted: true,
}
// should come from the service
var accessSrv access.Service
// smart contracts are provided a secured snap that checks actions on it
secureSnap := newSecureSnap(snap, accessSrv, step.Current.GetIdentity())
err := contract.Execute(secureSnap, step)
if err != nil {
res.Accepted = false
res.Message = err.Error()
}
return res, nil
}
func newSecureSnap(snap store.Snapshot, accessSrv access.Service,
identity access.Identity) store.Snapshot {
return secureSnap{...}
}
type secureSnap struct {
snap store.Snapshot
accessSrv access.Service
identity access.Identity
}
func (snap secureSnap) Get(key []byte) ([]byte, error) {
creds := newSnapCred(key, "read")
err := snap.accessSrv.Match(snap.snap, creds, snap.identity)
if err != nil {
return nil, xerrors.Errorf("verification failed: %v", err)
}
return snap.snap.Get(key)
}
func (snap secureSnap) Set(key []byte, value []byte) error {
creds := newSnapCred(key, "write")
err := snap.accessSrv.Match(snap.snap, creds, snap.identity)
if err != nil {
return xerrors.Errorf("verification failed: %v", err)
}
return snap.snap.Set(key, value)
}
func (snap secureSnap) Delete(key []byte) error {
creds := newSnapCred(key, "delete")
err := snap.accessSrv.Match(snap.snap, creds, snap.identity)
if err != nil {
return xerrors.Errorf("verification failed: %v", err)
}
return snap.snap.Delete(key)
}
func newSnapCred(key []byte, action string) snapCred {
return snapCred{
key: key,
action: action,
}
}
// snapCred defines credentials for operations on a snap. The credentials match
// the "read", "write", "delete" actions defined on the 4 first bytes of a key.
// For example here the rule on a single key prefix:
//
// -- 0xabcdef12
// -- "read"
// -- Alice
// -- Bob
// -- "write"
// -- Bob
// -- "delete"
// -- Bob
//
type snapCred struct {
key []byte
action string
}
func (sc snapCred) GetID() []byte {
return append([]byte{}, sc.key[:4]...)
}
func (cs snapCred) GetRule() string {
return cs.action
} It could be optimized for example by performing the checks in batches. We could also express rules differently with "*" or "R/W", or by including the contract to express rules like "User Bob can write key X for contract A but only read key X for contract B":
|
There's a weakness in Dela (#258) whereby the (existing) smart contracts store key=values without any prefix for the keys in a shared key/value store. Effectively this would allow any malicious user to overwrite other smart contracts' data.
This fixes it for the Distributed Access Rights Controls (DARC) functionality.