Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement Filecoin.StateAccountKey RPC API #3735

Merged
merged 6 commits into from
Nov 23, 2023
Merged

Conversation

hanabi1224
Copy link
Contributor

@hanabi1224 hanabi1224 commented Nov 22, 2023

Summary of changes

As part of #3639

Changes introduced in this pull request:

  • implement Filecoin.StateAccountKey RPC API

(Manually changed the code to compare the last 500 tipsets)

     Running `target/release/forest-tool api compare /home/me/fr/snapshots/calibnet/forest_snapshot_calibnet_2023-11-21_height_1108092.forest.car.zst --filter StateAccount`
| RPC Method                     | Forest | Lotus |
|--------------------------------|--------|-------|
| Filecoin.StateAccountKey (476) | Valid  | Valid |

Lotus code:

func (m *StateModule) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
	ts, err := m.Chain.GetTipSetFromKey(ctx, tsk)
	if err != nil {
		return address.Undef, xerrors.Errorf("loading tipset %s: %w", tsk, err)
	}

	return m.StateManager.ResolveToDeterministicAddress(ctx, addr, ts)
}

// ResolveToDeterministicAddress is similar to `vm.ResolveToDeterministicAddr` but does not allow `Actor` type of addresses.
// Uses the `TipSet` `ts` to generate the VM state.
func (sm *StateManager) ResolveToDeterministicAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
	switch addr.Protocol() {
	case address.BLS, address.SECP256K1, address.Delegated:
		return addr, nil
	case address.Actor:
		return address.Undef, xerrors.New("cannot resolve actor address to key address")
	default:
	}

	if ts == nil {
		ts = sm.cs.GetHeaviestTipSet()
	}

	cst := cbor.NewCborStore(sm.cs.StateBlockstore())

	// First try to resolve the actor in the parent state, so we don't have to compute anything.
	tree, err := state.LoadStateTree(cst, ts.ParentState())
	if err != nil {
		return address.Undef, xerrors.Errorf("failed to load parent state tree at tipset %s: %w", ts.Parents(), err)
	}

	resolved, err := vm.ResolveToDeterministicAddr(tree, cst, addr)
	if err == nil {
		return resolved, nil
	}

	// If that fails, compute the tip-set and try again.
	st, _, err := sm.TipSetState(ctx, ts)
	if err != nil {
		return address.Undef, xerrors.Errorf("resolve address failed to get tipset %s state: %w", ts, err)
	}

	tree, err = state.LoadStateTree(cst, st)
	if err != nil {
		return address.Undef, xerrors.Errorf("failed to load state tree at tipset %s: %w", ts, err)
	}

	return vm.ResolveToDeterministicAddr(tree, cst, addr)
}

// ResolveToDeterministicAddr returns the public key type of address
// (`BLS`/`SECP256K1`) of an actor identified by `addr`, or its
// delegated address.
func ResolveToDeterministicAddr(state types.StateTree, cst cbor.IpldStore, addr address.Address) (address.Address, error) {
	if addr.Protocol() == address.BLS || addr.Protocol() == address.SECP256K1 || addr.Protocol() == address.Delegated {
		return addr, nil
	}

	act, err := state.GetActor(addr)
	if err != nil {
		return address.Undef, xerrors.Errorf("failed to find actor: %s", addr)
	}

	if state.Version() >= types.StateTreeVersion5 {
		if act.Address != nil {
			// If there _is_ an f4 address, return it as "key" address
			return *act.Address, nil
		}
	}

	aast, err := account.Load(adt.WrapStore(context.TODO(), cst), act)
	if err != nil {
		return address.Undef, xerrors.Errorf("failed to get account actor state for %s: %w", addr, err)
	}
	return aast.PubkeyAddress()

}

Reference issue to close (if applicable)

Closes

Other information and links

Change checklist

  • I have performed a self-review of my own code,
  • I have made corresponding changes to the documentation,
  • I have added tests that prove my fix is effective or that my feature works (if possible),
  • I have made sure the CHANGELOG is up-to-date. All user-facing changes should be reflected in this document.

@hanabi1224 hanabi1224 marked this pull request as ready for review November 23, 2023 07:05
@hanabi1224 hanabi1224 requested a review from a team as a code owner November 23, 2023 07:05
@hanabi1224 hanabi1224 requested review from LesnyRumcajs and elmattic and removed request for a team November 23, 2023 07:05
.get_actor(&addr)?
.with_context(|| format!("failed to find actor: {addr}"))?;

// if state.Version() >= types.StateTreeVersion5
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// if state.Version() >= types.StateTreeVersion5

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the Go code. Rust api does not expose state tree version so I have to use some workaround here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So let's better have a comment than a commented-out Go code. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines +313 to +315
if let Some(address) = actor.delegated_address {
return Ok(address.into());
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we even need to check if it's FVM4 or FVM3? What if (when) we get FVM5?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's good point, it's better to check older versions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But do we need to have this check at all? I mean, paranoid checks are fine by me, but I believe the delegated_address should be None for older versions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But do we need to have this check at all? I mean, paranoid checks are fine by me, but I believe the delegated_address should be None for older versions.

I think you are right, but I'm inclined to keep the logic the same with Go, what do you think?

if state.Version() >= types.StateTreeVersion5 {
		if act.Address != nil {
			// If there _is_ an f4 address, return it as "key" address
			return *act.Address, nil
		}
	}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, it's a cheap operation anyway.

@hanabi1224 hanabi1224 added this pull request to the merge queue Nov 23, 2023
Merged via the queue into main with commit dffa22c Nov 23, 2023
36 checks passed
@hanabi1224 hanabi1224 deleted the hm/state-account-key branch November 23, 2023 10:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants