From 1d6db306ece8576b98870d6440524259dfc3996d Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Sat, 11 Sep 2021 22:24:53 -0400 Subject: [PATCH] Randomness: Move getters from ChainAPI to StateAPI --- api/api_full.go | 11 +-- api/mocks/mock_full.go | 60 +++++------ api/proxy_gen.go | 52 +++++----- api/v0api/full.go | 5 + api/v0api/proxy_gen.go | 26 +++++ api/v0api/v0mocks/mock_full.go | 30 ++++++ api/v0api/v1_wrapper.go | 10 ++ api/version.go | 2 +- build/openrpc/full.json.gz | Bin 25416 -> 25412 bytes chain/gen/gen.go | 12 +-- chain/sync_test.go | 6 +- chain/vm/runtime.go | 8 +- documentation/en/api-v0-methods.md | 52 ++++++++++ documentation/en/api-v1-unstable-methods.md | 104 ++++++++++---------- extern/storage-sealing/checks.go | 2 +- extern/storage-sealing/mocks/api.go | 60 +++++------ extern/storage-sealing/sealing.go | 4 +- extern/storage-sealing/states_sealing.go | 4 +- itests/wdpost_dispute_test.go | 6 +- node/impl/full/chain.go | 30 ------ node/impl/full/state.go | 33 +++++++ storage/adapter_storage_miner.go | 8 +- storage/miner.go | 4 +- storage/wdpost_run.go | 6 +- storage/wdpost_run_test.go | 4 +- 25 files changed, 334 insertions(+), 205 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 0649ececf..b04180e19 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -72,12 +72,6 @@ type FullNode interface { // ChainHead returns the current head of the chain. ChainHead(context.Context) (*types.TipSet, error) //perm:read - // ChainGetRandomnessFromTickets is used to sample the chain for randomness. - ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) //perm:read - - // ChainGetRandomnessFromBeacon is used to sample the beacon for randomness. - ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) //perm:read - // ChainGetBlock returns the block specified by the given CID. ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error) //perm:read // ChainGetTipSet returns the tipset specified by the given TipSetKey. @@ -591,6 +585,11 @@ type FullNode interface { // StateNetworkVersion returns the network version at the given tipset StateNetworkVersion(context.Context, types.TipSetKey) (apitypes.NetworkVersion, error) //perm:read + // StateGetRandomnessFromTickets is used to sample the chain for randomness. + StateGetRandomnessFromTickets(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) //perm:read + // StateGetRandomnessFromBeacon is used to sample the beacon for randomness. + StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) //perm:read + // MethodGroup: Msig // The Msig methods are used to interact with multisig wallets on the // filecoin network diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 7e3818401..a6781b0b7 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -300,36 +300,6 @@ func (mr *MockFullNodeMockRecorder) ChainGetPath(arg0, arg1, arg2 interface{}) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainGetPath", reflect.TypeOf((*MockFullNode)(nil).ChainGetPath), arg0, arg1, arg2) } -// ChainGetRandomnessFromBeacon mocks base method. -func (m *MockFullNode) ChainGetRandomnessFromBeacon(arg0 context.Context, arg1 types.TipSetKey, arg2 crypto.DomainSeparationTag, arg3 abi.ChainEpoch, arg4 []byte) (abi.Randomness, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ChainGetRandomnessFromBeacon", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(abi.Randomness) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ChainGetRandomnessFromBeacon indicates an expected call of ChainGetRandomnessFromBeacon. -func (mr *MockFullNodeMockRecorder) ChainGetRandomnessFromBeacon(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainGetRandomnessFromBeacon", reflect.TypeOf((*MockFullNode)(nil).ChainGetRandomnessFromBeacon), arg0, arg1, arg2, arg3, arg4) -} - -// ChainGetRandomnessFromTickets mocks base method. -func (m *MockFullNode) ChainGetRandomnessFromTickets(arg0 context.Context, arg1 types.TipSetKey, arg2 crypto.DomainSeparationTag, arg3 abi.ChainEpoch, arg4 []byte) (abi.Randomness, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ChainGetRandomnessFromTickets", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(abi.Randomness) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ChainGetRandomnessFromTickets indicates an expected call of ChainGetRandomnessFromTickets. -func (mr *MockFullNodeMockRecorder) ChainGetRandomnessFromTickets(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainGetRandomnessFromTickets", reflect.TypeOf((*MockFullNode)(nil).ChainGetRandomnessFromTickets), arg0, arg1, arg2, arg3, arg4) -} - // ChainGetTipSet mocks base method. func (m *MockFullNode) ChainGetTipSet(arg0 context.Context, arg1 types.TipSetKey) (*types.TipSet, error) { m.ctrl.T.Helper() @@ -2291,6 +2261,36 @@ func (mr *MockFullNodeMockRecorder) StateGetActor(arg0, arg1, arg2 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetActor", reflect.TypeOf((*MockFullNode)(nil).StateGetActor), arg0, arg1, arg2) } +// StateGetRandomnessFromBeacon mocks base method. +func (m *MockFullNode) StateGetRandomnessFromBeacon(arg0 context.Context, arg1 crypto.DomainSeparationTag, arg2 abi.ChainEpoch, arg3 []byte, arg4 types.TipSetKey) (abi.Randomness, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetRandomnessFromBeacon", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(abi.Randomness) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetRandomnessFromBeacon indicates an expected call of StateGetRandomnessFromBeacon. +func (mr *MockFullNodeMockRecorder) StateGetRandomnessFromBeacon(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetRandomnessFromBeacon", reflect.TypeOf((*MockFullNode)(nil).StateGetRandomnessFromBeacon), arg0, arg1, arg2, arg3, arg4) +} + +// StateGetRandomnessFromTickets mocks base method. +func (m *MockFullNode) StateGetRandomnessFromTickets(arg0 context.Context, arg1 crypto.DomainSeparationTag, arg2 abi.ChainEpoch, arg3 []byte, arg4 types.TipSetKey) (abi.Randomness, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetRandomnessFromTickets", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(abi.Randomness) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetRandomnessFromTickets indicates an expected call of StateGetRandomnessFromTickets. +func (mr *MockFullNodeMockRecorder) StateGetRandomnessFromTickets(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetRandomnessFromTickets", reflect.TypeOf((*MockFullNode)(nil).StateGetRandomnessFromTickets), arg0, arg1, arg2, arg3, arg4) +} + // StateListActors mocks base method. func (m *MockFullNode) StateListActors(arg0 context.Context, arg1 types.TipSetKey) ([]address.Address, error) { m.ctrl.T.Helper() diff --git a/api/proxy_gen.go b/api/proxy_gen.go index c03b83531..7a504cf77 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -130,10 +130,6 @@ type FullNodeStruct struct { ChainGetPath func(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*HeadChange, error) `perm:"read"` - ChainGetRandomnessFromBeacon func(p0 context.Context, p1 types.TipSetKey, p2 crypto.DomainSeparationTag, p3 abi.ChainEpoch, p4 []byte) (abi.Randomness, error) `perm:"read"` - - ChainGetRandomnessFromTickets func(p0 context.Context, p1 types.TipSetKey, p2 crypto.DomainSeparationTag, p3 abi.ChainEpoch, p4 []byte) (abi.Randomness, error) `perm:"read"` - ChainGetTipSet func(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) `perm:"read"` ChainGetTipSetAfterHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `perm:"read"` @@ -350,6 +346,10 @@ type FullNodeStruct struct { StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `perm:"read"` + StateGetRandomnessFromBeacon func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` + + StateGetRandomnessFromTickets func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` + StateListActors func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"` StateListMessages func(p0 context.Context, p1 *MessageMatch, p2 types.TipSetKey, p3 abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"` @@ -1171,28 +1171,6 @@ func (s *FullNodeStub) ChainGetPath(p0 context.Context, p1 types.TipSetKey, p2 t return *new([]*HeadChange), ErrNotSupported } -func (s *FullNodeStruct) ChainGetRandomnessFromBeacon(p0 context.Context, p1 types.TipSetKey, p2 crypto.DomainSeparationTag, p3 abi.ChainEpoch, p4 []byte) (abi.Randomness, error) { - if s.Internal.ChainGetRandomnessFromBeacon == nil { - return *new(abi.Randomness), ErrNotSupported - } - return s.Internal.ChainGetRandomnessFromBeacon(p0, p1, p2, p3, p4) -} - -func (s *FullNodeStub) ChainGetRandomnessFromBeacon(p0 context.Context, p1 types.TipSetKey, p2 crypto.DomainSeparationTag, p3 abi.ChainEpoch, p4 []byte) (abi.Randomness, error) { - return *new(abi.Randomness), ErrNotSupported -} - -func (s *FullNodeStruct) ChainGetRandomnessFromTickets(p0 context.Context, p1 types.TipSetKey, p2 crypto.DomainSeparationTag, p3 abi.ChainEpoch, p4 []byte) (abi.Randomness, error) { - if s.Internal.ChainGetRandomnessFromTickets == nil { - return *new(abi.Randomness), ErrNotSupported - } - return s.Internal.ChainGetRandomnessFromTickets(p0, p1, p2, p3, p4) -} - -func (s *FullNodeStub) ChainGetRandomnessFromTickets(p0 context.Context, p1 types.TipSetKey, p2 crypto.DomainSeparationTag, p3 abi.ChainEpoch, p4 []byte) (abi.Randomness, error) { - return *new(abi.Randomness), ErrNotSupported -} - func (s *FullNodeStruct) ChainGetTipSet(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) { if s.Internal.ChainGetTipSet == nil { return nil, ErrNotSupported @@ -2381,6 +2359,28 @@ func (s *FullNodeStub) StateGetActor(p0 context.Context, p1 address.Address, p2 return nil, ErrNotSupported } +func (s *FullNodeStruct) StateGetRandomnessFromBeacon(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) { + if s.Internal.StateGetRandomnessFromBeacon == nil { + return *new(abi.Randomness), ErrNotSupported + } + return s.Internal.StateGetRandomnessFromBeacon(p0, p1, p2, p3, p4) +} + +func (s *FullNodeStub) StateGetRandomnessFromBeacon(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) { + return *new(abi.Randomness), ErrNotSupported +} + +func (s *FullNodeStruct) StateGetRandomnessFromTickets(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) { + if s.Internal.StateGetRandomnessFromTickets == nil { + return *new(abi.Randomness), ErrNotSupported + } + return s.Internal.StateGetRandomnessFromTickets(p0, p1, p2, p3, p4) +} + +func (s *FullNodeStub) StateGetRandomnessFromTickets(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) { + return *new(abi.Randomness), ErrNotSupported +} + func (s *FullNodeStruct) StateListActors(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) { if s.Internal.StateListActors == nil { return *new([]address.Address), ErrNotSupported diff --git a/api/v0api/full.go b/api/v0api/full.go index a22406fe1..d7e38ce97 100644 --- a/api/v0api/full.go +++ b/api/v0api/full.go @@ -598,6 +598,11 @@ type FullNode interface { // StateNetworkVersion returns the network version at the given tipset StateNetworkVersion(context.Context, types.TipSetKey) (apitypes.NetworkVersion, error) //perm:read + // StateGetRandomnessFromTickets is used to sample the chain for randomness. + StateGetRandomnessFromTickets(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) //perm:read + // StateGetRandomnessFromBeacon is used to sample the beacon for randomness. + StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) //perm:read + // MethodGroup: Msig // The Msig methods are used to interact with multisig wallets on the // filecoin network diff --git a/api/v0api/proxy_gen.go b/api/v0api/proxy_gen.go index eec8577fc..dd6330a02 100644 --- a/api/v0api/proxy_gen.go +++ b/api/v0api/proxy_gen.go @@ -267,6 +267,10 @@ type FullNodeStruct struct { StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `perm:"read"` + StateGetRandomnessFromBeacon func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` + + StateGetRandomnessFromTickets func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` + StateGetReceipt func(p0 context.Context, p1 cid.Cid, p2 types.TipSetKey) (*types.MessageReceipt, error) `perm:"read"` StateListActors func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"` @@ -1742,6 +1746,28 @@ func (s *FullNodeStub) StateGetActor(p0 context.Context, p1 address.Address, p2 return nil, ErrNotSupported } +func (s *FullNodeStruct) StateGetRandomnessFromBeacon(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) { + if s.Internal.StateGetRandomnessFromBeacon == nil { + return *new(abi.Randomness), ErrNotSupported + } + return s.Internal.StateGetRandomnessFromBeacon(p0, p1, p2, p3, p4) +} + +func (s *FullNodeStub) StateGetRandomnessFromBeacon(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) { + return *new(abi.Randomness), ErrNotSupported +} + +func (s *FullNodeStruct) StateGetRandomnessFromTickets(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) { + if s.Internal.StateGetRandomnessFromTickets == nil { + return *new(abi.Randomness), ErrNotSupported + } + return s.Internal.StateGetRandomnessFromTickets(p0, p1, p2, p3, p4) +} + +func (s *FullNodeStub) StateGetRandomnessFromTickets(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) { + return *new(abi.Randomness), ErrNotSupported +} + func (s *FullNodeStruct) StateGetReceipt(p0 context.Context, p1 cid.Cid, p2 types.TipSetKey) (*types.MessageReceipt, error) { if s.Internal.StateGetReceipt == nil { return nil, ErrNotSupported diff --git a/api/v0api/v0mocks/mock_full.go b/api/v0api/v0mocks/mock_full.go index 97ca4e7fe..0344eebf3 100644 --- a/api/v0api/v0mocks/mock_full.go +++ b/api/v0api/v0mocks/mock_full.go @@ -2171,6 +2171,36 @@ func (mr *MockFullNodeMockRecorder) StateGetActor(arg0, arg1, arg2 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetActor", reflect.TypeOf((*MockFullNode)(nil).StateGetActor), arg0, arg1, arg2) } +// StateGetRandomnessFromBeacon mocks base method. +func (m *MockFullNode) StateGetRandomnessFromBeacon(arg0 context.Context, arg1 crypto.DomainSeparationTag, arg2 abi.ChainEpoch, arg3 []byte, arg4 types.TipSetKey) (abi.Randomness, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetRandomnessFromBeacon", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(abi.Randomness) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetRandomnessFromBeacon indicates an expected call of StateGetRandomnessFromBeacon. +func (mr *MockFullNodeMockRecorder) StateGetRandomnessFromBeacon(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetRandomnessFromBeacon", reflect.TypeOf((*MockFullNode)(nil).StateGetRandomnessFromBeacon), arg0, arg1, arg2, arg3, arg4) +} + +// StateGetRandomnessFromTickets mocks base method. +func (m *MockFullNode) StateGetRandomnessFromTickets(arg0 context.Context, arg1 crypto.DomainSeparationTag, arg2 abi.ChainEpoch, arg3 []byte, arg4 types.TipSetKey) (abi.Randomness, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetRandomnessFromTickets", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(abi.Randomness) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetRandomnessFromTickets indicates an expected call of StateGetRandomnessFromTickets. +func (mr *MockFullNodeMockRecorder) StateGetRandomnessFromTickets(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetRandomnessFromTickets", reflect.TypeOf((*MockFullNode)(nil).StateGetRandomnessFromTickets), arg0, arg1, arg2, arg3, arg4) +} + // StateGetReceipt mocks base method. func (m *MockFullNode) StateGetReceipt(arg0 context.Context, arg1 cid.Cid, arg2 types.TipSetKey) (*types.MessageReceipt, error) { m.ctrl.T.Helper() diff --git a/api/v0api/v1_wrapper.go b/api/v0api/v1_wrapper.go index ff4474fe5..7f7291600 100644 --- a/api/v0api/v1_wrapper.go +++ b/api/v0api/v1_wrapper.go @@ -3,6 +3,8 @@ package v0api import ( "context" + "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/chain/types" "golang.org/x/xerrors" @@ -184,4 +186,12 @@ func (w *WrapperV1Full) MsigRemoveSigner(ctx context.Context, msig address.Addre return w.executePrototype(ctx, p) } +func (w *WrapperV1Full) ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { + return w.StateGetRandomnessFromTickets(ctx, personalization, randEpoch, entropy, tsk) +} + +func (w *WrapperV1Full) ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { + return w.StateGetRandomnessFromBeacon(ctx, personalization, randEpoch, entropy, tsk) +} + var _ FullNode = &WrapperV1Full{} diff --git a/api/version.go b/api/version.go index 687f5135a..2c87fe0a4 100644 --- a/api/version.go +++ b/api/version.go @@ -54,7 +54,7 @@ func VersionForType(nodeType NodeType) (Version, error) { // semver versions of the rpc api exposed var ( - FullAPIVersion0 = newVer(1, 3, 0) + FullAPIVersion0 = newVer(1, 4, 0) FullAPIVersion1 = newVer(2, 1, 0) MinerAPIVersion0 = newVer(1, 2, 0) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index d0409cc040900badfaede9d07260618de119cf0e..06a131c8e0ae318768080cf3858c76636917db9d 100644 GIT binary patch literal 25412 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ȋITP{*`dm^;^+s)>02`|mEhXRiAT$#KYd%%+ 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 AoB#j- literal 25416 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 I2m|qd08zYU%K!iX diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 128421c18..6f111a69e 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -570,8 +570,8 @@ func (cg *ChainGen) YieldRepo() (repo.Repo, error) { } type MiningCheckAPI interface { - ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) - ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) + StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) + StateGetRandomnessFromTickets(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) MinerGetBaseInfo(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*api.MiningBaseInfo, error) @@ -585,26 +585,26 @@ type mca struct { bcn beacon.Schedule } -func (mca mca) ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { +func (mca mca) StateGetRandomnessFromTickets(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) { pts, err := mca.sm.ChainStore().LoadTipSet(tsk) if err != nil { return nil, xerrors.Errorf("loading tipset key: %w", err) } - if randEpoch > build.UpgradeHyperdriveHeight { + if mca.sm.GetNtwkVersion(ctx, randEpoch) >= network.Version13 { return mca.sm.ChainStore().GetChainRandomnessLookingForward(ctx, pts.Cids(), personalization, randEpoch, entropy) } return mca.sm.ChainStore().GetChainRandomnessLookingBack(ctx, pts.Cids(), personalization, randEpoch, entropy) } -func (mca mca) ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { +func (mca mca) StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) { pts, err := mca.sm.ChainStore().LoadTipSet(tsk) if err != nil { return nil, xerrors.Errorf("loading tipset key: %w", err) } - if randEpoch > build.UpgradeHyperdriveHeight { + if mca.sm.GetNtwkVersion(ctx, randEpoch) >= network.Version13 { return mca.sm.ChainStore().GetBeaconRandomnessLookingForward(ctx, pts.Cids(), personalization, randEpoch, entropy) } diff --git a/chain/sync_test.go b/chain/sync_test.go index 32b2184de..bf84512cf 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -1057,7 +1057,7 @@ func TestDrandNull(t *testing.T) { t.Fatal("didn't inject nulls as expected") } - rand, err := tu.nds[p0].ChainGetRandomnessFromBeacon(tu.ctx, afterNull.TipSet().Key(), pers, nullHeight, entropy) + rand, err := tu.nds[p0].StateGetRandomnessFromBeacon(tu.ctx, pers, nullHeight, entropy, afterNull.TipSet().Key()) require.NoError(t, err) // calculate the expected randomness based on the beacon BEFORE the null @@ -1081,7 +1081,7 @@ func TestDrandNull(t *testing.T) { t.Fatal("didn't inject nulls as expected") } - rand0, err := tu.nds[p0].ChainGetRandomnessFromBeacon(tu.ctx, afterNull.TipSet().Key(), pers, nullHeight, entropy) + rand0, err := tu.nds[p0].StateGetRandomnessFromBeacon(tu.ctx, pers, nullHeight, entropy, afterNull.TipSet().Key()) require.NoError(t, err) // calculate the expected randomness based on the beacon AFTER the null @@ -1101,7 +1101,7 @@ func TestDrandNull(t *testing.T) { require.Equal(tu.t, p1Head.Key(), afterNull.TipSet().Key()) // Yes, p1 sources the same randomness as p0 - rand1, err := tu.nds[p1].ChainGetRandomnessFromBeacon(tu.ctx, afterNull.TipSet().Key(), pers, nullHeight, entropy) + rand1, err := tu.nds[p1].StateGetRandomnessFromBeacon(tu.ctx, pers, nullHeight, entropy, afterNull.TipSet().Key()) require.NoError(t, err) require.Equal(t, rand0, rand1) diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index 7117f3443..c0ace4907 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -214,7 +214,9 @@ func (rt *Runtime) GetActorCodeCID(addr address.Address) (ret cid.Cid, ok bool) func (rt *Runtime) GetRandomnessFromTickets(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness { var err error var res []byte - if randEpoch > build.UpgradeHyperdriveHeight { + + rnv := rt.vm.ntwkVersion(rt.ctx, randEpoch) + if rnv >= network.Version13 { res, err = rt.vm.rand.GetChainRandomnessLookingForward(rt.ctx, personalization, randEpoch, entropy) } else { res, err = rt.vm.rand.GetChainRandomnessLookingBack(rt.ctx, personalization, randEpoch, entropy) @@ -229,7 +231,9 @@ func (rt *Runtime) GetRandomnessFromTickets(personalization crypto.DomainSeparat func (rt *Runtime) GetRandomnessFromBeacon(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness { var err error var res []byte - if randEpoch > build.UpgradeHyperdriveHeight { + + rnv := rt.vm.ntwkVersion(rt.ctx, randEpoch) + if rnv >= network.Version13 { res, err = rt.vm.rand.GetBeaconRandomnessLookingForward(rt.ctx, personalization, randEpoch, entropy) } else { res, err = rt.vm.rand.GetBeaconRandomnessLookingBack(rt.ctx, personalization, randEpoch, entropy) diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index f5907f494..a3693261a 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -158,6 +158,8 @@ * [StateDealProviderCollateralBounds](#StateDealProviderCollateralBounds) * [StateDecodeParams](#StateDecodeParams) * [StateGetActor](#StateGetActor) + * [StateGetRandomnessFromBeacon](#StateGetRandomnessFromBeacon) + * [StateGetRandomnessFromTickets](#StateGetRandomnessFromTickets) * [StateGetReceipt](#StateGetReceipt) * [StateListActors](#StateListActors) * [StateListMessages](#StateListMessages) @@ -3907,6 +3909,56 @@ Response: } ``` +### StateGetRandomnessFromBeacon +StateGetRandomnessFromBeacon is used to sample the beacon for randomness. + + +Perms: read + +Inputs: +```json +[ + 2, + 10101, + "Ynl0ZSBhcnJheQ==", + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `null` + +### StateGetRandomnessFromTickets +StateGetRandomnessFromTickets is used to sample the chain for randomness. + + +Perms: read + +Inputs: +```json +[ + 2, + 10101, + "Ynl0ZSBhcnJheQ==", + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `null` + ### StateGetReceipt StateGetReceipt returns the message receipt for the given message or for a matching gas-repriced replacing message diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index e77e0c7bf..f03203314 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -24,8 +24,6 @@ * [ChainGetParentMessages](#ChainGetParentMessages) * [ChainGetParentReceipts](#ChainGetParentReceipts) * [ChainGetPath](#ChainGetPath) - * [ChainGetRandomnessFromBeacon](#ChainGetRandomnessFromBeacon) - * [ChainGetRandomnessFromTickets](#ChainGetRandomnessFromTickets) * [ChainGetTipSet](#ChainGetTipSet) * [ChainGetTipSetAfterHeight](#ChainGetTipSetAfterHeight) * [ChainGetTipSetByHeight](#ChainGetTipSetByHeight) @@ -167,6 +165,8 @@ * [StateDecodeParams](#StateDecodeParams) * [StateEncodeParams](#StateEncodeParams) * [StateGetActor](#StateGetActor) + * [StateGetRandomnessFromBeacon](#StateGetRandomnessFromBeacon) + * [StateGetRandomnessFromTickets](#StateGetRandomnessFromTickets) * [StateListActors](#StateListActors) * [StateListMessages](#StateListMessages) * [StateListMiners](#StateListMiners) @@ -690,56 +690,6 @@ Inputs: Response: `null` -### ChainGetRandomnessFromBeacon -ChainGetRandomnessFromBeacon is used to sample the beacon for randomness. - - -Perms: read - -Inputs: -```json -[ - [ - { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - }, - { - "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" - } - ], - 2, - 10101, - "Ynl0ZSBhcnJheQ==" -] -``` - -Response: `null` - -### ChainGetRandomnessFromTickets -ChainGetRandomnessFromTickets is used to sample the chain for randomness. - - -Perms: read - -Inputs: -```json -[ - [ - { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - }, - { - "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" - } - ], - 2, - 10101, - "Ynl0ZSBhcnJheQ==" -] -``` - -Response: `null` - ### ChainGetTipSet ChainGetTipSet returns the tipset specified by the given TipSetKey. @@ -4249,6 +4199,56 @@ Response: } ``` +### StateGetRandomnessFromBeacon +StateGetRandomnessFromBeacon is used to sample the beacon for randomness. + + +Perms: read + +Inputs: +```json +[ + 2, + 10101, + "Ynl0ZSBhcnJheQ==", + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `null` + +### StateGetRandomnessFromTickets +StateGetRandomnessFromTickets is used to sample the chain for randomness. + + +Perms: read + +Inputs: +```json +[ + 2, + 10101, + "Ynl0ZSBhcnJheQ==", + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `null` + ### StateListActors StateListActors returns the addresses of every actor in the state diff --git a/extern/storage-sealing/checks.go b/extern/storage-sealing/checks.go index b01f746ba..74a791fcb 100644 --- a/extern/storage-sealing/checks.go +++ b/extern/storage-sealing/checks.go @@ -152,7 +152,7 @@ func (m *Sealing) checkCommit(ctx context.Context, si SectorInfo, proof []byte, return err } - seed, err := m.Api.ChainGetRandomnessFromBeacon(ctx, tok, crypto.DomainSeparationTag_InteractiveSealChallengeSeed, si.SeedEpoch, buf.Bytes()) + seed, err := m.Api.StateGetRandomnessFromBeacon(ctx, crypto.DomainSeparationTag_InteractiveSealChallengeSeed, si.SeedEpoch, buf.Bytes(), tok) if err != nil { return &ErrApi{xerrors.Errorf("failed to get randomness for computing seal proof: %w", err)} } diff --git a/extern/storage-sealing/mocks/api.go b/extern/storage-sealing/mocks/api.go index d2962c56d..cc8561dc7 100644 --- a/extern/storage-sealing/mocks/api.go +++ b/extern/storage-sealing/mocks/api.go @@ -77,36 +77,6 @@ func (mr *MockSealingAPIMockRecorder) ChainGetMessage(arg0, arg1 interface{}) *g return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainGetMessage", reflect.TypeOf((*MockSealingAPI)(nil).ChainGetMessage), arg0, arg1) } -// ChainGetRandomnessFromBeacon mocks base method. -func (m *MockSealingAPI) ChainGetRandomnessFromBeacon(arg0 context.Context, arg1 sealing.TipSetToken, arg2 crypto.DomainSeparationTag, arg3 abi.ChainEpoch, arg4 []byte) (abi.Randomness, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ChainGetRandomnessFromBeacon", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(abi.Randomness) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ChainGetRandomnessFromBeacon indicates an expected call of ChainGetRandomnessFromBeacon. -func (mr *MockSealingAPIMockRecorder) ChainGetRandomnessFromBeacon(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainGetRandomnessFromBeacon", reflect.TypeOf((*MockSealingAPI)(nil).ChainGetRandomnessFromBeacon), arg0, arg1, arg2, arg3, arg4) -} - -// ChainGetRandomnessFromTickets mocks base method. -func (m *MockSealingAPI) ChainGetRandomnessFromTickets(arg0 context.Context, arg1 sealing.TipSetToken, arg2 crypto.DomainSeparationTag, arg3 abi.ChainEpoch, arg4 []byte) (abi.Randomness, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ChainGetRandomnessFromTickets", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(abi.Randomness) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ChainGetRandomnessFromTickets indicates an expected call of ChainGetRandomnessFromTickets. -func (mr *MockSealingAPIMockRecorder) ChainGetRandomnessFromTickets(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainGetRandomnessFromTickets", reflect.TypeOf((*MockSealingAPI)(nil).ChainGetRandomnessFromTickets), arg0, arg1, arg2, arg3, arg4) -} - // ChainHead mocks base method. func (m *MockSealingAPI) ChainHead(arg0 context.Context) (sealing.TipSetToken, abi.ChainEpoch, error) { m.ctrl.T.Helper() @@ -168,6 +138,36 @@ func (mr *MockSealingAPIMockRecorder) StateComputeDataCommitment(arg0, arg1, arg return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateComputeDataCommitment", reflect.TypeOf((*MockSealingAPI)(nil).StateComputeDataCommitment), arg0, arg1, arg2, arg3, arg4) } +// StateGetRandomnessFromBeacon mocks base method. +func (m *MockSealingAPI) StateGetRandomnessFromBeacon(arg0 context.Context, arg1 crypto.DomainSeparationTag, arg2 abi.ChainEpoch, arg3 []byte, arg4 sealing.TipSetToken) (abi.Randomness, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetRandomnessFromBeacon", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(abi.Randomness) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetRandomnessFromBeacon indicates an expected call of StateGetRandomnessFromBeacon. +func (mr *MockSealingAPIMockRecorder) StateGetRandomnessFromBeacon(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetRandomnessFromBeacon", reflect.TypeOf((*MockSealingAPI)(nil).StateGetRandomnessFromBeacon), arg0, arg1, arg2, arg3, arg4) +} + +// StateGetRandomnessFromTickets mocks base method. +func (m *MockSealingAPI) StateGetRandomnessFromTickets(arg0 context.Context, arg1 crypto.DomainSeparationTag, arg2 abi.ChainEpoch, arg3 []byte, arg4 sealing.TipSetToken) (abi.Randomness, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetRandomnessFromTickets", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(abi.Randomness) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetRandomnessFromTickets indicates an expected call of StateGetRandomnessFromTickets. +func (mr *MockSealingAPIMockRecorder) StateGetRandomnessFromTickets(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetRandomnessFromTickets", reflect.TypeOf((*MockSealingAPI)(nil).StateGetRandomnessFromTickets), arg0, arg1, arg2, arg3, arg4) +} + // StateLookupID mocks base method. func (m *MockSealingAPI) StateLookupID(arg0 context.Context, arg1 address.Address, arg2 sealing.TipSetToken) (address.Address, error) { m.ctrl.T.Helper() diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index 3defdd830..165003bc9 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -72,8 +72,8 @@ type SealingAPI interface { ChainHead(ctx context.Context) (TipSetToken, abi.ChainEpoch, error) ChainBaseFee(context.Context, TipSetToken) (abi.TokenAmount, error) ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.Message, error) - ChainGetRandomnessFromBeacon(ctx context.Context, tok TipSetToken, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) - ChainGetRandomnessFromTickets(ctx context.Context, tok TipSetToken, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) + StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tok TipSetToken) (abi.Randomness, error) + StateGetRandomnessFromTickets(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tok TipSetToken) (abi.Randomness, error) ChainReadObj(context.Context, cid.Cid) ([]byte, error) } diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 85e190c73..9dcb779a7 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -167,7 +167,7 @@ func (m *Sealing) getTicket(ctx statemachine.Context, sector SectorInfo) (abi.Se return nil, 0, allocated, xerrors.Errorf("sector %s precommitted but expired", sector.SectorNumber) } - rand, err := m.Api.ChainGetRandomnessFromTickets(ctx.Context(), tok, crypto.DomainSeparationTag_SealRandomness, ticketEpoch, buf.Bytes()) + rand, err := m.Api.StateGetRandomnessFromTickets(ctx.Context(), crypto.DomainSeparationTag_SealRandomness, ticketEpoch, buf.Bytes(), tok) if err != nil { return nil, 0, allocated, err } @@ -522,7 +522,7 @@ func (m *Sealing) handleWaitSeed(ctx statemachine.Context, sector SectorInfo) er if err := m.maddr.MarshalCBOR(buf); err != nil { return err } - rand, err := m.Api.ChainGetRandomnessFromBeacon(ectx, tok, crypto.DomainSeparationTag_InteractiveSealChallengeSeed, randHeight, buf.Bytes()) + rand, err := m.Api.StateGetRandomnessFromBeacon(ectx, crypto.DomainSeparationTag_InteractiveSealChallengeSeed, randHeight, buf.Bytes(), tok) if err != nil { err = xerrors.Errorf("failed to get randomness for computing seal proof (ch %d; rh %d; tsk %x): %w", curH, randHeight, tok, err) diff --git a/itests/wdpost_dispute_test.go b/itests/wdpost_dispute_test.go index 39bf38bf7..aa892aca7 100644 --- a/itests/wdpost_dispute_test.go +++ b/itests/wdpost_dispute_test.go @@ -320,9 +320,9 @@ func submitBadProof( } commEpoch := di.Open - commRand, err := client.ChainGetRandomnessFromTickets( - ctx, head.Key(), crypto.DomainSeparationTag_PoStChainCommit, - commEpoch, nil, + commRand, err := client.StateGetRandomnessFromTickets( + ctx, crypto.DomainSeparationTag_PoStChainCommit, + commEpoch, nil, head.Key(), ) if err != nil { return err diff --git a/node/impl/full/chain.go b/node/impl/full/chain.go index a37e4e990..e8d403337 100644 --- a/node/impl/full/chain.go +++ b/node/impl/full/chain.go @@ -10,7 +10,6 @@ import ( "strings" "sync" - "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/stmgr" "go.uber.org/fx" @@ -30,7 +29,6 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/specs-actors/actors/util/adt" "github.com/filecoin-project/lotus/api" @@ -99,34 +97,6 @@ func (m *ChainModule) ChainHead(context.Context) (*types.TipSet, error) { return m.Chain.GetHeaviestTipSet(), nil } -func (a *ChainAPI) ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { - pts, err := a.Chain.LoadTipSet(tsk) - if err != nil { - return nil, xerrors.Errorf("loading tipset key: %w", err) - } - - // Doing this here is slightly nicer than doing it in the chainstore directly, but it's still bad for ChainAPI to reason about network upgrades - if randEpoch > build.UpgradeHyperdriveHeight { - return a.Chain.GetChainRandomnessLookingForward(ctx, pts.Cids(), personalization, randEpoch, entropy) - } - - return a.Chain.GetChainRandomnessLookingBack(ctx, pts.Cids(), personalization, randEpoch, entropy) -} - -func (a *ChainAPI) ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { - pts, err := a.Chain.LoadTipSet(tsk) - if err != nil { - return nil, xerrors.Errorf("loading tipset key: %w", err) - } - - // Doing this here is slightly nicer than doing it in the chainstore directly, but it's still bad for ChainAPI to reason about network upgrades - if randEpoch > build.UpgradeHyperdriveHeight { - return a.Chain.GetBeaconRandomnessLookingForward(ctx, pts.Cids(), personalization, randEpoch, entropy) - } - - return a.Chain.GetBeaconRandomnessLookingBack(ctx, pts.Cids(), personalization, randEpoch, entropy) -} - func (a *ChainAPI) ChainGetBlock(ctx context.Context, msg cid.Cid) (*types.BlockHeader, error) { return a.Chain.GetBlock(msg) } diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 4b902565e..2981c0162 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -6,6 +6,8 @@ import ( "encoding/json" "strconv" + "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/go-state-types/cbor" cid "github.com/ipfs/go-cid" "go.uber.org/fx" @@ -1418,3 +1420,34 @@ func (m *StateModule) StateNetworkVersion(ctx context.Context, tsk types.TipSetK // But that's likely going to break a bunch of stuff. return m.StateManager.GetNtwkVersion(ctx, ts.Height()), nil } + +func (a *StateAPI) StateGetRandomnessFromTickets(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) { + pts, err := a.Chain.LoadTipSet(tsk) + if err != nil { + return nil, xerrors.Errorf("loading tipset key: %w", err) + } + + rnv := a.StateManager.GetNtwkVersion(ctx, randEpoch) + + if rnv >= network.Version13 { + return a.Chain.GetChainRandomnessLookingForward(ctx, pts.Cids(), personalization, randEpoch, entropy) + } + + return a.Chain.GetChainRandomnessLookingBack(ctx, pts.Cids(), personalization, randEpoch, entropy) +} + +func (a *StateAPI) StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) { + pts, err := a.Chain.GetTipSetFromKey(tsk) + if err != nil { + return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) + } + + rnv := a.StateManager.GetNtwkVersion(ctx, randEpoch) + + if rnv >= network.Version13 { + return a.Chain.GetBeaconRandomnessLookingForward(ctx, pts.Cids(), personalization, randEpoch, entropy) + } + + return a.Chain.GetBeaconRandomnessLookingBack(ctx, pts.Cids(), personalization, randEpoch, entropy) + +} diff --git a/storage/adapter_storage_miner.go b/storage/adapter_storage_miner.go index 531fe2d03..0b4b17f96 100644 --- a/storage/adapter_storage_miner.go +++ b/storage/adapter_storage_miner.go @@ -387,22 +387,22 @@ func (s SealingAPIAdapter) ChainGetMessage(ctx context.Context, mc cid.Cid) (*ty return s.delegate.ChainGetMessage(ctx, mc) } -func (s SealingAPIAdapter) ChainGetRandomnessFromBeacon(ctx context.Context, tok sealing.TipSetToken, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { +func (s SealingAPIAdapter) StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tok sealing.TipSetToken) (abi.Randomness, error) { tsk, err := types.TipSetKeyFromBytes(tok) if err != nil { return nil, err } - return s.delegate.ChainGetRandomnessFromBeacon(ctx, tsk, personalization, randEpoch, entropy) + return s.delegate.StateGetRandomnessFromBeacon(ctx, personalization, randEpoch, entropy, tsk) } -func (s SealingAPIAdapter) ChainGetRandomnessFromTickets(ctx context.Context, tok sealing.TipSetToken, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { +func (s SealingAPIAdapter) StateGetRandomnessFromTickets(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tok sealing.TipSetToken) (abi.Randomness, error) { tsk, err := types.TipSetKeyFromBytes(tok) if err != nil { return nil, err } - return s.delegate.ChainGetRandomnessFromTickets(ctx, tsk, personalization, randEpoch, entropy) + return s.delegate.StateGetRandomnessFromTickets(ctx, personalization, randEpoch, entropy, tsk) } func (s SealingAPIAdapter) ChainReadObj(ctx context.Context, ocid cid.Cid) ([]byte, error) { diff --git a/storage/miner.go b/storage/miner.go index 155f5f30d..0b1f66840 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -110,8 +110,8 @@ type fullNodeFilteredAPI interface { ChainHead(context.Context) (*types.TipSet, error) ChainNotify(context.Context) (<-chan []*api.HeadChange, error) - ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) - ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) + StateGetRandomnessFromTickets(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) + StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) ChainGetTipSetAfterHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error) diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index 82c7dede4..038ed3ac7 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -165,7 +165,7 @@ func (s *WindowPoStScheduler) runSubmitPoST( commEpoch = deadline.Challenge } - commRand, err := s.api.ChainGetRandomnessFromTickets(ctx, ts.Key(), crypto.DomainSeparationTag_PoStChainCommit, commEpoch, nil) + commRand, err := s.api.StateGetRandomnessFromTickets(ctx, crypto.DomainSeparationTag_PoStChainCommit, commEpoch, nil, ts.Key()) if err != nil { err = xerrors.Errorf("failed to get chain randomness from tickets for windowPost (ts=%d; deadline=%d): %w", ts.Height(), commEpoch, err) log.Errorf("submitPoStMessage failed: %+v", err) @@ -523,7 +523,7 @@ func (s *WindowPoStScheduler) runPoStCycle(ctx context.Context, di dline.Info, t return nil, xerrors.Errorf("getting current head: %w", err) } - rand, err := s.api.ChainGetRandomnessFromBeacon(ctx, headTs.Key(), crypto.DomainSeparationTag_WindowedPoStChallengeSeed, di.Challenge, buf.Bytes()) + rand, err := s.api.StateGetRandomnessFromBeacon(ctx, crypto.DomainSeparationTag_WindowedPoStChallengeSeed, di.Challenge, buf.Bytes(), headTs.Key()) if err != nil { return nil, xerrors.Errorf("failed to get chain randomness from beacon for window post (ts=%d; deadline=%d): %w", ts.Height(), di, err) } @@ -652,7 +652,7 @@ func (s *WindowPoStScheduler) runPoStCycle(ctx context.Context, di dline.Info, t return nil, xerrors.Errorf("getting current head: %w", err) } - checkRand, err := s.api.ChainGetRandomnessFromBeacon(ctx, headTs.Key(), crypto.DomainSeparationTag_WindowedPoStChallengeSeed, di.Challenge, buf.Bytes()) + checkRand, err := s.api.StateGetRandomnessFromBeacon(ctx, crypto.DomainSeparationTag_WindowedPoStChallengeSeed, di.Challenge, buf.Bytes(), headTs.Key()) if err != nil { return nil, xerrors.Errorf("failed to get chain randomness from beacon for window post (ts=%d; deadline=%d): %w", ts.Height(), di, err) } diff --git a/storage/wdpost_run_test.go b/storage/wdpost_run_test.go index 61f2a324b..78d9431d4 100644 --- a/storage/wdpost_run_test.go +++ b/storage/wdpost_run_test.go @@ -60,11 +60,11 @@ func (m *mockStorageMinerAPI) StateNetworkVersion(ctx context.Context, key types return build.NewestNetworkVersion, nil } -func (m *mockStorageMinerAPI) ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { +func (m *mockStorageMinerAPI) StateGetRandomnessFromTickets(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) { return abi.Randomness("ticket rand"), nil } -func (m *mockStorageMinerAPI) ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { +func (m *mockStorageMinerAPI) StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) { return abi.Randomness("beacon rand"), nil }