From 05ac9453cd7298e65748b5043af8be1862690382 Mon Sep 17 00:00:00 2001 From: Aayush Date: Sun, 18 Dec 2022 14:14:50 -0500 Subject: [PATCH 1/8] Refactor: Rand: return randomness base without hashing --- chain/gen/genesis/miners.go | 6 ++- chain/rand/rand.go | 73 ++++++++++++++++++++++++------------- chain/stmgr/stmgr.go | 4 +- chain/vm/runtime.go | 26 ++++++++++--- chain/vm/vm.go | 6 --- chain/vm/vmi.go | 7 ++++ 6 files changed, 81 insertions(+), 41 deletions(-) diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index c083f4fda..ebbe16326 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -590,16 +590,18 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal return c, nil } +var _ vm.Rand = new(fakeRand) + // TODO: copied from actors test harness, deduplicate or remove from here type fakeRand struct{} -func (fr *fakeRand) GetChainRandomness(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (fr *fakeRand) GetChainRandomness(ctx context.Context, randEpoch abi.ChainEpoch) ([]byte, error) { out := make([]byte, 32) _, _ = rand.New(rand.NewSource(int64(randEpoch * 1000))).Read(out) //nolint return out, nil } -func (fr *fakeRand) GetBeaconRandomness(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (fr *fakeRand) GetBeaconRandomness(ctx context.Context, randEpoch abi.ChainEpoch) ([]byte, error) { out := make([]byte, 32) _, _ = rand.New(rand.NewSource(int64(randEpoch))).Read(out) //nolint return out, nil diff --git a/chain/rand/rand.go b/chain/rand/rand.go index c35280ab5..dce4231e2 100644 --- a/chain/rand/rand.go +++ b/chain/rand/rand.go @@ -17,7 +17,6 @@ import ( "github.com/filecoin-project/lotus/chain/beacon" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/chain/vm" ) var log = logging.Logger("rand") @@ -70,7 +69,7 @@ func (sr *stateRand) GetBeaconRandomnessTipset(ctx context.Context, round abi.Ch return randTs, nil } -func (sr *stateRand) getChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) { +func (sr *stateRand) getChainRandomness(ctx context.Context, round abi.ChainEpoch, lookback bool) ([]byte, error) { _, span := trace.StartSpan(ctx, "store.GetChainRandomness") defer span.End() span.AddAttributes(trace.Int64Attribute("round", int64(round))) @@ -94,11 +93,7 @@ func (sr *stateRand) getChainRandomness(ctx context.Context, pers crypto.DomainS return nil, err } - mtb := randTs.MinTicketBlock() - - // if at (or just past -- for null epochs) appropriate epoch - // or at genesis (works for negative epochs) - return DrawRandomness(mtb.Ticket.VRFProof, pers, round, entropy) + return randTs.MinTicketBlock().Ticket.VRFProof, nil } type NetworkVersionGetter func(context.Context, abi.ChainEpoch) network.Version @@ -110,7 +105,7 @@ type stateRand struct { networkVersionGetter NetworkVersionGetter } -func NewStateRand(cs *store.ChainStore, blks []cid.Cid, b beacon.Schedule, networkVersionGetter NetworkVersionGetter) vm.Rand { +func NewStateRand(cs *store.ChainStore, blks []cid.Cid, b beacon.Schedule, networkVersionGetter NetworkVersionGetter) *stateRand { return &stateRand{ cs: cs, blks: blks, @@ -120,7 +115,7 @@ func NewStateRand(cs *store.ChainStore, blks []cid.Cid, b beacon.Schedule, netwo } // network v0-12 -func (sr *stateRand) getBeaconRandomnessV1(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (sr *stateRand) getBeaconRandomnessV1(ctx context.Context, round abi.ChainEpoch) ([]byte, error) { randTs, err := sr.GetBeaconRandomnessTipset(ctx, round, true) if err != nil { return nil, err @@ -131,13 +126,11 @@ func (sr *stateRand) getBeaconRandomnessV1(ctx context.Context, pers crypto.Doma return nil, err } - // if at (or just past -- for null epochs) appropriate epoch - // or at genesis (works for negative epochs) - return DrawRandomness(be.Data, pers, round, entropy) + return be.Data, nil } // network v13 -func (sr *stateRand) getBeaconRandomnessV2(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (sr *stateRand) getBeaconRandomnessV2(ctx context.Context, round abi.ChainEpoch) ([]byte, error) { randTs, err := sr.GetBeaconRandomnessTipset(ctx, round, false) if err != nil { return nil, err @@ -148,15 +141,13 @@ func (sr *stateRand) getBeaconRandomnessV2(ctx context.Context, pers crypto.Doma return nil, err } - // if at (or just past -- for null epochs) appropriate epoch - // or at genesis (works for negative epochs) - return DrawRandomness(be.Data, pers, round, entropy) + return be.Data, nil } // network v14 and on -func (sr *stateRand) getBeaconRandomnessV3(ctx context.Context, pers crypto.DomainSeparationTag, filecoinEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (sr *stateRand) getBeaconRandomnessV3(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([]byte, error) { if filecoinEpoch < 0 { - return sr.getBeaconRandomnessV2(ctx, pers, filecoinEpoch, entropy) + return sr.getBeaconRandomnessV2(ctx, filecoinEpoch) } be, err := sr.extractBeaconEntryForEpoch(ctx, filecoinEpoch) @@ -165,31 +156,61 @@ func (sr *stateRand) getBeaconRandomnessV3(ctx context.Context, pers crypto.Doma return nil, err } - return DrawRandomness(be.Data, pers, filecoinEpoch, entropy) + return be.Data, nil } -func (sr *stateRand) GetChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, filecoinEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (sr *stateRand) GetChainRandomness(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([]byte, error) { nv := sr.networkVersionGetter(ctx, filecoinEpoch) if nv >= network.Version13 { - return sr.getChainRandomness(ctx, pers, filecoinEpoch, entropy, false) + return sr.getChainRandomness(ctx, filecoinEpoch, false) } - return sr.getChainRandomness(ctx, pers, filecoinEpoch, entropy, true) + return sr.getChainRandomness(ctx, filecoinEpoch, true) } -func (sr *stateRand) GetBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, filecoinEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (sr *stateRand) GetBeaconRandomness(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([]byte, error) { nv := sr.networkVersionGetter(ctx, filecoinEpoch) if nv >= network.Version14 { - return sr.getBeaconRandomnessV3(ctx, pers, filecoinEpoch, entropy) + return sr.getBeaconRandomnessV3(ctx, filecoinEpoch) } else if nv == network.Version13 { - return sr.getBeaconRandomnessV2(ctx, pers, filecoinEpoch, entropy) + return sr.getBeaconRandomnessV2(ctx, filecoinEpoch) } else { - return sr.getBeaconRandomnessV1(ctx, pers, filecoinEpoch, entropy) + return sr.getBeaconRandomnessV1(ctx, filecoinEpoch) } } +func (sr *stateRand) DrawChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, filecoinEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { + rbase, err := sr.GetChainRandomness(ctx, filecoinEpoch) + + if err != nil { + return nil, xerrors.Errorf("failed to get chain randomness: %w", err) + } + + ret, err := DrawRandomness(rbase, pers, filecoinEpoch, entropy) + if err != nil { + return nil, xerrors.Errorf("failed to draw chain randomness: %w", err) + } + + return ret, nil +} + +func (sr *stateRand) DrawBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, filecoinEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { + rbase, err := sr.GetBeaconRandomness(ctx, filecoinEpoch) + + if err != nil { + return nil, xerrors.Errorf("failed to get chain randomness: %w", err) + } + + ret, err := DrawRandomness(rbase, pers, filecoinEpoch, entropy) + if err != nil { + return nil, xerrors.Errorf("failed to draw chain randomness: %w", err) + } + + return ret, nil +} + func (sr *stateRand) extractBeaconEntryForEpoch(ctx context.Context, filecoinEpoch abi.ChainEpoch) (*types.BeaconEntry, error) { randTs, err := sr.GetBeaconRandomnessTipset(ctx, filecoinEpoch, false) if err != nil { diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 12b991e57..d7ac71d66 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -509,7 +509,7 @@ func (sm *StateManager) GetRandomnessFromBeacon(ctx context.Context, personaliza r := rand.NewStateRand(sm.ChainStore(), pts.Cids(), sm.beacon, sm.GetNetworkVersion) - return r.GetBeaconRandomness(ctx, personalization, randEpoch, entropy) + return r.DrawBeaconRandomness(ctx, personalization, randEpoch, entropy) } @@ -521,5 +521,5 @@ func (sm *StateManager) GetRandomnessFromTickets(ctx context.Context, personaliz r := rand.NewStateRand(sm.ChainStore(), pts.Cids(), sm.beacon, sm.GetNetworkVersion) - return r.GetChainRandomness(ctx, personalization, randEpoch, entropy) + return r.DrawChainRandomness(ctx, personalization, randEpoch, entropy) } diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index a5b108238..f647918e9 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -8,6 +8,8 @@ import ( "os" "time" + "github.com/filecoin-project/lotus/chain/rand" + "github.com/ipfs/go-cid" ipldcbor "github.com/ipfs/go-ipld-cbor" "go.opencensus.io/trace" @@ -229,21 +231,35 @@ 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 { - res, err := rt.vm.rand.GetChainRandomness(rt.ctx, personalization, randEpoch, entropy) + randomnessBase, err := rt.vm.rand.GetChainRandomness(rt.ctx, randEpoch) if err != nil { panic(aerrors.Fatalf("could not get ticket randomness: %s", err)) } - return res + + ret, err := rand.DrawRandomness(randomnessBase, personalization, randEpoch, entropy) + + if err != nil { + panic(aerrors.Fatalf("could not draw ticket randomness: %s", err)) + } + + return ret } func (rt *Runtime) GetRandomnessFromBeacon(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness { - res, err := rt.vm.rand.GetBeaconRandomness(rt.ctx, personalization, randEpoch, entropy) + randomnessBase, err := rt.vm.rand.GetBeaconRandomness(rt.ctx, randEpoch) if err != nil { - panic(aerrors.Fatalf("could not get beacon randomness: %s", err)) + panic(aerrors.Fatalf("could not get ticket randomness: %s", err)) } - return res + + ret, err := rand.DrawRandomness(randomnessBase, personalization, randEpoch, entropy) + + if err != nil { + panic(aerrors.Fatalf("could not draw ticket randomness: %s", err)) + } + + return ret } func (rt *Runtime) NewActorAddress() address.Address { diff --git a/chain/vm/vm.go b/chain/vm/vm.go index d6973210c..9db811e94 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -21,7 +21,6 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" builtin_types "github.com/filecoin-project/go-state-types/builtin" - "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/network" @@ -286,11 +285,6 @@ func NewLegacyVM(ctx context.Context, opts *VMOpts) (*LegacyVM, error) { }, nil } -type Rand interface { - GetChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) - GetBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) -} - type ApplyRet struct { types.MessageReceipt ActorErr aerrors.ActorError diff --git a/chain/vm/vmi.go b/chain/vm/vmi.go index 042621ca2..749d38d44 100644 --- a/chain/vm/vmi.go +++ b/chain/vm/vmi.go @@ -5,6 +5,8 @@ import ( "fmt" "os" + "github.com/filecoin-project/go-state-types/abi" + cid "github.com/ipfs/go-cid" "github.com/filecoin-project/go-state-types/network" @@ -68,3 +70,8 @@ func NewVM(ctx context.Context, opts *VMOpts) (Interface, error) { return newVMExecutor(vmi, opts.ExecutionLane), nil } + +type Rand interface { + GetChainRandomness(ctx context.Context, round abi.ChainEpoch) ([]byte, error) + GetBeaconRandomness(ctx context.Context, round abi.ChainEpoch) ([]byte, error) +} From 98a82d3dfec9f53540a90384161e1aba3dbee77a Mon Sep 17 00:00:00 2001 From: Aayush Date: Sun, 18 Dec 2022 14:59:34 -0500 Subject: [PATCH 2/8] Refactor: Rand: Rename DrawRandomness to DrawRandomnessFromBase --- chain/consensus/filcns/filecoin.go | 6 +++--- chain/gen/gen.go | 4 ++-- chain/rand/rand.go | 6 +++--- chain/rand/rand_test.go | 8 ++++---- chain/stmgr/actors.go | 2 +- chain/vm/runtime.go | 4 ++-- chain/vm/vmi.go | 4 ++-- miner/miner.go | 4 ++-- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/chain/consensus/filcns/filecoin.go b/chain/consensus/filcns/filecoin.go index fd49f1c9a..b5ec13a60 100644 --- a/chain/consensus/filcns/filecoin.go +++ b/chain/consensus/filcns/filecoin.go @@ -201,7 +201,7 @@ func (filec *FilecoinEC) ValidateBlock(ctx context.Context, b *types.FullBlock) return xerrors.Errorf("failed to marshal miner address to cbor: %w", err) } - vrfBase, err := rand.DrawRandomness(rBeacon.Data, crypto.DomainSeparationTag_ElectionProofProduction, h.Height, buf.Bytes()) + vrfBase, err := rand.DrawRandomnessFromBase(rBeacon.Data, crypto.DomainSeparationTag_ElectionProofProduction, h.Height, buf.Bytes()) if err != nil { return xerrors.Errorf("could not draw randomness: %w", err) } @@ -267,7 +267,7 @@ func (filec *FilecoinEC) ValidateBlock(ctx context.Context, b *types.FullBlock) beaconBase = h.BeaconEntries[len(h.BeaconEntries)-1] } - vrfBase, err := rand.DrawRandomness(beaconBase.Data, crypto.DomainSeparationTag_TicketProduction, h.Height-build.TicketRandomnessLookback, buf.Bytes()) + vrfBase, err := rand.DrawRandomnessFromBase(beaconBase.Data, crypto.DomainSeparationTag_TicketProduction, h.Height-build.TicketRandomnessLookback, buf.Bytes()) if err != nil { return xerrors.Errorf("failed to compute vrf base for ticket: %w", err) } @@ -345,7 +345,7 @@ func (filec *FilecoinEC) VerifyWinningPoStProof(ctx context.Context, nv network. rbase = h.BeaconEntries[len(h.BeaconEntries)-1] } - rand, err := rand.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, h.Height, buf.Bytes()) + rand, err := rand.DrawRandomnessFromBase(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, h.Height, buf.Bytes()) if err != nil { return xerrors.Errorf("failed to get randomness for verifying winning post proof: %w", err) } diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 6d891f260..9f8d0834d 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -376,7 +376,7 @@ func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m add buf.Write(pts.MinTicket().VRFProof) } - ticketRand, err := rand.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_TicketProduction, round-build.TicketRandomnessLookback, buf.Bytes()) + ticketRand, err := rand.DrawRandomnessFromBase(rbase.Data, crypto.DomainSeparationTag_TicketProduction, round-build.TicketRandomnessLookback, buf.Bytes()) if err != nil { return nil, nil, nil, err } @@ -647,7 +647,7 @@ func IsRoundWinner(ctx context.Context, round abi.ChainEpoch, return nil, xerrors.Errorf("failed to cbor marshal address: %w", err) } - electionRand, err := rand.DrawRandomness(brand.Data, crypto.DomainSeparationTag_ElectionProofProduction, round, buf.Bytes()) + electionRand, err := rand.DrawRandomnessFromBase(brand.Data, crypto.DomainSeparationTag_ElectionProofProduction, round, buf.Bytes()) if err != nil { return nil, xerrors.Errorf("failed to draw randomness: %w", err) } diff --git a/chain/rand/rand.go b/chain/rand/rand.go index dce4231e2..8ff6300c9 100644 --- a/chain/rand/rand.go +++ b/chain/rand/rand.go @@ -21,7 +21,7 @@ import ( var log = logging.Logger("rand") -func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func DrawRandomnessFromBase(rbase []byte, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { h := blake2b.New256() if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil { return nil, xerrors.Errorf("deriving randomness: %w", err) @@ -188,7 +188,7 @@ func (sr *stateRand) DrawChainRandomness(ctx context.Context, pers crypto.Domain return nil, xerrors.Errorf("failed to get chain randomness: %w", err) } - ret, err := DrawRandomness(rbase, pers, filecoinEpoch, entropy) + ret, err := DrawRandomnessFromBase(rbase, pers, filecoinEpoch, entropy) if err != nil { return nil, xerrors.Errorf("failed to draw chain randomness: %w", err) } @@ -203,7 +203,7 @@ func (sr *stateRand) DrawBeaconRandomness(ctx context.Context, pers crypto.Domai return nil, xerrors.Errorf("failed to get chain randomness: %w", err) } - ret, err := DrawRandomness(rbase, pers, filecoinEpoch, entropy) + ret, err := DrawRandomnessFromBase(rbase, pers, filecoinEpoch, entropy) if err != nil { return nil, xerrors.Errorf("failed to draw chain randomness: %w", err) } diff --git a/chain/rand/rand_test.go b/chain/rand/rand_test.go index acd928854..e2e722165 100644 --- a/chain/rand/rand_test.go +++ b/chain/rand/rand_test.go @@ -69,7 +69,7 @@ func TestNullRandomnessV1(t *testing.T) { } //stm: @BLOCKCHAIN_RAND_DRAW_RANDOMNESS_01 - rand2, err := rand.DrawRandomness(resp.Entry.Data, pers, randEpoch, entropy) + rand2, err := rand.DrawRandomnessFromBase(resp.Entry.Data, pers, randEpoch, entropy) if err != nil { t.Fatal(err) } @@ -148,8 +148,8 @@ func TestNullRandomnessV2(t *testing.T) { } //stm: @BLOCKCHAIN_RAND_DRAW_RANDOMNESS_01, @BLOCKCHAIN_RAND_EXTRACT_BEACON_ENTRY_FOR_EPOCH_01, @BLOCKCHAIN_RAND_GET_BEACON_RANDOMNESS_TIPSET_03 - // note that the randEpoch passed to DrawRandomness is still randEpoch (not the latest ts height) - rand2, err := rand.DrawRandomness(resp.Entry.Data, pers, randEpoch, entropy) + // note that the randEpoch passed to DrawRandomnessFromBase is still randEpoch (not the latest ts height) + rand2, err := rand.DrawRandomnessFromBase(resp.Entry.Data, pers, randEpoch, entropy) if err != nil { t.Fatal(err) } @@ -232,7 +232,7 @@ func TestNullRandomnessV3(t *testing.T) { } //stm: @BLOCKCHAIN_RAND_DRAW_RANDOMNESS_01 - rand2, err := rand.DrawRandomness(resp.Entry.Data, pers, randEpoch, entropy) + rand2, err := rand.DrawRandomnessFromBase(resp.Entry.Data, pers, randEpoch, entropy) if err != nil { t.Fatal(err) } diff --git a/chain/stmgr/actors.go b/chain/stmgr/actors.go index 4de39c7f1..56744fa74 100644 --- a/chain/stmgr/actors.go +++ b/chain/stmgr/actors.go @@ -355,7 +355,7 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcs beacon.Schedule return nil, xerrors.Errorf("failed to marshal miner address: %w", err) } - prand, err := rand.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, round, buf.Bytes()) + prand, err := rand.DrawRandomnessFromBase(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, round, buf.Bytes()) if err != nil { return nil, xerrors.Errorf("failed to get randomness for winning post: %w", err) } diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index f647918e9..031c3d6ca 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -237,7 +237,7 @@ func (rt *Runtime) GetRandomnessFromTickets(personalization crypto.DomainSeparat panic(aerrors.Fatalf("could not get ticket randomness: %s", err)) } - ret, err := rand.DrawRandomness(randomnessBase, personalization, randEpoch, entropy) + ret, err := rand.DrawRandomnessFromBase(randomnessBase, personalization, randEpoch, entropy) if err != nil { panic(aerrors.Fatalf("could not draw ticket randomness: %s", err)) @@ -253,7 +253,7 @@ func (rt *Runtime) GetRandomnessFromBeacon(personalization crypto.DomainSeparati panic(aerrors.Fatalf("could not get ticket randomness: %s", err)) } - ret, err := rand.DrawRandomness(randomnessBase, personalization, randEpoch, entropy) + ret, err := rand.DrawRandomnessFromBase(randomnessBase, personalization, randEpoch, entropy) if err != nil { panic(aerrors.Fatalf("could not draw ticket randomness: %s", err)) diff --git a/chain/vm/vmi.go b/chain/vm/vmi.go index 749d38d44..924f54738 100644 --- a/chain/vm/vmi.go +++ b/chain/vm/vmi.go @@ -72,6 +72,6 @@ func NewVM(ctx context.Context, opts *VMOpts) (Interface, error) { } type Rand interface { - GetChainRandomness(ctx context.Context, round abi.ChainEpoch) ([]byte, error) - GetBeaconRandomness(ctx context.Context, round abi.ChainEpoch) ([]byte, error) + GetChainRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) + GetBeaconRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) } diff --git a/miner/miner.go b/miner/miner.go index b6285eda6..ff2f9a4d2 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -531,7 +531,7 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (minedBlock *type return nil, err } - rand, err := lrand.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, round, buf.Bytes()) + rand, err := lrand.DrawRandomnessFromBase(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, round, buf.Bytes()) if err != nil { err = xerrors.Errorf("failed to get randomness for winning post: %w", err) return nil, err @@ -599,7 +599,7 @@ func (m *Miner) computeTicket(ctx context.Context, brand *types.BeaconEntry, rou buf.Write(chainRand.VRFProof) } - input, err := lrand.DrawRandomness(brand.Data, crypto.DomainSeparationTag_TicketProduction, round-build.TicketRandomnessLookback, buf.Bytes()) + input, err := lrand.DrawRandomnessFromBase(brand.Data, crypto.DomainSeparationTag_TicketProduction, round-build.TicketRandomnessLookback, buf.Bytes()) if err != nil { return nil, err } From e5fbba7958ee790112687f25214b33b5774b2084 Mon Sep 17 00:00:00 2001 From: Aayush Date: Sun, 18 Dec 2022 15:05:39 -0500 Subject: [PATCH 3/8] Refactor: Rand: Return randomness digest to VM --- chain/gen/genesis/miners.go | 8 +++--- chain/rand/rand.go | 51 ++++++++++++++++++++----------------- chain/vm/runtime.go | 8 +++--- extern/filecoin-ffi | 2 +- 4 files changed, 36 insertions(+), 33 deletions(-) diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index ebbe16326..556f5206b 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -595,16 +595,16 @@ var _ vm.Rand = new(fakeRand) // TODO: copied from actors test harness, deduplicate or remove from here type fakeRand struct{} -func (fr *fakeRand) GetChainRandomness(ctx context.Context, randEpoch abi.ChainEpoch) ([]byte, error) { +func (fr *fakeRand) GetChainRandomness(ctx context.Context, randEpoch abi.ChainEpoch) ([32]byte, error) { out := make([]byte, 32) _, _ = rand.New(rand.NewSource(int64(randEpoch * 1000))).Read(out) //nolint - return out, nil + return *(*[32]byte)(out), nil } -func (fr *fakeRand) GetBeaconRandomness(ctx context.Context, randEpoch abi.ChainEpoch) ([]byte, error) { +func (fr *fakeRand) GetBeaconRandomness(ctx context.Context, randEpoch abi.ChainEpoch) ([32]byte, error) { out := make([]byte, 32) _, _ = rand.New(rand.NewSource(int64(randEpoch))).Read(out) //nolint - return out, nil + return *(*[32]byte)(out), nil } func currentTotalPower(ctx context.Context, vm vm.Interface, maddr address.Address) (*power0.CurrentTotalPowerReturn, error) { diff --git a/chain/rand/rand.go b/chain/rand/rand.go index 8ff6300c9..7f16cbc08 100644 --- a/chain/rand/rand.go +++ b/chain/rand/rand.go @@ -22,12 +22,15 @@ import ( var log = logging.Logger("rand") func DrawRandomnessFromBase(rbase []byte, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return DrawRandomnessFromDigest(blake2b.Sum256(rbase), pers, round, entropy) +} + +func DrawRandomnessFromDigest(digest [32]byte, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { h := blake2b.New256() if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil { return nil, xerrors.Errorf("deriving randomness: %w", err) } - VRFDigest := blake2b.Sum256(rbase) - _, err := h.Write(VRFDigest[:]) + _, err := h.Write(digest[:]) if err != nil { return nil, xerrors.Errorf("hashing VRFDigest: %w", err) } @@ -69,18 +72,18 @@ func (sr *stateRand) GetBeaconRandomnessTipset(ctx context.Context, round abi.Ch return randTs, nil } -func (sr *stateRand) getChainRandomness(ctx context.Context, round abi.ChainEpoch, lookback bool) ([]byte, error) { +func (sr *stateRand) getChainRandomness(ctx context.Context, round abi.ChainEpoch, lookback bool) ([32]byte, error) { _, span := trace.StartSpan(ctx, "store.GetChainRandomness") defer span.End() span.AddAttributes(trace.Int64Attribute("round", int64(round))) ts, err := sr.cs.LoadTipSet(ctx, types.NewTipSetKey(sr.blks...)) if err != nil { - return nil, err + return [32]byte{}, err } if round > ts.Height() { - return nil, xerrors.Errorf("cannot draw randomness from the future") + return [32]byte{}, xerrors.Errorf("cannot draw randomness from the future") } searchHeight := round @@ -90,10 +93,10 @@ func (sr *stateRand) getChainRandomness(ctx context.Context, round abi.ChainEpoc randTs, err := sr.cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback) if err != nil { - return nil, err + return [32]byte{}, err } - return randTs.MinTicketBlock().Ticket.VRFProof, nil + return blake2b.Sum256(randTs.MinTicketBlock().Ticket.VRFProof), nil } type NetworkVersionGetter func(context.Context, abi.ChainEpoch) network.Version @@ -115,37 +118,37 @@ func NewStateRand(cs *store.ChainStore, blks []cid.Cid, b beacon.Schedule, netwo } // network v0-12 -func (sr *stateRand) getBeaconRandomnessV1(ctx context.Context, round abi.ChainEpoch) ([]byte, error) { +func (sr *stateRand) getBeaconRandomnessV1(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) { randTs, err := sr.GetBeaconRandomnessTipset(ctx, round, true) if err != nil { - return nil, err + return [32]byte{}, err } be, err := sr.cs.GetLatestBeaconEntry(ctx, randTs) if err != nil { - return nil, err + return [32]byte{}, err } - return be.Data, nil + return blake2b.Sum256(be.Data), nil } // network v13 -func (sr *stateRand) getBeaconRandomnessV2(ctx context.Context, round abi.ChainEpoch) ([]byte, error) { +func (sr *stateRand) getBeaconRandomnessV2(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) { randTs, err := sr.GetBeaconRandomnessTipset(ctx, round, false) if err != nil { - return nil, err + return [32]byte{}, err } be, err := sr.cs.GetLatestBeaconEntry(ctx, randTs) if err != nil { - return nil, err + return [32]byte{}, err } - return be.Data, nil + return blake2b.Sum256(be.Data), nil } // network v14 and on -func (sr *stateRand) getBeaconRandomnessV3(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([]byte, error) { +func (sr *stateRand) getBeaconRandomnessV3(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([32]byte, error) { if filecoinEpoch < 0 { return sr.getBeaconRandomnessV2(ctx, filecoinEpoch) } @@ -153,13 +156,13 @@ func (sr *stateRand) getBeaconRandomnessV3(ctx context.Context, filecoinEpoch ab be, err := sr.extractBeaconEntryForEpoch(ctx, filecoinEpoch) if err != nil { log.Errorf("failed to get beacon entry as expected: %s", err) - return nil, err + return [32]byte{}, err } - return be.Data, nil + return blake2b.Sum256(be.Data), nil } -func (sr *stateRand) GetChainRandomness(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([]byte, error) { +func (sr *stateRand) GetChainRandomness(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([32]byte, error) { nv := sr.networkVersionGetter(ctx, filecoinEpoch) if nv >= network.Version13 { @@ -169,7 +172,7 @@ func (sr *stateRand) GetChainRandomness(ctx context.Context, filecoinEpoch abi.C return sr.getChainRandomness(ctx, filecoinEpoch, true) } -func (sr *stateRand) GetBeaconRandomness(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([]byte, error) { +func (sr *stateRand) GetBeaconRandomness(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([32]byte, error) { nv := sr.networkVersionGetter(ctx, filecoinEpoch) if nv >= network.Version14 { @@ -182,13 +185,13 @@ func (sr *stateRand) GetBeaconRandomness(ctx context.Context, filecoinEpoch abi. } func (sr *stateRand) DrawChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, filecoinEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { - rbase, err := sr.GetChainRandomness(ctx, filecoinEpoch) + digest, err := sr.GetChainRandomness(ctx, filecoinEpoch) if err != nil { return nil, xerrors.Errorf("failed to get chain randomness: %w", err) } - ret, err := DrawRandomnessFromBase(rbase, pers, filecoinEpoch, entropy) + ret, err := DrawRandomnessFromDigest(digest, pers, filecoinEpoch, entropy) if err != nil { return nil, xerrors.Errorf("failed to draw chain randomness: %w", err) } @@ -197,13 +200,13 @@ func (sr *stateRand) DrawChainRandomness(ctx context.Context, pers crypto.Domain } func (sr *stateRand) DrawBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, filecoinEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { - rbase, err := sr.GetBeaconRandomness(ctx, filecoinEpoch) + digest, err := sr.GetBeaconRandomness(ctx, filecoinEpoch) if err != nil { return nil, xerrors.Errorf("failed to get chain randomness: %w", err) } - ret, err := DrawRandomnessFromBase(rbase, pers, filecoinEpoch, entropy) + ret, err := DrawRandomnessFromDigest(digest, pers, filecoinEpoch, entropy) if err != nil { return nil, xerrors.Errorf("failed to draw chain randomness: %w", err) } diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index 031c3d6ca..96e2068ab 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -231,13 +231,13 @@ 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 { - randomnessBase, err := rt.vm.rand.GetChainRandomness(rt.ctx, randEpoch) + digest, err := rt.vm.rand.GetChainRandomness(rt.ctx, randEpoch) if err != nil { panic(aerrors.Fatalf("could not get ticket randomness: %s", err)) } - ret, err := rand.DrawRandomnessFromBase(randomnessBase, personalization, randEpoch, entropy) + ret, err := rand.DrawRandomnessFromDigest(digest, personalization, randEpoch, entropy) if err != nil { panic(aerrors.Fatalf("could not draw ticket randomness: %s", err)) @@ -247,13 +247,13 @@ func (rt *Runtime) GetRandomnessFromTickets(personalization crypto.DomainSeparat } func (rt *Runtime) GetRandomnessFromBeacon(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness { - randomnessBase, err := rt.vm.rand.GetBeaconRandomness(rt.ctx, randEpoch) + digest, err := rt.vm.rand.GetBeaconRandomness(rt.ctx, randEpoch) if err != nil { panic(aerrors.Fatalf("could not get ticket randomness: %s", err)) } - ret, err := rand.DrawRandomnessFromBase(randomnessBase, personalization, randEpoch, entropy) + ret, err := rand.DrawRandomnessFromDigest(digest, personalization, randEpoch, entropy) if err != nil { panic(aerrors.Fatalf("could not draw ticket randomness: %s", err)) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index a458f638e..8c79a5f87 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit a458f638e3c8603c9b5a9ed9847c3af4597e46d4 +Subproject commit 8c79a5f876ced3587f7fb872d29e1cd159d06b08 From 91ee13b4611ad2570ed7856e801fc38f4934114a Mon Sep 17 00:00:00 2001 From: Aayush Date: Mon, 21 Aug 2023 16:26:51 -0400 Subject: [PATCH 4/8] fix: refactor rand_replay --- conformance/rand_record.go | 25 ++++++++++--------------- conformance/rand_replay.go | 29 ++++++++++++----------------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/conformance/rand_record.go b/conformance/rand_record.go index 277c984a7..e6df0c397 100644 --- a/conformance/rand_record.go +++ b/conformance/rand_record.go @@ -6,7 +6,6 @@ import ( "sync" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/test-vectors/schema" "github.com/filecoin-project/lotus/api/v0api" @@ -44,22 +43,20 @@ func (r *RecordingRand) loadHead() { r.head = head.Key() } -func (r *RecordingRand) GetChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (r *RecordingRand) GetChainRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) { r.once.Do(r.loadHead) // FullNode's v0 ChainGetRandomnessFromTickets handles whether we should be looking forward or back - ret, err := r.api.ChainGetRandomnessFromTickets(ctx, r.head, pers, round, entropy) + ret, err := r.api.ChainGetRandomnessFromTickets(ctx, r.head, round) if err != nil { return ret, err } - r.reporter.Logf("fetched and recorded chain randomness for: dst=%d, epoch=%d, entropy=%x, result=%x", pers, round, entropy, ret) + r.reporter.Logf("fetched and recorded chain randomness for: epoch=%d, result=%x", round, ret) match := schema.RandomnessMatch{ On: schema.RandomnessRule{ - Kind: schema.RandomnessChain, - DomainSeparationTag: int64(pers), - Epoch: int64(round), - Entropy: entropy, + Kind: schema.RandomnessChain, + Epoch: int64(round), }, Return: []byte(ret), } @@ -70,21 +67,19 @@ func (r *RecordingRand) GetChainRandomness(ctx context.Context, pers crypto.Doma return ret, err } -func (r *RecordingRand) GetBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (r *RecordingRand) GetBeaconRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) { r.once.Do(r.loadHead) - ret, err := r.api.StateGetRandomnessFromBeacon(ctx, pers, round, entropy, r.head) + ret, err := r.api.StateGetRandomnessFromBeacon(ctx, round, r.head) if err != nil { return ret, err } - r.reporter.Logf("fetched and recorded beacon randomness for: dst=%d, epoch=%d, entropy=%x, result=%x", pers, round, entropy, ret) + r.reporter.Logf("fetched and recorded beacon randomness for: epoch=%d, result=%x", round, ret) match := schema.RandomnessMatch{ On: schema.RandomnessRule{ - Kind: schema.RandomnessBeacon, - DomainSeparationTag: int64(pers), - Epoch: int64(round), - Entropy: entropy, + Kind: schema.RandomnessBeacon, + Epoch: int64(round), }, Return: []byte(ret), } diff --git a/conformance/rand_replay.go b/conformance/rand_replay.go index ef19e41bb..74f1667f1 100644 --- a/conformance/rand_replay.go +++ b/conformance/rand_replay.go @@ -5,7 +5,6 @@ import ( "context" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/test-vectors/schema" "github.com/filecoin-project/lotus/chain/vm" @@ -42,38 +41,34 @@ func (r *ReplayingRand) match(requested schema.RandomnessRule) ([]byte, bool) { return nil, false } -func (r *ReplayingRand) GetChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (r *ReplayingRand) GetChainRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) { rule := schema.RandomnessRule{ - Kind: schema.RandomnessChain, - DomainSeparationTag: int64(pers), - Epoch: int64(round), - Entropy: entropy, + Kind: schema.RandomnessChain, + Epoch: int64(round), } if ret, ok := r.match(rule); ok { - r.reporter.Logf("returning saved chain randomness: dst=%d, epoch=%d, entropy=%x, result=%x", pers, round, entropy, ret) + r.reporter.Logf("returning saved chain randomness: epoch=%d, result=%x", round, ret) return ret, nil } - r.reporter.Logf("returning fallback chain randomness: dst=%d, epoch=%d, entropy=%x", pers, round, entropy) + r.reporter.Logf("returning fallback chain randomness: epoch=%d", round) - return r.fallback.GetChainRandomness(ctx, pers, round, entropy) + return r.fallback.GetChainRandomness(ctx, round) } -func (r *ReplayingRand) GetBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (r *ReplayingRand) GetBeaconRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) { rule := schema.RandomnessRule{ - Kind: schema.RandomnessBeacon, - DomainSeparationTag: int64(pers), - Epoch: int64(round), - Entropy: entropy, + Kind: schema.RandomnessBeacon, + Epoch: int64(round), } if ret, ok := r.match(rule); ok { - r.reporter.Logf("returning saved beacon randomness: dst=%d, epoch=%d, entropy=%x, result=%x", pers, round, entropy, ret) + r.reporter.Logf("returning saved beacon randomness: epoch=%d, result=%x", round, ret) return ret, nil } - r.reporter.Logf("returning fallback beacon randomness: dst=%d, epoch=%d, entropy=%x", pers, round, entropy) + r.reporter.Logf("returning fallback beacon randomness: epoch=%d, ", round) - return r.fallback.GetBeaconRandomness(ctx, pers, round, entropy) + return r.fallback.GetBeaconRandomness(ctx, round) } From 034f6cf832fa43ef03a806bdad13a4191af29067 Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 16 Aug 2023 11:55:37 -0400 Subject: [PATCH 5/8] wip: 2 more randomness fetching APIs --- api/api_full.go | 5 +++ api/mocks/mock_full.go | 30 +++++++++++++ api/proxy_gen.go | 26 +++++++++++ chain/stmgr/stmgr.go | 23 ++++++++++ chain/vm/runtime.go | 3 +- chain/vm/vmi.go | 3 +- documentation/en/api-v1-unstable-methods.md | 48 +++++++++++++++++++++ node/impl/full/state.go | 27 ++++++++++++ 8 files changed, 161 insertions(+), 4 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 591799b48..91d4b1dac 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -641,6 +641,11 @@ type FullNode interface { // 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 + // StateGetRandomnessFromTickets is used to sample the chain for randomness. + StateGetRandomnessDigestFromTickets(ctx context.Context, randEpoch abi.ChainEpoch, tsk types.TipSetKey) (abi.Randomness, error) //perm:read + // StateGetRandomnessFromBeacon is used to sample the beacon for randomness. + StateGetRandomnessDigestFromBeacon(ctx context.Context, randEpoch abi.ChainEpoch, tsk types.TipSetKey) (abi.Randomness, error) //perm:read + // StateGetBeaconEntry returns the beacon entry for the given filecoin epoch. If // the entry has not yet been produced, the call will block until the entry // becomes available diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index a1e9c1230..d2f2e528e 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -3263,6 +3263,36 @@ func (mr *MockFullNodeMockRecorder) StateGetNetworkParams(arg0 interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetNetworkParams", reflect.TypeOf((*MockFullNode)(nil).StateGetNetworkParams), arg0) } +// StateGetRandomnessDigestFromBeacon mocks base method. +func (m *MockFullNode) StateGetRandomnessDigestFromBeacon(arg0 context.Context, arg1 abi.ChainEpoch, arg2 types.TipSetKey) (abi.Randomness, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetRandomnessDigestFromBeacon", arg0, arg1, arg2) + ret0, _ := ret[0].(abi.Randomness) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetRandomnessDigestFromBeacon indicates an expected call of StateGetRandomnessDigestFromBeacon. +func (mr *MockFullNodeMockRecorder) StateGetRandomnessDigestFromBeacon(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetRandomnessDigestFromBeacon", reflect.TypeOf((*MockFullNode)(nil).StateGetRandomnessDigestFromBeacon), arg0, arg1, arg2) +} + +// StateGetRandomnessDigestFromTickets mocks base method. +func (m *MockFullNode) StateGetRandomnessDigestFromTickets(arg0 context.Context, arg1 abi.ChainEpoch, arg2 types.TipSetKey) (abi.Randomness, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetRandomnessDigestFromTickets", arg0, arg1, arg2) + ret0, _ := ret[0].(abi.Randomness) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetRandomnessDigestFromTickets indicates an expected call of StateGetRandomnessDigestFromTickets. +func (mr *MockFullNodeMockRecorder) StateGetRandomnessDigestFromTickets(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetRandomnessDigestFromTickets", reflect.TypeOf((*MockFullNode)(nil).StateGetRandomnessDigestFromTickets), 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() diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 953bc828a..95668596d 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -494,6 +494,10 @@ type FullNodeMethods struct { StateGetNetworkParams func(p0 context.Context) (*NetworkParams, error) `perm:"read"` + StateGetRandomnessDigestFromBeacon func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (abi.Randomness, error) `perm:"read"` + + StateGetRandomnessDigestFromTickets func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (abi.Randomness, 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"` @@ -3432,6 +3436,28 @@ func (s *FullNodeStub) StateGetNetworkParams(p0 context.Context) (*NetworkParams return nil, ErrNotSupported } +func (s *FullNodeStruct) StateGetRandomnessDigestFromBeacon(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (abi.Randomness, error) { + if s.Internal.StateGetRandomnessDigestFromBeacon == nil { + return *new(abi.Randomness), ErrNotSupported + } + return s.Internal.StateGetRandomnessDigestFromBeacon(p0, p1, p2) +} + +func (s *FullNodeStub) StateGetRandomnessDigestFromBeacon(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (abi.Randomness, error) { + return *new(abi.Randomness), ErrNotSupported +} + +func (s *FullNodeStruct) StateGetRandomnessDigestFromTickets(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (abi.Randomness, error) { + if s.Internal.StateGetRandomnessDigestFromTickets == nil { + return *new(abi.Randomness), ErrNotSupported + } + return s.Internal.StateGetRandomnessDigestFromTickets(p0, p1, p2) +} + +func (s *FullNodeStub) StateGetRandomnessDigestFromTickets(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (abi.Randomness, error) { + return *new(abi.Randomness), 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 diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index d7ac71d66..c977e3883 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -523,3 +523,26 @@ func (sm *StateManager) GetRandomnessFromTickets(ctx context.Context, personaliz return r.DrawChainRandomness(ctx, personalization, randEpoch, entropy) } + +func (sm *StateManager) GetRandomnessDigestFromBeacon(ctx context.Context, randEpoch abi.ChainEpoch, tsk types.TipSetKey) ([32]byte, error) { + pts, err := sm.ChainStore().GetTipSetFromKey(ctx, tsk) + if err != nil { + return [32]byte{}, xerrors.Errorf("loading tipset %s: %w", tsk, err) + } + + r := rand.NewStateRand(sm.ChainStore(), pts.Cids(), sm.beacon, sm.GetNetworkVersion) + + return r.GetBeaconRandomness(ctx, randEpoch) + +} + +func (sm *StateManager) GetRandomnessDigestFromTickets(ctx context.Context, randEpoch abi.ChainEpoch, tsk types.TipSetKey) ([32]byte, error) { + pts, err := sm.ChainStore().LoadTipSet(ctx, tsk) + if err != nil { + return [32]byte{}, xerrors.Errorf("loading tipset key: %w", err) + } + + r := rand.NewStateRand(sm.ChainStore(), pts.Cids(), sm.beacon, sm.GetNetworkVersion) + + return r.GetChainRandomness(ctx, randEpoch) +} diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index 96e2068ab..355fcea2b 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -8,8 +8,6 @@ import ( "os" "time" - "github.com/filecoin-project/lotus/chain/rand" - "github.com/ipfs/go-cid" ipldcbor "github.com/ipfs/go-ipld-cbor" "go.opencensus.io/trace" @@ -35,6 +33,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/aerrors" "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/rand" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/types" ) diff --git a/chain/vm/vmi.go b/chain/vm/vmi.go index 924f54738..d022479f1 100644 --- a/chain/vm/vmi.go +++ b/chain/vm/vmi.go @@ -5,10 +5,9 @@ import ( "fmt" "os" - "github.com/filecoin-project/go-state-types/abi" - cid "github.com/ipfs/go-cid" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/chain/types" diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 75c4a3108..056bb5be2 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -235,6 +235,8 @@ * [StateGetClaim](#StateGetClaim) * [StateGetClaims](#StateGetClaims) * [StateGetNetworkParams](#StateGetNetworkParams) + * [StateGetRandomnessDigestFromBeacon](#StateGetRandomnessDigestFromBeacon) + * [StateGetRandomnessDigestFromTickets](#StateGetRandomnessDigestFromTickets) * [StateGetRandomnessFromBeacon](#StateGetRandomnessFromBeacon) * [StateGetRandomnessFromTickets](#StateGetRandomnessFromTickets) * [StateListActors](#StateListActors) @@ -6963,6 +6965,52 @@ Response: } ``` +### StateGetRandomnessDigestFromBeacon +StateGetRandomnessFromBeacon is used to sample the beacon for randomness. + + +Perms: read + +Inputs: +```json +[ + 10101, + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `"Bw=="` + +### StateGetRandomnessDigestFromTickets +StateGetRandomnessFromTickets is used to sample the chain for randomness. + + +Perms: read + +Inputs: +```json +[ + 10101, + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `"Bw=="` + ### StateGetRandomnessFromBeacon StateGetRandomnessFromBeacon is used to sample the beacon for randomness. diff --git a/node/impl/full/state.go b/node/impl/full/state.go index c8ea55ea1..32a6caf11 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -1752,7 +1752,34 @@ func (a *StateAPI) StateGetRandomnessFromTickets(ctx context.Context, personaliz func (a *StateAPI) StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) { return a.StateManager.GetRandomnessFromBeacon(ctx, personalization, randEpoch, entropy, tsk) +} +func (a *StateAPI) StateGetRandomnessDigestFromTickets(ctx context.Context, randEpoch abi.ChainEpoch, tsk types.TipSetKey) (abi.Randomness, error) { + ts, err := a.Chain.GetTipSetFromKey(ctx, tsk) + if err != nil { + return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) + } + + ret, err := a.StateManager.GetRandomnessDigestFromTickets(ctx, randEpoch, ts.Key()) + if err != nil { + return nil, xerrors.Errorf("failed to get randomness digest from tickets: %w", err) + } + + return ret[:], nil +} + +func (a *StateAPI) StateGetRandomnessDigestFromBeacon(ctx context.Context, randEpoch abi.ChainEpoch, tsk types.TipSetKey) (abi.Randomness, error) { + ts, err := a.Chain.GetTipSetFromKey(ctx, tsk) + if err != nil { + return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) + } + + ret, err := a.StateManager.GetRandomnessDigestFromBeacon(ctx, randEpoch, ts.Key()) + if err != nil { + return nil, xerrors.Errorf("failed to get randomness digest from tickets: %w", err) + } + + return ret[:], nil } func (a *StateAPI) StateGetBeaconEntry(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) { From c90faf07546a14116a2b1a8bad11c66b6b156450 Mon Sep 17 00:00:00 2001 From: Aayush Date: Mon, 21 Aug 2023 17:09:44 -0400 Subject: [PATCH 6/8] fixup conformance tests to incorporate randomness changes --- api/api_full.go | 4 ++-- build/openrpc/full.json.gz | Bin 33996 -> 34100 bytes build/openrpc/gateway.json.gz | Bin 11395 -> 11393 bytes build/openrpc/miner.json.gz | Bin 15944 -> 15939 bytes build/openrpc/worker.json.gz | Bin 5244 -> 5245 bytes conformance/rand_fixed.go | 9 ++++----- conformance/rand_record.go | 20 ++++++++++---------- conformance/rand_replay.go | 11 ++++------- documentation/en/api-v1-unstable-methods.md | 4 ++-- extern/test-vectors | 2 +- go.mod | 2 +- go.sum | 4 ++-- 12 files changed, 26 insertions(+), 30 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 91d4b1dac..0e128b398 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -641,9 +641,9 @@ type FullNode interface { // 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 - // StateGetRandomnessFromTickets is used to sample the chain for randomness. + // StateGetRandomnessDigestFromTickets. is used to sample the chain for randomness. StateGetRandomnessDigestFromTickets(ctx context.Context, randEpoch abi.ChainEpoch, tsk types.TipSetKey) (abi.Randomness, error) //perm:read - // StateGetRandomnessFromBeacon is used to sample the beacon for randomness. + // StateGetRandomnessDigestFromBeacon is used to sample the beacon for randomness. StateGetRandomnessDigestFromBeacon(ctx context.Context, randEpoch abi.ChainEpoch, tsk types.TipSetKey) (abi.Randomness, error) //perm:read // StateGetBeaconEntry returns the beacon entry for the given filecoin epoch. If diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 19c64d5ab95658991b2c25c5602117a9c5445ea9..9533da309f8ef460ed700e05965a471983c043a6 100644 GIT binary patch delta 27522 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 delta 27424 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

P`8f|HR^>gW9PBPu???j#p%gP`&H)&%rb*BkzV2 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 Lh+5U1zjXotPMk?m delta 8281 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 XL;dmb`{Vx$00960%C+vwzjXotPdydL diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 5481bbf12725458787bee716291227945dbdf44e..b9fb03fe671fc4df2ffc1af6684d9a55c69a8e25 100644 GIT binary patch delta 13453 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

k3nLh{nmHLr-qza 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 delta 13459 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 BH#q63d2WFczViae47n0lZVmiEUf{ym0}Y zn`9y;C}aC~$roR312O@5-G55l#*iSZW1g&s|5f2xp_FbHZpoPiIL+%4M(23_)=$&|-snS!mPJd6;^u8oZ z|3uJ4t|y(6rDd&|E;^7h4t=pCx!A6ek|3WKYivp$bsEf&r>DZspTB3|M zS^Ahn%CP6Qc{iiJ4!f~Lp;fh3kRd^aUKbe}DvFj(hJGb*VjaTB=GtJzJIjfv7R(R3twSFMG~IY@bo@US2>t z4<3g?D8V)b+k9~GDzY3h3^->zmo6QhuOaW3nIvcG<10XLaTqc_M*bB zd~P|s%Zmm!yJy(sM2Fn!n*xfW_7(?X3EX(CxKX%x80z)@wL;%W2nFXRA{0+?#=7Ad zHi65k_Yk4XL3_xWc7ik$On+v9!4V3eX}ZYskO}a%v@STO%&kvtcY#bWSpf+C48H%6 z0R(5Zi45@WagL2Sz{JF?iL8#r(xTX4Z#xb&?vc5zg>}|@0Er18_{LJrp*QcwmolHE z6Q<{Ix>6@Up@E?G=$Z4uN`u;fPBiZxEu6pYAOCTdD|_MmJy}fAYJaNfQ>-mlgXI$G z>Vqz=#VVgbbb;srqW4F|i2fHQru}$fU(t$CzAK<8V)_!zFq09PET!*feH>@+J~C}! z!0l+@CKAzeVS6-Cm9sgtEJS9(HX$ag+`gZem39ez4PqUb+*xD|ZGV&?c~aIYW;X^($S#^LEB#nz2dX(ZbASs7r7^STo6bx}*JWYU;a}RNcUv)`%~_MG)nH8hOivI8g@1C}!=D~Me2`l60c0@~ zO~OtAC(TG&hMjJ4z&E`eDlpID|Mh7_SyJhLh^74R)u*{Z{(Fx8{o#YJ>i5`jSX*v# z$?Y8{V~xvGC1W|8i?;1aS|AhqeKWxXx~pG_VVlgHneX(_K#zEoqNv$ze^d+HeYV+e z+&R8BB0L!HC4a&Mli6WRcvHf2QQ=Gqd~@fpLc5XTOEKS#@CDR2RdiLx8|kQL4W<)x zSI}KScLm+eqPwq`I;PxqhEb+dlrlpuZdlnyjz;5ZP`Q@!p^=i!htA2=<|IHYOZI0b z$>RiA$|5$x-j`c>+ZM;gKtf3y`3faVxT2X^G*Ov3*?&K6k8Z1CY6>3wPxoZ$d`4^+ zT_Vq3y2j>60a=PXc#^)M#l?K(VgG|YU6t8`Ymcd9_~YskIS|m=cl=V zE<;HRdw+|JM_(?krBg+gK0yy<)o^ALW9hnxQ0Y`z%Tg~P%19fH21;C-?XwyMiq?~q zNlnPqQ-<15?~PPFCRT6IQwBYCs10hBY*(9-DV>&(PE*yMs`mAvs>H=pG-Wted;OlG zmlCj(E#V*~p{8s7@lYFSgY5p(6A}(|UDf-_Sbx_O5~`z-K3008(MYLR!og_R8|%Y? zKQ_S<6sgBBXVu+;`IYP|uBTW$brIf@JflbL{qYS7Sp_wd3&o~=e69}^3nSv&y* zwF1HM)jjF_Tc++w&)+I_U(5a{q0H?=({%i^fUCQFrEM<%0Dc{CvVCegCLG8jm%-KUU)d8hQMD!d3MAO=$f=p1&cjFFOS|iqO=b%l&U+zjvhcHD$1w z+ErY=D-XCkjoIh12TD&J_jR?W$EV@3xnBLUm2}{$qV~tiNLNRRj?;#^HX8ReeOUL- zSAlq9x0h>yr!_jC^zW_G`INhFlYh=<`u{^#`!mGMS$;Yq0F;)f;r0GQMC0A@-7Rb8 zh*$GkBIgoL-g0d0`7_E_9~2y}{&izPE$UyxYJY?3pJMziQ~x9=v`hVCs9--JH#j+e z>3{JnqJe4z2N4Y@EtL}+@Z(45xS5bD#MV%6~B zSX08qU?4I{5GWu}K%js?0f7Q<3Xo!$L}tODcl7$t$@`WZcU>Jo}sdfUc1b`FXOhg@|4O z6ygC(U4%}4X@xgaWfsW=PaBCFxgH`%z{2a1;^|7=$Ktzz;b7Vuk4BRYnrcc%H;1S* z(U8#@!+|=S>J!+9BY#Pj{z7;*r@_q*Uu^?genqM4CC(tZ^4Hx0ZDv2I?H+#b(Wcm} zY1poktheEfM9FzmqzxHwH?6G(Y_IF~mtIfHF?829kmp??&kJ;#%i?31vrT`w#Af&U zx|oW_3o5YsZE1XVo|H4&;Dd||*KZTc7wliK|Ch}E^+7iKzeE;-9=i5}`;p1X{1dr( zS$}l5^%9a9Z_ret`lf#fe7cn)c&cUQ+6Ka&(&n;J1HVtF9-?fdR9%*?@J|%{U43_d zQ(~EJ59)i?eR%<}0$^Vo!1jlE0K2dsc;k_SOnGn$Cpd(J^Uv=Cc+5seR!BA|@_=6` z3adIB?>2GEC*XHFM=n>0MG*i-+gZ}ub8MW?Y@4+JPJPevmA`*2Eh{7Z!+#i=>uy6o zEbE0E*I<-qT#;2(ZX1i_?43z#C{8$;k`R z0eghjr5_bckmrB-yUXmckCD)z_yMI-=t*o(>^%pSoMDr9$2roJrXWAwTVjVZgBZgnae)C9TY-8;jhGH&85~ zwah}>z@f7g`6cp7fp`M(UK!%4O3pb^%&lLzsQQZ3E^|$bQip9P#YJio3}X$Ct}h8p zY>5rG0TbWAx{_FF5&LF>s0gA`4^ipm9Q+l7+$yv4UFPZ&^d06`o1^b!cot?NX6h|8 z*fy||9F2c|iPS`3puoV_hJk9%nRwL>!#n&Gyrz!7+js8WSA35WXZgusvZP>pARY-r zz9XmHF)J{!2PYP*TbywBMozd>wVV^~t`Pl|_}aj2H=B%kgmGfqR>4OHy8;gV@E|5i z!N&)?3K!75_W+%vja5Jopy1uf#yYy-H=6=FhwOhJ>;Vr^iplJjEZ_=Ic#i-CQ}hTt zWZ1;?0JQ;g?{EewVm~PK?g3MGb7|H1sRQfPQbxrzG{PD-$FhoV2};|Ho^uY))qF`! z`;i-I!fcsPlW!(ru7izaS9`X3{Ndu+47tDy6CR8XZ%$wOIbTkPUp;*^P5(U#;a za)&8E=XR+m8Un#>a|f?=?`cK$tfVk~nc2z4u9%i?OzZS%y=Mwi7@_dGV+S-nXB#y5 zY}5~L<+PKB6L5d*=fn_&w#-R3!(7=DwXJ)|r+Q(M0-6`5;9_?>sT$eo$?K}!^M-I# z>y31!GLp7bAqP1@N`FC2|GmVBa;oGuA722+Hrt^qVA1-s)o)kT#IlWh4=f4A7NB$F sA^=?kND!>)0e4A=AL1#ZJouDqIF#1wyY>GE00960&7uZrTdghu0HVjhMF0Q* delta 3985 zcmV;C4{q@NDEug}3TW`apk{Lm%0b->`z?S7hkkm2X zt7Lv9l}cq#SRQLT)Yq3^BOWeH?YlS+K?{?ahujBjpcBup&^z%uQ>CYBoqwLH>3vC- z{)wQ8Tu(YBOUqg_U34I2#&;1@qV6x8TDL7Xf{h&6|Akh*76nSDlwr?p^KM3c9d=`hLaS=6AVY!-y*4s59A=ZDUkRL8h<6?v=E|Tw@4>r(+~o6- zFmpBu33`l!bL>Ppm|SGpeSZj2M?tQZs?unbtxEqu)R=QBlAni{J?9~|&!~4VFCd+V zkE7c#9}U0bzu6|@B+|aHO>yT2J8h$-v`z|HvJ??2 zWPi18nnXNAIk^`01F9^PV4H$%zBaZw9_LU5OrV8-qD$mh*nroL36seLB7YYy43(Zz zj0@pdR0D({bMr_Lz{=ajkisqHS12_`E5!JonR zA2NX8%r=n$-aXE-F$b8Km^G2ru~=FZ8|-bzfyO;Dx3#d&dJiBm0R-PzsyX!L-S|@G zlXSxL98Op2%`)E+%^K3Hi`8_U~o(tQffvTL%p=BX53$_U{VdeJyysWfKNKAV{kmtqdz)rAB->AzFgI%6d zj38M>C2kPwz~s&%Ykz2?1R-Z|8r!TIH4>2>QKJUK$rYuMowhwXU+x1ghA6%lwWreQ zXyaqIa3cG!k$D#Q{yI=rmUbeZ-stL`EQNaoK}dW2b21K3A1StOlu9Gfw$I7{ORNtI znOG{!ESua_IDaUV+aCV(_~C=pnhzk0 znP?Jr3OH#-(lYFHivzyt?NEVv7XPnLE6S2e|3fV0f3H5x4f5Y}^zRQJd{w{4j>Fn= zlS^*zI2mhPo+=s3*<7@3PtpRJ*zcPOCeU5|N(|d%=FEJjhX#7YqZCEWZu_HJ;O?`{ ze&f#ZwGm-m+kZ=h2`00{nDC~A=c2-y6!_-OVTE=h#g}5f8{rG6Z>s33j5pFz&l*f8 z=&qom-jU0`})u3`MyMukpi+5dGI8CLyL>~%ESH#d%7yK2iG1`$?(V3BXnXvc5lxvuD#%|FaBRQ zaRKfURXMgtPac$$M}O|=Y|K^(Co|vB!e&iX6@4U2-{6nQEchD%q|!B~v;rA)ThGJyq@NLsf~3r)bJ>toHgn zMK2{_CtJcnNb{o!PePg7howoH49}kA=cn}jV`uO>TOZxs%e>5IzdVj3O2Q>2d`Gl+J`J2%CgFJsjT3>bwa1^ai zfKE-V_o|Js-i1BWvmbS1F;{_ zCIfgS`vFHYfU^4m$1;EjESQi&1Djc>(5hKh5+_Yrtsff+qTAN7ZW#Kb3jR^zNEn;9fQ;<(bJrxC}LCV#zf zbWCZ|D@DhYCh6#pD^1d~A6J_6T;Q0}q!)sYDNTCi=XlaYytCt?W|sTk#D4Ec>1)bh zGqtO@dRHEBcN(+LV-J*`I_~RgPmfQ-V{^UwWh?2xRYmQOm65KF5*?=vb!{~6Yx=P6 zov#A%#BMLw0#9pnKIz|ErSmCw-+v~Z&-DL?toCP!nX~+KL;xr)QN!!~hls|zb0Df?CwShSmNC)j!4fTc-X=P-vI>$56q3KyGky z{?h;AS40EV2o53|NJIm*0~ACw&@qL38PPx)U?3uco@J&WB7<5S7$G8qM1N$EhztrK z!bLMgf}6JQgA1$$I5a&|sKqu)VMB$ug3`hryo6NY6Po6P6Nu3COrbfguOZZ@ImN2s z!?C7>i@`u-k|0n(pnyODfdT>r-WUoj3HwV#6LGu$)}*ocK$aa(mQwO3>u7-LbP<%T%#F&Xpr^ zaNKLj4CNEYPlkn*B9~mmdanD+o>v^BkkGA?!x2;Er;G;26|+dn zOYw{=$l9sdH{JY{a~o|CGVoQT_E#ci>#4cCT)Y+Sp5bnbB?nx7~8T8QWs zKp`Hm)J5pzmsWT)Rc4V~@U)S*k?SFH1T4HBDW0y>eJs8k7!Ibr@n|&ZpsA*GbaRM0 z6Ac-iF&wDFsXl>yIDe94=`Vz5a~j<2@YOb;R%GI08lykT8`LV_&|`uxT_m@uo)N0`+N!pxc|NOMbUUy$ZEP5=aHegPvR zNON09reARo6eVDOH!HEBq4$e^=k# z-;`LU+k^U^bzfcptN_^82C!N$4`3Je18+QXkSPyN;RJ_}aQ^vy0FT+|$O_2@MIP`A zMPXHEu_yw-Xgf<eUnu7dzZ;2gd0se*H zpSF;4k?WrxKYVB=GQCptdC^vFp|!v8)~2^V1)Dn-n=85ZxdV@qj&Zv$Q`2xTK2&FU zoZfg>Ip2szyMZTXor^AloC|XPlF9i%-$%}Q<31xftDk?G5%SgFm$WjUZY**$-axT{ z)-nri1BcF1yagvkxJXTcVXWcN^(BFc zEwSM?VB#BCR}w2NV&6;<6+u+$Au6MsgTG>sTV-~>%UpeezQg=#bM&1I&%#W^OudB$ z+XhyWqtSmak(vk$6d3s0FmRl6CSJ9}@D4u(uc_nj_MLn872l)8S$;B@EGgI?h)2SZ z@5m{4%nD5G!HLD{7AM@jkrVDzCFg{@D@1=KzBX{%%_d_WVVu~uRq)Zlu7E>7Jcx-> z@bST}!Uc5iJwWGZV-?T?D0p|Wv5qeI&8C3PA^U#^d%#1KVlulW3%CLl-Xj3P6g>hD z88$IJKyAR>JDfp^*bfT5d%)D)Tv|1L>cD!nlu`-g3>nA%Q*+WRJ&X?2SS5F^J(?9u`S8d|C z^QV93E+(@gMT(bmus)^4%J;mr&7=L*#8QkRb~bjMhr7Kwt=4lsR}2;4GosL#f6VBV zQfj@;&gm5sxogtqs5Fh7F8}7Vr=RmZUaZ3paEp-w4)u(YqH0wq;U8bMh7a;SWeIfP z0Nu{h>i6K?0y;OlWxDgvcfq(7E>lDbEm$zC$SIj`2lVAzBf`SJbYt!$b zxH{B(E11ykx1imclC%H0857Mcrgi$X-ZKR$j8J&pu>)E!XB#y5 zY}5~L<&%dKaeq{EVu(Up<|Lb8uI!20);;7?y)a1u%?nd-vAdmAjqLQ~b=B^9LpZAS zM!HfNNn5IrdQOniUl7xOFEOH=D!I+a7r?R2cIXONwEk@M+f_BOY~$VoOG2>)=p4BS rKo>~0RR6Y3 Date: Tue, 22 Aug 2023 11:14:17 -0400 Subject: [PATCH 7/8] refactor: move vm.Rand to rand.Rand --- chain/consensus/compute_state.go | 2 +- chain/gen/genesis/miners.go | 3 ++- chain/rand/rand.go | 11 ++++++++--- chain/stmgr/stmgr.go | 24 ++++++++++++++++++++++-- chain/vm/fvm.go | 3 ++- chain/vm/vm.go | 5 +++-- chain/vm/vmi.go | 6 ------ cmd/tvx/main.go | 4 ++-- conformance/driver.go | 13 +++++++------ conformance/rand_fixed.go | 6 +++--- conformance/rand_record.go | 4 ++-- conformance/rand_replay.go | 6 +++--- 12 files changed, 55 insertions(+), 32 deletions(-) diff --git a/chain/consensus/compute_state.go b/chain/consensus/compute_state.go index 64b9624ea..b81975f63 100644 --- a/chain/consensus/compute_state.go +++ b/chain/consensus/compute_state.go @@ -80,7 +80,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, pstate cid.Cid, bms []FilecoinBlockMessages, epoch abi.ChainEpoch, - r vm.Rand, + r rand.Rand, em stmgr.ExecMonitor, vmTracing bool, baseFee abi.TokenAmount, diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index 556f5206b..2d9942464 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -43,6 +43,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin/reward" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/consensus" + lrand "github.com/filecoin-project/lotus/chain/rand" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -590,7 +591,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal return c, nil } -var _ vm.Rand = new(fakeRand) +var _ lrand.Rand = new(fakeRand) // TODO: copied from actors test harness, deduplicate or remove from here type fakeRand struct{} diff --git a/chain/rand/rand.go b/chain/rand/rand.go index 7f16cbc08..40f9f593a 100644 --- a/chain/rand/rand.go +++ b/chain/rand/rand.go @@ -108,7 +108,12 @@ type stateRand struct { networkVersionGetter NetworkVersionGetter } -func NewStateRand(cs *store.ChainStore, blks []cid.Cid, b beacon.Schedule, networkVersionGetter NetworkVersionGetter) *stateRand { +type Rand interface { + GetChainRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) + GetBeaconRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) +} + +func NewStateRand(cs *store.ChainStore, blks []cid.Cid, b beacon.Schedule, networkVersionGetter NetworkVersionGetter) Rand { return &stateRand{ cs: cs, blks: blks, @@ -203,12 +208,12 @@ func (sr *stateRand) DrawBeaconRandomness(ctx context.Context, pers crypto.Domai digest, err := sr.GetBeaconRandomness(ctx, filecoinEpoch) if err != nil { - return nil, xerrors.Errorf("failed to get chain randomness: %w", err) + return nil, xerrors.Errorf("failed to get beacon randomness: %w", err) } ret, err := DrawRandomnessFromDigest(digest, pers, filecoinEpoch, entropy) if err != nil { - return nil, xerrors.Errorf("failed to draw chain randomness: %w", err) + return nil, xerrors.Errorf("failed to draw beacon randomness: %w", err) } return ret, nil diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index c977e3883..85c82c6f1 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -509,7 +509,17 @@ func (sm *StateManager) GetRandomnessFromBeacon(ctx context.Context, personaliza r := rand.NewStateRand(sm.ChainStore(), pts.Cids(), sm.beacon, sm.GetNetworkVersion) - return r.DrawBeaconRandomness(ctx, personalization, randEpoch, entropy) + digest, err := r.GetBeaconRandomness(ctx, randEpoch) + if err != nil { + return nil, xerrors.Errorf("getting beacon randomness: %w", err) + } + + ret, err := rand.DrawRandomnessFromDigest(digest, personalization, randEpoch, entropy) + if err != nil { + return nil, xerrors.Errorf("drawing beacon randomness: %w", err) + } + + return ret, nil } @@ -521,7 +531,17 @@ func (sm *StateManager) GetRandomnessFromTickets(ctx context.Context, personaliz r := rand.NewStateRand(sm.ChainStore(), pts.Cids(), sm.beacon, sm.GetNetworkVersion) - return r.DrawChainRandomness(ctx, personalization, randEpoch, entropy) + digest, err := r.GetChainRandomness(ctx, randEpoch) + if err != nil { + return nil, xerrors.Errorf("getting chain randomness: %w", err) + } + + ret, err := rand.DrawRandomnessFromDigest(digest, personalization, randEpoch, entropy) + if err != nil { + return nil, xerrors.Errorf("drawing chain randomness: %w", err) + } + + return ret, nil } func (sm *StateManager) GetRandomnessDigestFromBeacon(ctx context.Context, randEpoch abi.ChainEpoch, tsk types.TipSetKey) ([32]byte, error) { diff --git a/chain/vm/fvm.go b/chain/vm/fvm.go index ed2e40cd9..bc4c3a851 100644 --- a/chain/vm/fvm.go +++ b/chain/vm/fvm.go @@ -33,6 +33,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/aerrors" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors/policy" + "github.com/filecoin-project/lotus/chain/rand" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/sigs" @@ -43,7 +44,7 @@ var _ Interface = (*FVM)(nil) var _ ffi_cgo.Externs = (*FvmExtern)(nil) type FvmExtern struct { - Rand + rand.Rand blockstore.Blockstore epoch abi.ChainEpoch lbState LookbackStateGetter diff --git a/chain/vm/vm.go b/chain/vm/vm.go index 9db811e94..ba404ab1f 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -31,6 +31,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin/account" "github.com/filecoin-project/lotus/chain/actors/builtin/reward" + "github.com/filecoin-project/lotus/chain/rand" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/metrics" @@ -222,7 +223,7 @@ type LegacyVM struct { buf *blockstore.BufferedBlockstore blockHeight abi.ChainEpoch areg *ActorRegistry - rand Rand + rand rand.Rand circSupplyCalc CircSupplyCalculator networkVersion network.Version baseFee abi.TokenAmount @@ -236,7 +237,7 @@ type VMOpts struct { StateBase cid.Cid Epoch abi.ChainEpoch Timestamp uint64 - Rand Rand + Rand rand.Rand Bstore blockstore.Blockstore Actors *ActorRegistry Syscalls SyscallBuilder diff --git a/chain/vm/vmi.go b/chain/vm/vmi.go index d022479f1..042621ca2 100644 --- a/chain/vm/vmi.go +++ b/chain/vm/vmi.go @@ -7,7 +7,6 @@ import ( cid "github.com/ipfs/go-cid" - "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/chain/types" @@ -69,8 +68,3 @@ func NewVM(ctx context.Context, opts *VMOpts) (Interface, error) { return newVMExecutor(vmi, opts.ExecutionLane), nil } - -type Rand interface { - GetChainRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) - GetBeaconRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) -} diff --git a/cmd/tvx/main.go b/cmd/tvx/main.go index b1541e4e1..ab3e37d55 100644 --- a/cmd/tvx/main.go +++ b/cmd/tvx/main.go @@ -10,13 +10,13 @@ import ( "github.com/filecoin-project/go-jsonrpc" - "github.com/filecoin-project/lotus/api/v0api" + "github.com/filecoin-project/lotus/api/v1api" lcli "github.com/filecoin-project/lotus/cli" ) // FullAPI is a JSON-RPC client targeting a full node. It's initialized in a // cli.BeforeFunc. -var FullAPI v0api.FullNode +var FullAPI v1api.FullNode // Closer is the closer for the JSON-RPC client, which must be called on // cli.AfterFunc. diff --git a/conformance/driver.go b/conformance/driver.go index eb5973f72..3c62ca7b9 100644 --- a/conformance/driver.go +++ b/conformance/driver.go @@ -23,6 +23,7 @@ import ( "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" "github.com/filecoin-project/lotus/chain/index" + "github.com/filecoin-project/lotus/chain/rand" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" @@ -89,9 +90,9 @@ type ExecuteTipsetParams struct { ParentEpoch abi.ChainEpoch Tipset *schema.Tipset ExecEpoch abi.ChainEpoch - // Rand is an optional vm.Rand implementation to use. If nil, the driver - // will use a vm.Rand that returns a fixed value for all calls. - Rand vm.Rand + // Rand is an optional rand.Rand implementation to use. If nil, the driver + // will use a rand.Rand that returns a fixed value for all calls. + Rand rand.Rand // BaseFee if not nil or zero, will override the basefee of the tipset. BaseFee abi.TokenAmount } @@ -200,9 +201,9 @@ type ExecuteMessageParams struct { BaseFee abi.TokenAmount NetworkVersion network.Version - // Rand is an optional vm.Rand implementation to use. If nil, the driver - // will use a vm.Rand that returns a fixed value for all calls. - Rand vm.Rand + // Rand is an optional rand.Rand implementation to use. If nil, the driver + // will use a rand.Rand that returns a fixed value for all calls. + Rand rand.Rand // Lookback is the LookbackStateGetter; returns the state tree at a given epoch. Lookback vm.LookbackStateGetter diff --git a/conformance/rand_fixed.go b/conformance/rand_fixed.go index 773cf0c65..f35f05cd4 100644 --- a/conformance/rand_fixed.go +++ b/conformance/rand_fixed.go @@ -5,16 +5,16 @@ import ( "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/lotus/chain/vm" + "github.com/filecoin-project/lotus/chain/rand" ) type fixedRand struct{} -var _ vm.Rand = (*fixedRand)(nil) +var _ rand.Rand = (*fixedRand)(nil) // NewFixedRand creates a test vm.Rand that always returns fixed bytes value // of utf-8 string 'i_am_random_____i_am_random_____'. -func NewFixedRand() vm.Rand { +func NewFixedRand() rand.Rand { return &fixedRand{} } diff --git a/conformance/rand_record.go b/conformance/rand_record.go index 20d8bf81d..4dc30b28e 100644 --- a/conformance/rand_record.go +++ b/conformance/rand_record.go @@ -9,8 +9,8 @@ import ( "github.com/filecoin-project/test-vectors/schema" "github.com/filecoin-project/lotus/api/v1api" + "github.com/filecoin-project/lotus/chain/rand" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/chain/vm" ) type RecordingRand struct { @@ -26,7 +26,7 @@ type RecordingRand struct { recorded schema.Randomness } -var _ vm.Rand = (*RecordingRand)(nil) +var _ rand.Rand = (*RecordingRand)(nil) // NewRecordingRand returns a vm.Rand implementation that proxies calls to a // full Lotus node via JSON-RPC, and records matching rules and responses so diff --git a/conformance/rand_replay.go b/conformance/rand_replay.go index 13aae739b..6d78d813b 100644 --- a/conformance/rand_replay.go +++ b/conformance/rand_replay.go @@ -6,16 +6,16 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/test-vectors/schema" - "github.com/filecoin-project/lotus/chain/vm" + "github.com/filecoin-project/lotus/chain/rand" ) type ReplayingRand struct { reporter Reporter recorded schema.Randomness - fallback vm.Rand + fallback rand.Rand } -var _ vm.Rand = (*ReplayingRand)(nil) +var _ rand.Rand = (*ReplayingRand)(nil) // NewReplayingRand replays recorded randomness when requested, falling back to // fixed randomness if the value cannot be found; hence this is a safe From 2f113e58ca41a42f1030b5264111161fef5c03f3 Mon Sep 17 00:00:00 2001 From: Aayush Date: Tue, 22 Aug 2023 11:51:00 -0400 Subject: [PATCH 8/8] fix tvx --- cmd/tvx/extract_message.go | 17 +++++++++-------- cmd/tvx/main.go | 2 +- cmd/tvx/state.go | 11 ++++++----- cmd/tvx/stores.go | 6 +++--- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/cmd/tvx/extract_message.go b/cmd/tvx/extract_message.go index 8ff8a2b79..95711414b 100644 --- a/cmd/tvx/extract_message.go +++ b/cmd/tvx/extract_message.go @@ -15,7 +15,8 @@ import ( "github.com/filecoin-project/test-vectors/schema" "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/api/v0api" + lapi "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/api/v1api" "github.com/filecoin-project/lotus/chain/actors/builtin" init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" "github.com/filecoin-project/lotus/chain/actors/builtin/reward" @@ -207,7 +208,7 @@ func doExtractMessage(opts extractOpts) error { // TODO sometimes this returns a nil receipt and no error ¯\_(ツ)_/¯ // ex: https://filfox.info/en/message/bafy2bzacebpxw3yiaxzy2bako62akig46x3imji7fewszen6fryiz6nymu2b2 // This code is lenient and skips receipt comparison in case of a nil receipt. - rec, err := FullAPI.StateGetReceipt(ctx, mcid, execTs.Key()) + rec, err := FullAPI.StateSearchMsg(ctx, execTs.Key(), mcid, api.LookbackNoLimit, false) if err != nil { return fmt.Errorf("failed to find receipt on chain: %w", err) } @@ -217,9 +218,9 @@ func doExtractMessage(opts extractOpts) error { var receipt *schema.Receipt if rec != nil { receipt = &schema.Receipt{ - ExitCode: int64(rec.ExitCode), - ReturnValue: rec.Return, - GasUsed: rec.GasUsed, + ExitCode: int64(rec.Receipt.ExitCode), + ReturnValue: rec.Receipt.Return, + GasUsed: rec.Receipt.GasUsed, } reporter := new(conformance.LogReporter) @@ -326,7 +327,7 @@ func doExtractMessage(opts extractOpts) error { // resolveFromChain queries the chain for the provided message, using the block CID to // speed up the query, if provided -func resolveFromChain(ctx context.Context, api v0api.FullNode, mcid cid.Cid, block string) (msg *types.Message, execTs *types.TipSet, incTs *types.TipSet, err error) { +func resolveFromChain(ctx context.Context, api lapi.FullNode, mcid cid.Cid, block string) (msg *types.Message, execTs *types.TipSet, incTs *types.TipSet, err error) { // Extract the full message. msg, err = api.ChainGetMessage(ctx, mcid) if err != nil { @@ -339,7 +340,7 @@ func resolveFromChain(ctx context.Context, api v0api.FullNode, mcid cid.Cid, blo log.Printf("locating message in blockchain") // Locate the message. - msgInfo, err := api.StateSearchMsg(ctx, mcid) + msgInfo, err := api.StateSearchMsg(ctx, types.EmptyTSK, mcid, lapi.LookbackNoLimit, false) if err != nil { return nil, nil, nil, fmt.Errorf("failed to locate message: %w", err) } @@ -384,7 +385,7 @@ func resolveFromChain(ctx context.Context, api v0api.FullNode, mcid cid.Cid, blo // as the previous tipset. In the context of vector generation, the target // tipset is the one where a message was executed, and the previous tipset is // the one where the message was included. -func fetchThisAndPrevTipset(ctx context.Context, api v0api.FullNode, target types.TipSetKey) (targetTs *types.TipSet, prevTs *types.TipSet, err error) { +func fetchThisAndPrevTipset(ctx context.Context, api v1api.FullNode, target types.TipSetKey) (targetTs *types.TipSet, prevTs *types.TipSet, err error) { // get the tipset on which this message was "executed" on. // https://github.com/filecoin-project/lotus/issues/2847 targetTs, err = api.ChainGetTipSet(ctx, target) diff --git a/cmd/tvx/main.go b/cmd/tvx/main.go index ab3e37d55..5021dd64b 100644 --- a/cmd/tvx/main.go +++ b/cmd/tvx/main.go @@ -102,7 +102,7 @@ func initialize(c *cli.Context) error { // Make the API client. var err error - if FullAPI, Closer, err = lcli.GetFullNodeAPI(c); err != nil { + if FullAPI, Closer, err = lcli.GetFullNodeAPIV1(c); err != nil { err = fmt.Errorf("failed to locate Lotus node; err: %w", err) } return err diff --git a/cmd/tvx/state.go b/cmd/tvx/state.go index 120eddd6b..9674bf17e 100644 --- a/cmd/tvx/state.go +++ b/cmd/tvx/state.go @@ -14,7 +14,8 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/lotus/api/v0api" + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/api/v1api" init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/types" @@ -24,13 +25,13 @@ import ( // StateSurgeon is an object used to fetch and manipulate state. type StateSurgeon struct { ctx context.Context - api v0api.FullNode + api v1api.FullNode stores *Stores } // NewSurgeon returns a state surgeon, an object used to fetch and manipulate // state. -func NewSurgeon(ctx context.Context, api v0api.FullNode, stores *Stores) *StateSurgeon { +func NewSurgeon(ctx context.Context, api v1api.FullNode, stores *Stores) *StateSurgeon { return &StateSurgeon{ ctx: ctx, api: api, @@ -86,9 +87,9 @@ func (sg *StateSurgeon) GetMaskedStateTree(previousRoot cid.Cid, retain []addres // GetAccessedActors identifies the actors that were accessed during the // execution of a message. -func (sg *StateSurgeon) GetAccessedActors(ctx context.Context, a v0api.FullNode, mid cid.Cid) ([]address.Address, error) { +func (sg *StateSurgeon) GetAccessedActors(ctx context.Context, a v1api.FullNode, mid cid.Cid) ([]address.Address, error) { log.Printf("calculating accessed actors during execution of message: %s", mid) - msgInfo, err := a.StateSearchMsg(ctx, mid) + msgInfo, err := a.StateSearchMsg(ctx, types.EmptyTSK, mid, api.LookbackNoLimit, false) if err != nil { return nil, err } diff --git a/cmd/tvx/stores.go b/cmd/tvx/stores.go index d4431a145..0ced44817 100644 --- a/cmd/tvx/stores.go +++ b/cmd/tvx/stores.go @@ -18,7 +18,7 @@ import ( format "github.com/ipfs/go-ipld-format" "golang.org/x/xerrors" - "github.com/filecoin-project/lotus/api/v0api" + "github.com/filecoin-project/lotus/api/v1api" "github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/chain/actors/adt" ) @@ -39,7 +39,7 @@ type Stores struct { // NewProxyingStores is a set of Stores backed by a proxying Blockstore that // proxies Get requests for unknown CIDs to a Filecoin node, via the // ChainReadObj RPC. -func NewProxyingStores(ctx context.Context, api v0api.FullNode) *Stores { +func NewProxyingStores(ctx context.Context, api v1api.FullNode) *Stores { ds := dssync.MutexWrap(ds.NewMapDatastore()) bs := &proxyingBlockstore{ ctx: ctx, @@ -84,7 +84,7 @@ type TracingBlockstore interface { // a Filecoin node via JSON-RPC. type proxyingBlockstore struct { ctx context.Context - api v0api.FullNode + api v1api.FullNode lk sync.Mutex tracing bool