filecoin-project / ref-fvm

Reference implementation of the Filecoin Virtual Machine
https://fvm.filecoin.io/
Other
382 stars 137 forks source link

FVM actor development story #734

Open mriise opened 2 years ago

mriise commented 2 years ago

Context

Currently developing an actor for FVM requires installing lotus, and while this is alright for M1, M2 we want to have fully user-programmable actors. There is already some work being done to make a high-level SDK for Rust (https://github.com/filecoin-project/devgrants/issues/562) and some ideas for doing some simple debugging with dual-execution in lotus, though eventually we will want to have more tools for developers to make things nicer.

This issue is to open the discussion about what we want for developer experience in the mid to further future.

mriise commented 2 years ago

pinging @karim-agha since they have interest in this

karim-agha commented 2 years ago

I think that one of the most important aspects of our Native Actors SDK would be to ability to exercise the actor logic within the scope of 1) a Rust unit test and 2) local single node validator. A good example of 1) is Solana's Program Test where you can create an instance of a validator node emulator as a rust object:

 let (mut client, payer, recent_blockhash) = ProgramTest::new(
        "spl_example_sysvar",
        program_id,
        processor!(process_instruction), // <-- this is the equivalent of our invoke() entrypoint
    )
    .start()
    .await;

and then process transactions by sending them to that emulator:

let mut transaction = Transaction::new_with_payer(
        &[Instruction::new_with_bincode(
            program_id,
            &(),
            vec![
                AccountMeta::new(sysvar::clock::id(), false),
                AccountMeta::new(sysvar::rent::id(), false),
            ],
        )],
        Some(&payer.pubkey()),
    );
    transaction.sign(&[&payer], recent_blockhash);
    banks_client.process_transaction(transaction).await.unwrap();

A more complete example of this mechanism can be found in the tests of the associated token account tests:


//-------
// setup
//------

// setup inputs
let wallet_address = Pubkey::new_unique();
let token_mint_address = Pubkey::new_unique();

// setup mock validator node
let (mut banks_client, payer, recent_blockhash) =
        program_test_2022(token_mint_address, true).start().await;

//-------
// assert preconditions
//-------
assert_eq!(
        banks_client
            .get_account(associated_token_address)
            .await
            .expect("get_account"),
        None,
    );

//-------
// invoke
//-------
let mut transaction = Transaction::new_with_payer(
        &[create_associated_token_account(
            &payer.pubkey(),
            &wallet_address,
            &token_mint_address,
            &spl_token_2022::id(),
        )],
        Some(&payer.pubkey()),
    );
    transaction.sign(&[&payer], recent_blockhash);
    banks_client.process_transaction(transaction).await.unwrap();

//-------
// assert post conditions
//-------
let associated_account = banks_client
        .get_account(associated_token_address)
        .await
        .expect("get_account")
        .expect("associated_account not none");
assert_eq!(associated_account.data.len(), expected_token_account_len,);
assert_eq!(associated_account.owner, spl_token_2022::id());
assert_eq!(associated_account.lamports, expected_token_account_balance);

In our case I think it would be good if we create a stripped down version of the FVM with all the networking and storage mocked, where developers can predefine the chain conditions they expect their actors to operate in (or simulate a particular chain situation) the provide that as a core SDK components.

I also think that it is worth looking a look at Solana Test Validator, which is an instance of a validator that runs on localhost and simulates the chain. It preserves all state changes through test invocations as it lives in a separate process and lets developers not only test on-chain programs but also web3 web apps that need to interact with the chain RPC interface.

karim-agha commented 2 years ago

Actually I think that I've found something like that already in our codebase here: https://github.com/filecoin-project/ref-fvm/pull/290/files (examples/token/tests/token.rs)

anorth commented 2 years ago

A related discussion: https://github.com/filecoin-project/ref-fvm/issues/592