tokenika / eosfactory

Python-based EOS smart-contract development & testing framework
http://eosfactory.io/
Other
243 stars 62 forks source link

Set custom permission seems not working #140

Open Patrick-Shih opened 5 years ago

Patrick-Shih commented 5 years ago

I follow https://eosfactory.io/build/html/patterns/set/set_account_permission.html#weights-and-threshold and try to add eosio.code permission to account as follow

alice.set_account_permission(
    Permission.ACTIVE, {
        "threshold":
            1,
        "keys": [],
        "accounts": [{
            "permission": {
                "actor": str(alice),
                "permission": "active"
            },
            "weight": 1
        }, {
            "permission": {
                "actor": str(alice),
                "permission": "eosio.code"
            },
            "weight": 1
        }]
    }, Permission.OWNER, (alice, Permission.OWNER))

The eosio.code permission is working but original alice@active is not working anymore. For example when doing a push action like

host.push_action("hi", {"player": alice}, permission=(alice, Permission.ACTIVE))

it shows

eosfactory.core.errors.Error: ERROR:
Error 3090003: Provided keys, permissions, and delays do not satisfy declared authorizations
Ensure that you have the related private keys inside your wallet and your wallet is unlocked.
Error Details:
transaction declares authority '{"actor":"alice","permission":"active"}', but does not have signatures for it.

I do not know if this is a bug or I just use the wrong way to set permission?

BTW, alice.info() looks perfect

Account object name: alice
name: alice
created: 2019-02-20T10:47:46.000
permissions:
     owner     1:    1 alice@owner
        active     1:    1 alice@active, 1 alice@eosio.code
memory:
     quota:       unlimited  used:     2.674 KiB

net bandwidth:
     used:               unlimited
     available:          unlimited
     limit:              unlimited

cpu bandwidth:
     used:               unlimited
     available:          unlimited
     limit:              unlimited

My environment

stefanzarembinski commented 5 years ago

I guess that you need to have eosio system contracts installed in order to be able to refer to the eosio.code permission.

Within several days I will workout the issue.

stefanzarembinski commented 5 years ago

Sorry, I see that my previous comment is stupid. Nevertheless, I will solve the issue.

stefanzarembinski commented 5 years ago

Evidently, EOSIO does not like giving permissins to itself. Change str(alice) to carol. You do not need to convert account object to string in the actor field. The conversion is needed if there is many actors: EOSIO has them sorted lexicographically. Therefore, in the tutorial actors physical names are sorted, being converted to strings.

Patrick-Shih commented 5 years ago

Hi @stefanzarembinski,

Appreciate for the timely feedback. I think giving permission to other account does not solve the problem. It is a common use case on EOS mainnet to add eosio.code permission to some account to grant permission to contract's inner action with following cleos command:

cleos set account permission testaccount1 active --add-code

Meanwhile, testaccount1 should also be able to do something (call other contract's action) as a normal account with testaccount1@active permission.

I have tried remove the str() without change account (it is not what I want) but no luck it shows the same ERROR.

Or is there any way to just add eosio.code permission to an account without manually set all the permissions. Maybe something like

alice.add_code()
stefanzarembinski commented 5 years ago

Today, we are going to publish a new edition of EOSFactory. There is there a folder archive, see the arbitration contract example. In the tests folder, you can find the test1.md file: it can be executed as a python script:

eosfactory/pythonmd.sh archive/arbitration/tests/test1.md

The contract solves a case of interaction between smart contracts, using the eosio.code permission:

ESCROW.set_account_permission(Permission.ACTIVE,
    {
        "threshold": 1,
        "accounts": 
            [
                {
                    "permission": 
                        {
                            "actor": ESCROW,
                            "permission": "eosio.code"
                        },
                    "weight":1
                }
            ]
    },
    Permission.OWNER
)

I hope, the example may be useful to you. If you do not want to wait for the publication, see the branch pypi-ready.

You suggest a special syntax for the eosio.code permission setting. I will wait until you repeat it after gaining experience with permissions in EOSFactory.

Patrick-Shih commented 5 years ago

Hi @stefanzarembinski,

I have upgraded to EOSfactory 3.0.1 and retry what you say. The result is, still, Error 3090003

As stated in my first post, the issue is not on setting the eosio.code permission, it works perfectly. However, after setting the eosio.code permission, the original account@active is failing.

The issue is how to keep both eosio.code and account@active permission

For example using the eosio hello world contract with the following test code

import sys
from eosfactory.eosf import *

verbosity([Verbosity.INFO, Verbosity.OUT, Verbosity.DEBUG])

CONTRACT_WORKSPACE = sys.path[0] + "/../"

def test():
    reset()
    create_master_account("master")
    create_account("host", master)
    create_account("alice", master)

    contract = Contract(alice, "directory of the hello contract")
    contract.build(force=False)
    contract.deploy()

    alice.info()

    COMMENT('alice say hi:')
    alice.push_action("hi", {"user": alice}, permission=(alice, Permission.ACTIVE))

    COMMENT('Add eosio.code permission to alice:')
    alice.set_account_permission(Permission.ACTIVE, 
        {
            "threshold": 1,
            "accounts": 
            [
                {
                    "permission": {
                        "actor": alice,
                        "permission": "active"
                    },
                    "weight": 1
                }, 
                {
                    "permission": {
                        "actor": alice,
                        "permission": "eosio.code"
                    },
                    "weight": 1
                }
            ]
        }, Permission.OWNER
    )
    alice.info()

    COMMENT('alice say hi again:')
    alice.push_action("hi", {"user": alice}, permission=(alice, Permission.ACTIVE))

    stop()

if __name__ == "__main__":
    test()

The first alice.push_action("hi", {"user": alice}, permission=(alice, Permission.ACTIVE)) will success, but the second will fail

Here are the console output start from the first alice.info()

Account object name: alice
name: nc5tf5izwwgs
created: 2019-03-06T11:00:13.000
permissions:
     owner     1:    1 alice@owner
        active     1:    1 alice@active
memory:
     quota:       unlimited  used:     24.97 KiB

net bandwidth:
     used:               unlimited
     available:          unlimited
     limit:              unlimited

cpu bandwidth:
     used:               unlimited
     available:          unlimited
     limit:              unlimited

###  test:
alice say hi:

* push action ``hi``:
{"user": "alice"}
Hello, alice

###  test:
Add eosio.code permission to alice:

* account permission ``{'threshold': 1, 'accounts': [{'permission': {'actor': , 'permission': 'active'}, 'weight': 1}, {'permission': {'actor': , 'permission': 'eosio.code'}, 'weight': 1}]}``:
Account object name: alice
name: nc5tf5izwwgs
created: 2019-03-06T11:00:13.000
permissions:
     owner     1:    1 alice@owner
        active     1:    1 alice@active, 1 alice@eosio.code
memory:
     quota:       unlimited  used:     24.99 KiB

net bandwidth:
     used:               unlimited
     available:          unlimited
     limit:              unlimited

cpu bandwidth:
     used:               unlimited
     available:          unlimited
     limit:              unlimited

###  test:
alice say hi again:

ERROR /Users/shedoh/Dropbox/workspace/acxel/eosfactory/eosfactory/core/errors.py 44:
Error 3090003: Provided keys, permissions, and delays do not satisfy declared authorizations
Ensure that you have the related private keys inside your wallet and your wallet is unlocked.
Error Details:
transaction declares authority '{"actor":"alice","permission":"active"}', but does not have signatures for it.
stefanzarembinski commented 5 years ago

I use the eosio.code permission there. Please, try to compare our case with yours one. I will help you, if comparison does not solve the issue.

stefanzarembinski commented 5 years ago

There means https://github.com/tokenika/eosfactory/tree/master/archive/arbitration

Patrick-Shih commented 5 years ago

Thank you for the notion. I believe that the issue is not on the eosio.code.

Anyway I try what your say by replace the set_account_permission part as follow

    alice.set_account_permission(Permission.ACTIVE, 
        {
            "threshold": 1,
            "accounts": 
            [
                {
                    "permission": {
                        "actor": alice,
                        "permission": "eosio.code"
                    },
                    "weight": 1
                }
            ]
        }, Permission.OWNER
    )
    alice.info()

The output...

Account object name: alice
name: q35ywewzk1do
created: 2019-03-08T06:22:47.000
permissions: 
     owner     1:    1 alice@owner
        active     1:    1 alice@active
memory: 
     quota:       unlimited  used:     24.97 KiB  

net bandwidth: 
     used:               unlimited
     available:          unlimited
     limit:              unlimited

cpu bandwidth:
     used:               unlimited
     available:          unlimited
     limit:              unlimited

###  test:
alice say hi:

* push action ``hi``:
{"user": "alice"}
Hello, alice

###  test:
Add eosio.code permission to alice:

* account permission ``{'threshold': 1, 'accounts': [{'permission': {'actor': , 'permission': 'eosio.code'}, 'weight': 1}]}``:
Account object name: alice
name: q35ywewzk1do
created: 2019-03-08T06:22:47.000
permissions: 
     owner     1:    1 alice@owner
        active     1:    1 alice@eosio.code
memory: 
     quota:       unlimited  used:     24.96 KiB  

net bandwidth: 
     used:               unlimited
     available:          unlimited
     limit:              unlimited

cpu bandwidth:
     used:               unlimited
     available:          unlimited
     limit:              unlimited

###  test:
alice say hi again:

ERROR /Users/shedoh/Dropbox/workspace/acxel/eosfactory/eosfactory/core/errors.py 44:
Error 3090003: Provided keys, permissions, and delays do not satisfy declared authorizations
Ensure that you have the related private keys inside your wallet and your wallet is unlocked.
Error Details:
transaction declares authority '{"actor":"alice","permission":"active"}', but does not have signatures for it.

As you can see from the ouput, by setting only eosio.code permission, alice not even have alice@active permission, and the failure is expected.

permissions: 
     owner     1:    1 alice@owner
        active     1:    1 alice@eosio.code

Could you please try either my or your way to set the eosio.code permission, and try if that account can perform any action that require its active permission (like eosio.token transfer, or any action that require_auth the caller).

BTW, when using cleos, if the permission (alice@active is presented) is correct and key is presented, the error indicate that the wallet is locked. I believe EOSfactory must has done something in the backgroud to unlock the wallet or bypass it. Maybe there is something i missed to do, such as manually unlock the wallet again or the EOSfactory should do it again for me.

I will say that because I have try using EOSfactory on python interpreter. I create an account and do something then just leave it without stop the local test node. After some time and do the same action again, it show the Error 3090003. I do not know if these two are related or not, just for your reference.

stefanzarembinski commented 5 years ago

Let us consider the command:

alice.set_account_permission(
    Permission.ACTIVE, {
        "threshold":
            1,
        "keys": [],
        "accounts": [
            {
                "permission": 
                    {
                        "actor": alice,
                        "permission": "active"
                    },
                "weight": 1
            },
            {
                "permission": 
                    {
                        "actor": alice,
                        "permission": "eosio.code"
                    },
                "weight": 1
            }
        ]
    }, Permission.OWNER, (alice, Permission.OWNER)) 

It implements the command cleos set account permission alice active {...} owner -p alice@owner. The second argument which is active is the name of the alice's permission to be set. In our case, the permission alice@active has been previously defined, on the creation of the account, and the corresponding keys reside in the wallet.

Now, we execute the action:

HOST.push_action(
    "hi", {"user":alice}, permission=(alice, Permission.ACTIVE))

The code of the action checks the authority of the user which is alice. The permission shown is alice@active. But alice@active is redefined with the command set account permission, and, therefore, cannot be verified with the wallet keys, valid before redefinition.

This is how I see the issue now. Am I wrong?

If you want to be sure about the status of the wallet, you can use the following commands:

get_wallet().open()
get_wallet().unlock()
print(get_wallet().keys())

Also, you can inspect all the calls to cleos:


import eosfactory.core.setup as setup
is_print_command_line = True
stefanzarembinski commented 5 years ago

This is your case but hello_world is replaced with eosio_token.

import sys, os
import eosfactory.core.config as config
from eosfactory.eosf import *

verbosity([Verbosity.INFO, Verbosity.OUT, Verbosity.DEBUG])

#CONTRACT_WORKSPACE = os.path.join(config.eosf_dir(), "contracts/hello_world")
CONTRACT_WORKSPACE = os.path.join(config.eosf_dir(), "contracts/eosio_token")

reset()
MASTER = new_master_account()

COMMENT('''
Build and deploy the contract:
''')

COMMENT('''
Create test accounts:
''')
ALICE = new_account(MASTER)
print(ALICE.active_key); 
print(get_wallet().keys())

ALICE.set_account_permission(
    Permission.ACTIVE, {
        "threshold":
            1,
        "keys": [],
        "accounts": [
            {
                "permission": 
                    {
                        "actor": ALICE,
                        "permission": "active"
                    },
                "weight": 1
            },
            {
                "permission": 
                    {
                        "actor": ALICE,
                        "permission": "eosio.code"
                    },
                "weight": 1
            }
        ]
    }, Permission.OWNER, (ALICE, Permission.OWNER)) 

HOST = new_account(MASTER)
smart_contract = Contract(HOST, CONTRACT_WORKSPACE)
smart_contract.build(force=False)
smart_contract.deploy()

# HOST.push_action(
#     "hi", {"user":ALICE}, permission=(ALICE, Permission.ACTIVE))

HOST.push_action(
    "create",
    {
        "issuer": MASTER,
        "maximum_supply": "1000000000.0000 EOS"
    },
    permission=[(MASTER, Permission.OWNER), (HOST, Permission.ACTIVE)])

HOST.push_action(
    "issue",
    {
        "to": ALICE, "quantity": "100.0000 EOS", "memo": ""
    },
    permission=(MASTER, Permission.ACTIVE))

stop()
stefanzarembinski commented 5 years ago

See the previous two comments. Here comes the third one.

You say:

BTW, when using cleos, if the permission (alice@active is presented) is correct and key is presented

The main feature of EOSFactory is its compatibility with plain cleos bash work. Will you, please, pass the bash script thet you mention?

I want to verify it against EOSFactory.

Patrick-Shih commented 5 years ago

Thanks for the elaboration. I understand your point.

When using cleos I always use --add-code to add eosio.code permission. After some survey I found it do the following things (ref)

cleos set account permission YOUR_CONTRACT active '{"threshold": 1,"keys": [{"key": "CURRENT_PUBLIC_KEY(S)_IN_ACTIVE_PERM","weight": 1}], "accounts": [{"permission":{"actor":"YOUR_CONTRACT","permission":"eosio.code"},"weight":1}]}' -p YOUR_CONTRACT@owner

I found an interesting note that

"keys": [{"key": "CURRENT_PUBLIC_KEY(S)_IN_ACTIVE_PERM","weight": 1}],

It appears that --add-code automatically add all current public keys into the keys field, and that is why I can still use original account@active permission.

I try the same thing by bring in the CURRENT_PUBLIC_KEY as follow when calling set_account_permission` and it WORKS!!

"keys": [{"key": ALICE.active_key.key_public, "weight":1 }],

Thanks you very much!

BTW, I still recommend to add something like alice.add_code() since

  1. cleos does provide that command
  2. I found the key_public field by read the code shell/account.py, which is not on the document. I believe not every user knows and like to read the code.
stefanzarembinski commented 5 years ago

I am glad to see good news from you. Thank you for your persistance.

I will accept your advice. Please, elaborate it more: now I am overworked so much...

stefanzarembinski commented 5 years ago

I am sorry for such a long delay: now I correct the method set_account_permission so that the following expressions make sense:

HOST.set_account_permission(Permission.ACTIVE, add_code=True)
HOST.set_account_permission(Permission.ACTIVE, remove_code=True)

This improvement will be published soon (v3.1.3)

I see now that the last remark of mine, in the previous comment, proves that I really have been overworked then.

But, have you ever seen EOSIDE? It is a VSCode extension, available from the Marketplace. I would like to now your opinion on it, very much.

Patrick-Shih commented 5 years ago

You must take care of your self ... 💪💪💪

Thanks for the info about EOSIDE, look wonderful for me. I am a little busy recently. Will give it a try and let you know my feedback then.