Merge pull request #159 from filecoin-project/feat/block-sig

add signatures to blocks
This commit is contained in:
Whyrusleeping 2019-08-27 13:39:09 -07:00 committed by GitHub
commit ec9ad75dbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 155 additions and 21 deletions

View File

@ -68,7 +68,7 @@ type FullNode interface {
MinerRegister(context.Context, address.Address) error
MinerUnregister(context.Context, address.Address) error
MinerAddresses(context.Context) ([]address.Address, error)
MinerCreateBlock(context.Context, address.Address, *types.TipSet, []*types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error)
MinerCreateBlock(context.Context, address.Address, *types.TipSet, []*types.Ticket, types.ElectionProof, []*types.SignedMessage, uint64) (*chain.BlockMsg, error)
// // UX ?

View File

@ -57,7 +57,7 @@ type FullNodeStruct struct {
MinerRegister func(context.Context, address.Address) error `perm:"admin"`
MinerUnregister func(context.Context, address.Address) error `perm:"admin"`
MinerAddresses func(context.Context) ([]address.Address, error) `perm:"write"`
MinerCreateBlock func(context.Context, address.Address, *types.TipSet, []*types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error) `perm:"write"`
MinerCreateBlock func(context.Context, address.Address, *types.TipSet, []*types.Ticket, types.ElectionProof, []*types.SignedMessage, uint64) (*chain.BlockMsg, error) `perm:"write"`
WalletNew func(context.Context, string) (address.Address, error) `perm:"write"`
WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"`
@ -174,8 +174,8 @@ func (c *FullNodeStruct) MinerAddresses(ctx context.Context) ([]address.Address,
return c.Internal.MinerAddresses(ctx)
}
func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base *types.TipSet, tickets []*types.Ticket, eproof types.ElectionProof, msgs []*types.SignedMessage) (*chain.BlockMsg, error) {
return c.Internal.MinerCreateBlock(ctx, addr, base, tickets, eproof, msgs)
func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base *types.TipSet, tickets []*types.Ticket, eproof types.ElectionProof, msgs []*types.SignedMessage, ts uint64) (*chain.BlockMsg, error) {
return c.Internal.MinerCreateBlock(ctx, addr, base, tickets, eproof, msgs, ts)
}
func (c *FullNodeStruct) ChainSubmitBlock(ctx context.Context, blk *chain.BlockMsg) error {

View File

@ -522,6 +522,7 @@ func (t *BlockMsg) UnmarshalCBOR(br io.Reader) error {
// t.t.Header (types.BlockHeader)
t.Header = new(types.BlockHeader)
if err := t.Header.UnmarshalCBOR(br); err != nil {
return err
}

View File

@ -253,7 +253,7 @@ func (cg *ChainGen) NextBlock() (*types.FullBlock, []*types.SignedMessage, error
return nil, nil, err
}
fblk, err := MinerCreateBlock(context.TODO(), cg.cs, miner, parents, tickets, proof, msgs)
fblk, err := MinerCreateBlock(context.TODO(), cg.cs, cg.w, miner, parents, tickets, proof, msgs, 0)
if err != nil {
return nil, nil, err
}

View File

@ -8,15 +8,17 @@ import (
hamt "github.com/ipfs/go-hamt-ipld"
"github.com/pkg/errors"
sharray "github.com/whyrusleeping/sharray"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm"
"github.com/filecoin-project/go-lotus/chain/wallet"
)
func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, miner address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage) (*types.FullBlock, error) {
func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, w *wallet.Wallet, miner address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage, timestamp uint64) (*types.FullBlock, error) {
st, err := cs.TipSetState(parents.Cids())
if err != nil {
return nil, errors.Wrap(err, "failed to load tipset state")
@ -29,8 +31,18 @@ func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, miner address.A
return nil, err
}
owner, err := getMinerOwner(ctx, cs, st, miner)
if err != nil {
return nil, xerrors.Errorf("failed to get miner owner: %w", err)
}
worker, err := getMinerWorker(ctx, cs, st, miner)
if err != nil {
return nil, xerrors.Errorf("failed to get miner worker: %w", err)
}
// apply miner reward
if err := vmi.TransferFunds(actors.NetworkAddress, miner, vm.MiningRewardForBlock(parents)); err != nil {
if err := vmi.TransferFunds(actors.NetworkAddress, owner, vm.MiningRewardForBlock(parents)); err != nil {
return nil, err
}
@ -121,6 +133,25 @@ func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, miner address.A
pweight := cs.Weight(parents)
next.ParentWeight = types.NewInt(pweight)
// TODO: set timestamp
nosigbytes, err := next.Serialize()
if err != nil {
return nil, xerrors.Errorf("failed to serialize block header with no signature: %w", err)
}
waddr, err := vm.ResolveToKeyAddr(vmi.StateTree(), cst, worker)
if err != nil {
return nil, xerrors.Errorf("failed to resolve miner address to key address: %w", err)
}
sig, err := w.Sign(ctx, waddr, nosigbytes)
if err != nil {
return nil, xerrors.Errorf("failed to sign new block: %w", err)
}
next.BlockSig = *sig
fullBlock := &types.FullBlock{
Header: next,
BlsMessages: blsMessages,
@ -130,6 +161,40 @@ func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, miner address.A
return fullBlock, nil
}
func getMinerWorker(ctx context.Context, cs *store.ChainStore, state cid.Cid, maddr address.Address) (address.Address, error) {
rec, err := vm.CallRaw(ctx, cs, &types.Message{
To: maddr,
From: maddr,
Method: actors.MAMethods.GetWorkerAddr,
}, state, 0)
if err != nil {
return address.Undef, err
}
if rec.ExitCode != 0 {
return address.Undef, xerrors.Errorf("getWorker failed with exit code %d", rec.ExitCode)
}
return address.NewFromBytes(rec.Return)
}
func getMinerOwner(ctx context.Context, cs *store.ChainStore, state cid.Cid, maddr address.Address) (address.Address, error) {
rec, err := vm.CallRaw(ctx, cs, &types.Message{
To: maddr,
From: maddr,
Method: actors.MAMethods.GetOwner,
}, state, 0)
if err != nil {
return address.Undef, err
}
if rec.ExitCode != 0 {
return address.Undef, xerrors.Errorf("getOwner failed with exit code %d", rec.ExitCode)
}
return address.NewFromBytes(rec.Return)
}
func aggregateSignatures(sigs []types.Signature) (types.Signature, error) {
var blsSigs []bls.Signature
for _, s := range sigs {

View File

@ -304,6 +304,7 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B
Messages: mmb.Cid(),
MessageReceipts: emptyroot,
BLSAggregate: types.Signature{Type: types.KTBLS, Data: []byte("signatureeee")},
BlockSig: types.Signature{Type: types.KTBLS, Data: []byte("block signatureeee")},
}
sb, err := b.ToStorageBlock()

View File

@ -395,6 +395,32 @@ func getMinerWorker(ctx context.Context, cs *store.ChainStore, st cid.Cid, maddr
return worker, nil
}
func getMinerOwner(ctx context.Context, cs *store.ChainStore, st cid.Cid, maddr address.Address) (address.Address, error) {
recp, err := vm.CallRaw(ctx, cs, &types.Message{
To: maddr,
From: maddr,
Method: actors.MAMethods.GetOwner,
}, st, 0)
if err != nil {
return address.Undef, xerrors.Errorf("callRaw failed: %w", err)
}
if recp.ExitCode != 0 {
return address.Undef, xerrors.Errorf("getting miner owner addr failed (exit code %d)", recp.ExitCode)
}
owner, err := address.NewFromBytes(recp.Return)
if err != nil {
return address.Undef, err
}
if owner.Protocol() == address.ID {
return address.Undef, xerrors.Errorf("need to resolve owner address to a pubkeyaddr")
}
return owner, nil
}
// Should match up with 'Semantical Validation' in validation.md in the spec
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error {
h := b.Header
@ -426,7 +452,12 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
return xerrors.Errorf("failed to instantiate VM: %w", err)
}
if err := vmi.TransferFunds(actors.NetworkAddress, b.Header.Miner, vm.MiningRewardForBlock(baseTs)); err != nil {
owner, err := getMinerOwner(ctx, syncer.store, stateroot, b.Header.Miner)
if err != nil {
return xerrors.Errorf("getting miner owner for block miner failed: %w", err)
}
if err := vmi.TransferFunds(actors.NetworkAddress, owner, vm.MiningRewardForBlock(baseTs)); err != nil {
return xerrors.Errorf("fund transfer failed: %w", err)
}

View File

@ -39,6 +39,10 @@ type BlockHeader struct {
BLSAggregate Signature
MessageReceipts cid.Cid
Timestamp uint64
BlockSig Signature
}
func (b *BlockHeader) ToStorageBlock() (block.Block, error) {

View File

@ -39,6 +39,7 @@ func testBlockHeader(t testing.TB) *BlockHeader {
Messages: c,
Height: 85919298723,
StateRoot: c,
BlockSig: Signature{Type: KTBLS, Data: []byte("boo! im a signature")},
}
}

View File

@ -14,7 +14,7 @@ import (
var _ = xerrors.Errorf
func (t *BlockHeader) MarshalCBOR(w io.Writer) error {
if _, err := w.Write([]byte{138}); err != nil {
if _, err := w.Write([]byte{140}); err != nil {
return err
}
@ -80,6 +80,16 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error {
if err := cbg.WriteCid(w, t.MessageReceipts); err != nil {
return xerrors.Errorf("failed to write cid field t.MessageReceipts: %w", err)
}
// t.t.Timestamp (uint64)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.Timestamp)); err != nil {
return err
}
// t.t.BlockSig (types.Signature)
if err := t.BlockSig.MarshalCBOR(w); err != nil {
return err
}
return nil
}
@ -93,7 +103,7 @@ func (t *BlockHeader) UnmarshalCBOR(br io.Reader) error {
return fmt.Errorf("cbor input should be of type array")
}
if extra != 10 {
if extra != 12 {
return fmt.Errorf("cbor input had wrong number of fields")
}
@ -216,6 +226,21 @@ func (t *BlockHeader) UnmarshalCBOR(br io.Reader) error {
}
t.MessageReceipts = c
}
// t.t.Timestamp (uint64)
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.Timestamp = extra
// t.t.BlockSig (types.Signature)
if err := t.BlockSig.UnmarshalCBOR(br); err != nil {
return err
}
return nil
}

View File

@ -23,6 +23,8 @@ const (
)
const (
IKTUnknown = -1
IKTSecp256k1 = iota
IKTBLS
)
@ -117,6 +119,8 @@ func (s *Signature) TypeCode() int {
return IKTSecp256k1
case KTBLS:
return IKTBLS
case "":
return IKTUnknown
default:
panic("unsupported signature type")
}

View File

@ -158,7 +158,7 @@ func (vmctx *VMContext) VerifySignature(sig *types.Signature, act address.Addres
}
if act.Protocol() == address.ID {
kaddr, err := vmctx.resolveToKeyAddr(act)
kaddr, err := ResolveToKeyAddr(vmctx.state, vmctx.cst, act)
if err != nil {
return aerrors.Wrap(err, "failed to resolve address to key address")
}
@ -172,12 +172,12 @@ func (vmctx *VMContext) VerifySignature(sig *types.Signature, act address.Addres
return nil
}
func (vmctx *VMContext) resolveToKeyAddr(addr address.Address) (address.Address, aerrors.ActorError) {
func ResolveToKeyAddr(state types.StateTree, cst *hamt.CborIpldStore, addr address.Address) (address.Address, aerrors.ActorError) {
if addr.Protocol() == address.BLS || addr.Protocol() == address.SECP256K1 {
return addr, nil
}
act, err := vmctx.state.GetActor(addr)
act, err := state.GetActor(addr)
if err != nil {
return address.Undef, aerrors.Newf(1, "failed to find actor: %s", addr)
}
@ -187,7 +187,7 @@ func (vmctx *VMContext) resolveToKeyAddr(addr address.Address) (address.Address,
}
var aast actors.AccountActorState
if err := vmctx.cst.Get(context.TODO(), act.Head, &aast); err != nil {
if err := cst.Get(context.TODO(), act.Head, &aast); err != nil {
return address.Undef, aerrors.Escalate(err, fmt.Sprintf("failed to get account actor state for %s", addr))
}
@ -195,7 +195,6 @@ func (vmctx *VMContext) resolveToKeyAddr(addr address.Address) (address.Address,
}
func (vm *VM) makeVMContext(ctx context.Context, sroot cid.Cid, msg *types.Message, origin address.Address, usedGas types.BigInt) *VMContext {
return &VMContext{
ctx: ctx,
vm: vm,

1
go.mod
View File

@ -64,6 +64,7 @@ require (
github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a
github.com/smartystreets/assertions v1.0.1 // indirect
github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect
github.com/stretchr/objx v0.1.1 // indirect
github.com/stretchr/testify v1.3.0
github.com/whyrusleeping/cbor-gen v0.0.0-20190822012446-bb2210dd2804
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7

View File

@ -336,7 +336,7 @@ func (m *Miner) createBlock(base *MiningBase, ticket *types.Ticket, proof types.
msgs := m.selectMessages(pending)
// why even return this? that api call could just submit it for us
return m.api.MinerCreateBlock(context.TODO(), m.addresses[0], base.ts, append(base.tickets, ticket), proof, msgs)
return m.api.MinerCreateBlock(context.TODO(), m.addresses[0], base.ts, append(base.tickets, ticket), proof, msgs, 0)
}
func (m *Miner) selectMessages(msgs []*types.SignedMessage) []*types.SignedMessage {

View File

@ -23,6 +23,8 @@ import (
type ChainAPI struct {
fx.In
WalletAPI
Chain *store.ChainStore
PubSub *pubsub.PubSub
}
@ -163,8 +165,8 @@ func (a *ChainAPI) ChainReadState(ctx context.Context, act *types.Actor, ts *typ
}
// This is on ChainAPI because miner.Miner requires this, and MinerAPI requires miner.Miner
func (a *ChainAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage) (*chain.BlockMsg, error) {
fblk, err := gen.MinerCreateBlock(ctx, a.Chain, addr, parents, tickets, proof, msgs)
func (a *ChainAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage, ts uint64) (*chain.BlockMsg, error) {
fblk, err := gen.MinerCreateBlock(ctx, a.Chain, a.Wallet, addr, parents, tickets, proof, msgs, ts)
if err != nil {
return nil, err
}