How can i check the module-name in `cont` type transactions for gas-station? - pact-lang

I have implemented a gas-station for my project.
When i tried to pay gas for a cont type txn, and it threw me Inside an exec error.
(defcap GAS_PAYER:bool
( user:string
limit:integer
price:decimal
)
(enforce (= "exec" (at "tx-type" (read-msg))) "Inside an exec")
(enforce (= 1 (length (at "exec-code" (read-msg)))) "Tx of only one pact function")
(enforce (= "(free.anedak." (take 13 (at 0 (at "exec-code" (read-msg))))) "only free.anedak smart contract")
(compose-capability (ALLOW_GAS))
)
While looking at the code, I found that only exec type transactions are supported in gas-stations and the ability to check that the txn call is made only by my contract is only possible in exec type txns.
The data to check for module name and txn types are added in this block of code.
https://github.com/kadena-io/chainweb-node/blob/0b24dd35a7fda27719ea7a2ca54db2cfd1196d95/src/Chainweb/Pact/TransactionExec.hs#L876.
I don't think i can use the existing props for checking the module name in cont type txns.
https://github.com/kadena-io/chainweb-node/blob/8c32fcfff85c4e5b61a9554f0180ca6c90840e42/test/pact/continuation-gas-payer.pact
I found this one for cont type txns, but no checks on module name.
Any solutions on how i can check the module-name in cont type txns will be helpful as this will prevent my gas-station from spamming.

Related

How do I grant a capability to an account after a module has been deployed?

I have my contract deployed on the testnet and I am trying to call (mint-nft) which has no arguments, but it does have a require-capability ACCOUNT_GUARD. I am getting error as follows:
Error from (api.testnet.chainweb.com): : Failure: require-capability: not granted:
(free.slightly-less-shitty-nft-project.ACCOUNT_GUARD "k:ab7cef70e1d91a138b8f65d26d6917ffcb852b84525b7dc2193843e8dfebf799")
ACCOUNT_GUARD capability defined: (taken from kitty-kad contract)
(defcap ACCOUNT_GUARD(account:string) ; Used for admin functions
#doc "Verifies account meets format and belongs to caller"
(enforce (= "k:" (take 2 account)) "For security, only support k: accounts")
(enforce-guard
(at "guard" (coin.details account))
)
)
A simplified mint-nft is defined as follows:
(defun mint-nft()
#doc "Allows any k: address to mint an nft"
(require-capability (ACCOUNT_GUARD (at "sender" (chain-data))))
(insert nft-main-table id {
"id":id,
"generation":0,
"owner-address":(at "sender" (chain-data))
})
)
From my understanding, ACCOUNT_GUARD is simply checking to see that the account calling the contract is a valid k: address and that the account calling it is the owner of said account.
Why would the ACCOUNT_GUARD not be granted to the caller of (mint-nft) in this case?
How do I grant the ACCOUNT_GUARD capability to any user that calls (mint-nft)?
Capabilities must be acquired before require-capability will succeed. See https://pact-language.readthedocs.io/en/stable/pact-reference.html#expressing-capabilities-in-code-defcap for discussion of this.
Capabilities are acquired with with-capability such that code within the body of the with-capability expression have the granted capability in scope. At that point you could call mint-nft, if that was a "protected function" that you did not want users to call at top level.
However if mint-nft is the intended top-level call, then use with-capability instead of require-capability, and include the insert call in the body. (Your indentation already looks like this even though there is no body in require-capability btw).
Lastly, you should not base anything on sender -- this is the gas payer only and in Kadena gas can be paid by a gas station or really anybody. Instead you should manage a user table with the associated guard, and validate the principal when creating the user. k: without any associated guard is meaningless.

Using loops to set an agent type as idle that will accept an agent when in that state and reject if busy

I have 3 identical suites represented by one agent type for productionSuite and I want to use loops to set the suites as idle and busy. The agent of productionOrder needs to be sent to only the idle rooms.
I have a parameter of assignedSuite in the ProductionOrder agent that equals a random suite that is picked in the source on main. I started to try loops in this source that relate to the statechart for ProductionSuite agent. I think I need a piece of code to defines the ProductionSuite as 0,1,2 and then checks with a loop if they have an ProductionOrder or not.
[Source]
(Original Code)
agent=ProductionOrder
agent.assignedSuite = productionSuite(uniform_discr(0,2));
deliver("Suite is Scheduled", agent.assignedSuite);
(new code)
Action:
`if ( productionSuite(0).inState(idle))
agent.assignedSuite = productionSuite(0);
agent.receive("Suite is Scheduled");
if ( productionSuite(1).inState(idle))
agent.assignedSuite = productionSuite(1);
agent.receive("Suite is Scheduled");
if ( productionSuite(2).inState(idle))
agent.assignedSuite = productionSuite(2);
agent.receive("Suite is Scheduled");`
The error I get is that idle cannot be resolved as a variable. Though I am not sure this is the best method to use. Could also use some direction on when to group the suites or if I should define them separately.
that error is caused because your Source object doesn't know "idle". You need to rewrite it as follows:
if (productionSuite(0).inState(ProductionSuite.idle))
Assuming that your productionSuite agents are of type ProductionSuite (notice the capital letter). In short, you need to tell the code checking for the state to which agent type the state belongs so it knows where to look.
Hope this helps

How to prevent transactions from violating application invariants in Datomic

To elaborate, most relational databases have the idea of a database constraint. Here is the Postgres documentation on constraints. What tools does Datomic offer to constrain data or maintain some set of invariants on the data stored?
EDIT 2019-06-28: Since 0.9.5927 (Datomic On-Prem) / 480-8770 (Datomic Cloud), Datomic supports finer write-time validation via Attribute Predicates, Entity Specs and Entity Predicates. This makes most of the initial answer invalid or irrelevant.
In particular, observe that Entity Predicates accept a database value as a parameter, so they can actually enforce invariants that span several Entities.
By default, Datomic enforces only a very limited set of constraints regarding what data may be written, including mostly:
uniqueness constraints: see Identity and Uniqueness
type constraints, e.g you may not write a number to an attribute that is :db.type/string
entity resolution: operations like [:db/add [:my.app/id "fjdsklfjsl"] :my.app/content 42] will fail if the [:my.app/id "fjdsklfjsl"] lookup-ref does not resolve to an existing entity
conflicts, e.g Datomic won't let you :db/add 2 different values for the same entity-attribute pair if the attribute cardinality is one.
(I may be forgetting some, if so please comment.)
In particular at the time of writing, there is no built-in way to add custom validation or 'foreign-key' constraint to a given attribute.
However, combining Transaction Functions and speculative writes (a.k.a db.with()) gives you a powerful way of enforcing arbitrary invariants. For instance, you can wrap a transaction in a transaction function that applies the transaction speculatively using db.with(), then searches the speculative result for invariant violations, throwing an Exception if it finds some. You can even make this transaction function very generic by implementing the 'search invariant violations' part in Datalog.
Here's an example of what the API may look like:
[:myapp.fns/checking-invariants
;; a description of the invariant
{:query
[:find ?message ?user-id
:in $db-before $db-after ?tx-data ?tempids ?user-id
:where
[$db-before ?user :myapp.user/id ?user-id]
[$db-before ?user :myapp.user/email ?email-before]
[$db-after ?user :myapp.user/email ?email-after]
[(not= ?email-before ?email-after)]
[(ground "A user may not change her email") ?message]]
:inputs ["user-id-12342141"]}
;; the wrapped transaction
[[:db/add 125315815291 :myapp.user/email "hello.world#yopmail.com"]
[:db/add 125315815291 :myapp.user/name "Foo Bar"]]]
Here's an (untested) implementation of :myapp.fns/checking-invariants:
{:db/ident :myapp.fns/checking-invariants,
:db/fn #db/fn{:lang :clojure,
:imports [],
:requires [[datomic.api :as d]],
:params [db invariant-q tx],
:code
(let [{:keys [query inputs]} invariants-q
{:keys [db-before db-after tx-data tempids]}
(d/with db tx)]
(when-some [violations (apply d/q query
db-before db-after tx-data tempids
inputs)]
(throw (ex-info
"Transaction would violate invariants."
{:tx tx
:violations violations
:t (d/basis-t db-before)})))
tx)}}
Limitations:
you can only protect externally: the client has to opt in to using this invariant-checking transaction function.
be careful about performance - abusing this approach may put too much load on the Transactor. In cases where it is safe to do so, you may prefer to perform validation on the Peer using db.invoke()
make sure your transaction is deterministic, as it will be run twice (more precisely, make sure that whether your transaction violates the invariant is deterministic)
One way is to use transaction functions that modify data and do constraint validation during modification: http://docs.datomic.com/database-functions.html#uses-for-transaction-functions

How can I determine which rpm installs the module which defines an SELinux type?

My package needs to set up some SELinux rules to allow my program access to certain things. Although I know which types I need to use in setting up the rules, I'm not sure which packages install those types. I would like to make sure that my package has dependencies on the SELinux types I reference. Is there a way I can find out which package was responsible for installing a given SELinux type?
In this specific case I'm looking for unconfined_service_t, but a general solution would be great because I'm sure I'll hit this again.
SELinux modules are in /usr/share/selinux/targeted/ directory.
You must guess (more about this later) which file it may be.
# cp /etc/selinux/targeted/modules/active/modules/cdrecord.pp /tmp
# file cdrecord.pp
cdrecord.pp: bzip2 compressed data, block size = 900k
# bunzip2 cdrecord.pp
bunzip2: Can't guess original name for cdrecord.pp -- using cdrecord.pp.out
# dnf install checkpolicy
...
# sedismod cdrecord.pp.out
Reading policy...
libsepol.policydb_index_others: security: 0 users, 3 roles, 42 types, 3 bools
libsepol.policydb_index_others: security: 1 sens, 1024 cats
libsepol.policydb_index_others: security: 51 classes, 0 rules, 0 cond rules
libsepol.policydb_index_others: security: 0 users, 3 roles, 42 types, 3 bools
libsepol.policydb_index_others: security: 1 sens, 1024 cats
libsepol.policydb_index_others: security: 51 classes, 0 rules, 0 cond rules
Binary policy module file loaded.
Module name: cdrecord
Module version: 2.6.0
Select a command:
1) display unconditional AVTAB
2) display conditional AVTAB
3) display users
4) display bools
5) display roles
6) display types, attributes, and aliases
7) display role transitions
8) display role allows
9) Display policycon
0) Display initial SIDs
a) Display avrule requirements
b) Display avrule declarations
c) Display policy capabilities
l) Link in a module
u) Display the unknown handling setting
F) Display filename_trans rules
f) set output file
m) display menu
q) quit
Command ('m' for menu): 6
...
staff_cdrecord_t [1]: alias for type cdrecord_t flags:0
...
Let say that staff_cdrecord_t was the one which interested us. Hurray!
Now just query which package provide it:
# rpm -qf /etc/selinux/targeted/modules/active/modules/cdrecord.pp
selinux-policy-targeted-3.13.1-105.20.fc21.noarch
So the only question is what file from /etc/selinux/targeted/modules/active/modules/ you query. Well you either have to go one by one (unless somebody knows some way, which is scriptable) or you have to use common sense. When I look for staff_cdrecord_t, I would start with cdrecord.pp.

cross-group transaction error with only one entity

I'm trying to execute the code below. Some times it works fine. But some times it does not work.
#db.transactional
def _add_data_to_site(self, key):
site = models.Site.get_by_key_name('s:%s' % self.site_id)
if not site:
site = models.Site()
if key not in site.data:
site.data.append(key)
site.put()
memcache.delete_multi(['', ':0', ':1'], key_prefix='s%s' %
self.site_id)
I'm getting the error:
File "/base/data/home/apps/xxxxxxx/1-7-1.366398694339889874xxxxxxx.py", line 91, in _add_data_to_site
site.put()
File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 1070, in put
return datastore.Put(self._entity, **kwargs)
File "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py", line 579, in Put
return PutAsync(entities, **kwargs).get_result()
File "/base/python_runtime/python_lib/versions/1/google/appengine/api/apiproxy_stub_map.py", line 604, in get_result
return self.__get_result_hook(self)
File "/base/python_runtime/python_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1569, in __put_hook
self.check_rpc_success(rpc)
File "/base/python_runtime/python_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1224, in check_rpc_success
raise _ToDatastoreError(err)
BadRequestError: cross-group transaction need to be explicitly specified, see TransactionOptions.Builder.withXG
So, my question is:
If I'm changing only one entity (models.Site) why am I getting a cross-group transaction error?
As mentioned in the logs: "Cross-group transaction need to be explicitly specified".
Try specifying it by using
#db.transactional(xg=True)
Instead of:
#db.transactional
Does this work if you specify parent=None in your get_by_key_name() query?
Essentially, in order to use a transaction, all entities in the transaction must share the same parent (ie you query using one parent, and create a new entity withe the same parent), or you must use a XG transaction. You're seeing a problem because you didn't specify a parent.
You may need to create artificial entities to behave as parents in order to do what you're trying to do.
I had the same issue. By stepping through the client code, I made the following two observations:
1) Setting a parent of (None) seems to still indicate a parent of that kind, even if there's no specific record elected as that parent.
2) Your transaction will include all ReferenceProperty properties as well.
Therefore, you should, theoretically, get the cross-group transaction exception if you haven't declared a parent (by either omitting or setting to (None)) on any of the kinds that you're affecting if there's at least two (because if you're using kind A and kind B, it looks like you're using two different entity groups, for A records and for B records), -as well as- any of the kinds referred-to by any ReferenceProperty properties.
To fix this, you must create, at least, a kind without any properties, that can be set as the parent of all of your previously no-parent records, as well as the parent of all ReferenceProperty properties that they declare.
If that's not sufficient, then set the flag for the cross-group transaction.
Also, the text of the exception, for me, was: "cross-groups transaction need to be explicitly specified" (plural "groups"). I have version 1.7.6 of the Python AppEngine client.
Please upvote this answer if it fits your scenario.
A cross group transaction error refers to the entity groups being used, not the kind used (here Site).
When it occurs, it's because you are attempting a transaction on entities with different parents, hence putting them in different entity groups.
SHAMELESS PLUG:
You should stop using db and move your code to ndb, especially since it seems you're in the development phase.

Resources