21 / 27Module 1: A Rell backend with FT accountsBeginner
Test the registration
Follow these steps to test the new module:
-
Add a test file at
src/test/registration_test.rell. -
Register the test module in
chromia.ymlby listing it under the modules:
blockchains: newschain: test: modules: - test- Open
registration_test.rell, add the required imports, and declare the EVM address and private key the tests will use:
@test module;import registration. { register_user };import news_feed. { make_post, user, post };import lib.ft4.accounts. { single_sig_auth_descriptor };import lib.ft4.auth. { evm_signatures };import lib.ft4.core.accounts.strategies.open. { ras_open };import lib.ft4.external.accounts.{ add_auth_descriptor };import lib.ft4.external.accounts.strategies. { get_register_account_message };import lib.ft4.test.core.{ ft_auth_operation_for, evm_auth_operation_for, evm_sign };val evm_address = x"1337c28e95ce85175af66353fecccd676e3d273a";val evm_privkey = x"18e2d37cd5b51555c52d454c22608dee5e7151384f2d7b7bc21616e2eadc3e6f";- Add a test case to
registration_test.rellthat registers an account with EVM signatures:
function test_evm_registration() { val alice_auth_desc = single_sig_auth_descriptor(evm_address, set(["A", "T"])); val alice_session_1 = rell.test.keypairs.alice; val session1_auth_desc = single_sig_auth_descriptor(alice_session_1.pub, set(["S"])); val strategy_op = ras_open(alice_auth_desc, session1_auth_desc); val register_op = register_user("Alice"); val message = get_register_account_message( strategy_op.to_gtx_operation(), register_op.to_gtx_operation() ); val signature = evm_sign(message, evm_privkey); // Register account using evm address rell.test.tx() .op(evm_signatures([evm_address], [signature])) .op(strategy_op) .op(register_op) .sign(alice_session_1) .run(); assert_equals(user @ { } ( @sum 1 ), 1); // Make post using session key rell.test.tx() .op(ft_auth_operation_for(alice_session_1.pub)) .op(make_post("My first post")) .sign(alice_session_1) .run(); assert_equals(post @ { } ( @sum 1 ), 1); // Start a new session val alice_session_2 = rell.test.keypairs.bob; val session_auth_desc = single_sig_auth_descriptor(alice_session_2.pub, set(["S"])); val add_auth_descriptor_operation = add_auth_descriptor(session_auth_desc); // Add session token rell.test.tx() .op(evm_auth_operation_for(evm_privkey, add_auth_descriptor_operation)) .op(add_auth_descriptor_operation) .sign(alice_session_2) .run(); // Make another post using session key rell.test.tx() .op(ft_auth_operation_for(alice_session_2.pub)) .op(make_post("My second post")) .sign(alice_session_2) .run(); assert_equals(post @ { } ( @sum 1 ), 2);}The test mimics a user who signs up with an EVM address and a matching signature. It does the following:
- Builds an authentication descriptor for the EVM key carrying the "A" and "T" flags, which grant administrative and transfer access in the
ftframework. - Generates a session keypair and a matching authentication descriptor for Alice (
alice_session_1). - Produces the EVM registration signature with
evm_sign, covering the operations the transaction will call. - Assembles a transaction containing the
register_accountoperations and signs it with Alice's session keypair. - Checks that the registration succeeded.
Posting with session keypair
As an extra check, the next test uses the session keypair to create a post:
// Make a post using the session keypair rell.test.tx() .op(ft_auth_operation_for(alice_session_1.pub)) .op(make_post("My first post")) .sign(alice_session_1) .run(); assert_equals(post @ { } ( @sum 1 ), 1);Here the session keypair signs the transaction, and ft_auth_operation_for from the ft test module supplies the authentication properties. The test:
- Builds a new transaction, using
ft_auth_operation_forto add the authentication properties. - Signs it with Alice's session keypair.
- Checks that the post was created.
Logging in with a new session keypair
To confirm the behavior holds across logins, the following test simulates a user signing in a second time. The user generates a fresh session keypair and registers it with the dapp through the ft4.add_auth_descriptor operation:
// Start a new session val alice_session_2 = rell.test.keypairs.bob; val session_auth_desc = single_sig_auth_descriptor(alice_session_2.pub, set(["MySession"])); val add_auth_descriptor_operation = add_auth_descriptor(session_auth_desc); // Add a session token rell.test.tx() .op(evm_auth_operation_for(evm_privkey, add_auth_descriptor_operation)) .op(add_auth_descriptor_operation) .sign(alice_session_2) .run(); // Make another post using the session key rell.test.tx() .op(ft_auth_operation_for(alice_session_2.pub)) .op(make_post("My second post")) .sign(alice_session_2) .run(); assert_equals(post @ { } ( @sum 1 ), 2);The test:
- Generates a new session keypair and defines a single-signature auth descriptor with the "MySession" flag.
- Builds a transaction that adds the new session key.
- Signs that transaction with the EVM private key.
- Creates another post and checks that it was created.
To learn more about how FT4 works, including authentication descriptors and session handling, see the FT4 Accounts and Tokens documentation.
You now have a module that registers user accounts through an EVM wallet, along with tests that confirm it works.