paritytech / polkadot-staking-miner

polkadot staking miner
GNU General Public License v3.0
23 stars 8 forks source link

fetch weights from remote node #12

Open niklasad1 opened 2 years ago

niklasad1 commented 2 years ago

Currently, the weights are hardcoded for each chain because the MinerConfig trait from substrate is required to be implemented.

Instead ideally it would be useful if we could just get the weights via runtime RPC API. Perhaps something like:

   fn weight_from_extrinsic(uxt: UnsignedExtrinsic) -> Weight

Similar to https://github.com/paritytech/polkadot/blob/master/runtime/polkadot/src/weights/pallet_election_provider_multi_phase.rs#L142-#L153

Then just plug add the Weight as a parameter to the call to mine_solution_with_snapshot.

Which in turn requires changes in EPM pallet to remove solution_weight from the Miner trait.

That pretty much would make it work.

https://github.com/paritytech/substrate/issues/11665 might help with this.

/cc @kianenigma

kianenigma commented 2 years ago

Optionally, fn solution_weight should stay, instead in the impl MinerConfig for _ in the staking miner, instead we forward the call to the RPC. Might clash a bit with mixing async functions with a normal function, wdyt?

niklasad1 commented 2 years ago

it's a bit difficult to make fn solution_weight a future or something like that because the EPM pallet uses that API and it needs to be implemented for the runtime mocks.

It's probably easier to remove some of the things from MinerConfig and try what I wrote above, it's a bit more work than I expected :P

niklasad1 commented 2 years ago

After getting some help by Kian and we discussed the to create a "fake RawSolution" with correct data from the snapshot. Example snippet code in staking miner how to do it:

    let (voters, targets, desired_targets) = snapshot(&api, hash).await?;

    // voters, targets and desired_targets should be used in here.
    let raw_solution = Default::default();
    let uxt = api
        .tx()
        .election_provider_multi_phase()
        .submit(raw_solution)?;
    let xt = uxt
        .create_signed(&*signer, ExtrinsicParams::default())
        .await?;

    let encoded_xt = Bytes(xt.encoded().to_vec());

    let weight: Weight = api
        .client
        .rpc()
        .client
        .request(
            "state_call",
            rpc_params!["TransactionPaymentApi_query_fee_details", encoded_xt],
        )
        .await
        .unwrap();

  // this is currently not possible and doesn't work because `EPM::maximum_voter_for_weight`     
  // calls`MinerConfig::solution_weight several times this just has a static weight.....
   MinerConfig::mine_solution_with_snapshot(
        voters,
        targets,
        desired_targets,
        weight
    )

In order to have this API in miner then the EPM pallet would need to be changed to take Weight as input and it seems not like a super good idea because EPM::maximum_voter_for_weight actually calls MinerConfig::solution_weight more than once with the different values.

Thus, it could be possible move maximum_voter_for_weight to the staking-miner side of things and then just pass in maximum_allowed_voters in mine_solution_with_snapshot instead of the weight.

//cc @kianenigma thoughts on doing that?

niklasad1 commented 2 years ago

Thus the proposed could would look something like:

    pub async fn maximum_voter_for_weight(
                &self,
        desired_winners: u32,
        size: SolutionOrSnapshotSize,
        max_weight: Weight,
    ) -> u32 {

     // this could be closure but async closures doesn't work
     async fn with_weight(rpc, voters: u32, targets: u32, active_voters: u32, desired_targets: u32) -> Weight {
          let solution = from(voters, targets, active_voters, desired_targets);
          let uxt = api.tx().election_provider_multi_phase().submit(raw_solution).encode();
          rpc.request("state_call". rpc_params!["TransactionPaymentApi_query_fee_details", Bytes::new(uxt)
      }

      ... the existing code with binary search etc....
      ... that calls `with_weight`
    }

    let (voters, targets, desired_targets) = snapshot(&api, hash).await?;

    let size = SolutionOrSnapshotSize { voters: voters.len() as u32, targets: targets.len() as u32 };
    let maximum_allowed_voters = maximum_voter_for_weight(voters, size, MinerConfig::MaxWeight);

   MinerConfig::mine_solution_with_snapshot(
        voters,
        targets,
        desired_targets,
        maximum_allowed_voters,
    )

it looks a bit hacky but that's simplest I could come up with.

kianenigma commented 2 years ago

This is done, but likely it is super duper slow, so will leave open for now.