package gen import ( "context" "github.com/filecoin-project/go-state-types/crypto" blockadt "github.com/filecoin-project/specs-actors/actors/util/adt" cid "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/sigs/bls" ffi "github.com/filecoin-project/filecoin-ffi" ) func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w api.WalletAPI, bt *api.BlockTemplate) (*types.FullBlock, error) { pts, err := sm.ChainStore().LoadTipSet(bt.Parents) if err != nil { return nil, xerrors.Errorf("failed to load parent tipset: %w", err) } st, recpts, err := sm.TipSetState(ctx, pts) if err != nil { return nil, xerrors.Errorf("failed to load tipset state: %w", err) } _, lbst, err := stmgr.GetLookbackTipSetForRound(ctx, sm, pts, bt.Epoch) if err != nil { return nil, xerrors.Errorf("getting lookback miner actor state: %w", err) } worker, err := stmgr.GetMinerWorkerRaw(ctx, sm, lbst, bt.Miner) if err != nil { return nil, xerrors.Errorf("failed to get miner worker: %w", err) } next := &types.BlockHeader{ Miner: bt.Miner, Parents: bt.Parents.Cids(), Ticket: bt.Ticket, ElectionProof: bt.Eproof, BeaconEntries: bt.BeaconValues, Height: bt.Epoch, Timestamp: bt.Timestamp, WinPoStProof: bt.WinningPoStProof, ParentStateRoot: st, ParentMessageReceipts: recpts, } var blsMessages []*types.Message var secpkMessages []*types.SignedMessage var blsMsgCids, secpkMsgCids []cid.Cid var blsSigs []crypto.Signature for _, msg := range bt.Messages { if msg.Signature.Type == crypto.SigTypeBLS { blsSigs = append(blsSigs, msg.Signature) blsMessages = append(blsMessages, &msg.Message) c, err := sm.ChainStore().PutMessage(&msg.Message) if err != nil { return nil, err } blsMsgCids = append(blsMsgCids, c) } else { c, err := sm.ChainStore().PutMessage(msg) if err != nil { return nil, err } secpkMsgCids = append(secpkMsgCids, c) secpkMessages = append(secpkMessages, msg) } } store := sm.ChainStore().Store(ctx) blsmsgroot, err := toArray(store, blsMsgCids) if err != nil { return nil, xerrors.Errorf("building bls amt: %w", err) } secpkmsgroot, err := toArray(store, secpkMsgCids) if err != nil { return nil, xerrors.Errorf("building secpk amt: %w", err) } mmcid, err := store.Put(store.Context(), &types.MsgMeta{ BlsMessages: blsmsgroot, SecpkMessages: secpkmsgroot, }) if err != nil { return nil, err } next.Messages = mmcid aggSig, err := aggregateSignatures(blsSigs) if err != nil { return nil, err } next.BLSAggregate = aggSig pweight, err := sm.ChainStore().Weight(ctx, pts) if err != nil { return nil, err } next.ParentWeight = pweight baseFee, err := sm.ChainStore().ComputeBaseFee(ctx, pts) if err != nil { return nil, xerrors.Errorf("computing base fee: %w", err) } next.ParentBaseFee = baseFee nosigbytes, err := next.SigningBytes() if err != nil { return nil, xerrors.Errorf("failed to get signing bytes for block: %w", err) } sig, err := w.WalletSign(ctx, worker, nosigbytes, api.MsgMeta{ Type: api.MTBlock, }) if err != nil { return nil, xerrors.Errorf("failed to sign new block: %w", err) } next.BlockSig = sig fullBlock := &types.FullBlock{ Header: next, BlsMessages: blsMessages, SecpkMessages: secpkMessages, } return fullBlock, nil } func aggregateSignatures(sigs []crypto.Signature) (*crypto.Signature, error) { sigsS := make([]ffi.Signature, len(sigs)) for i := 0; i < len(sigs); i++ { copy(sigsS[i][:], sigs[i].Data[:ffi.SignatureBytes]) } aggSig := ffi.Aggregate(sigsS) if aggSig == nil { if len(sigs) > 0 { return nil, xerrors.Errorf("bls.Aggregate returned nil with %d signatures", len(sigs)) } // Note: for blst this condition should not happen - nil should not // be returned return &crypto.Signature{ Type: crypto.SigTypeBLS, Data: ([]byte)(new(bls.Signature)[:]), // TODO: verify this is okay }, nil } return &crypto.Signature{ Type: crypto.SigTypeBLS, Data: ([]byte)(aggSig[:]), }, nil } func toArray(store blockadt.Store, cids []cid.Cid) (cid.Cid, error) { arr := blockadt.MakeEmptyArray(store) for i, c := range cids { oc := cbg.CborCid(c) if err := arr.Set(uint64(i), &oc); err != nil { return cid.Undef, err } } return arr.Root() }