diff --git a/api/api.go b/api/api.go index 4b308520c..8e523d874 100644 --- a/api/api.go +++ b/api/api.go @@ -87,7 +87,7 @@ type FullNode interface { WalletNew(context.Context, string) (address.Address, error) WalletList(context.Context) ([]address.Address, error) WalletBalance(context.Context, address.Address) (types.BigInt, error) - WalletSign(context.Context, address.Address, []byte) (*chain.Signature, error) + WalletSign(context.Context, address.Address, []byte) (*types.Signature, error) WalletDefaultAddress(context.Context) (address.Address, error) // Really not sure where this belongs. It could go on the wallet, or the message pool, or the chain... diff --git a/api/struct.go b/api/struct.go index d9a629b35..921ded8e6 100644 --- a/api/struct.go +++ b/api/struct.go @@ -49,7 +49,7 @@ type FullNodeStruct struct { WalletNew func(context.Context, string) (address.Address, error) `perm:"write"` WalletList func(context.Context) ([]address.Address, error) `perm:"write"` WalletBalance func(context.Context, address.Address) (types.BigInt, error) `perm:"read"` - WalletSign func(context.Context, address.Address, []byte) (*chain.Signature, error) `perm:"sign"` + WalletSign func(context.Context, address.Address, []byte) (*types.Signature, error) `perm:"sign"` WalletDefaultAddress func(context.Context) (address.Address, error) `perm:"write"` MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"` @@ -147,7 +147,7 @@ func (c *FullNodeStruct) WalletBalance(ctx context.Context, a address.Address) ( return c.Internal.WalletBalance(ctx, a) } -func (c *FullNodeStruct) WalletSign(ctx context.Context, k address.Address, msg []byte) (*chain.Signature, error) { +func (c *FullNodeStruct) WalletSign(ctx context.Context, k address.Address, msg []byte) (*types.Signature, error) { return c.Internal.WalletSign(ctx, k, msg) } diff --git a/chain/actors/actor_paych.go b/chain/actors/actor_paych.go new file mode 100644 index 000000000..defe072c1 --- /dev/null +++ b/chain/actors/actor_paych.go @@ -0,0 +1,281 @@ +package actors + +import ( + "bytes" + + "github.com/filecoin-project/go-lotus/chain/actors/aerrors" + "github.com/filecoin-project/go-lotus/chain/address" + "github.com/filecoin-project/go-lotus/chain/types" + + cbor "github.com/ipfs/go-ipld-cbor" +) + +const ChannelClosingDelay = 6 * 60 * 2 // six hours + +func init() { + cbor.RegisterCborType(PaymentChannelActorState{}) + cbor.RegisterCborType(PCAConstructorParams{}) + cbor.RegisterCborType(SignedVoucher{}) + cbor.RegisterCborType(Merge{}) + cbor.RegisterCborType(LaneState{}) + cbor.RegisterCborType(UpdateChannelState{}) +} + +type PaymentChannelActor struct{} + +type LaneState struct { + Closed bool + Redeemed types.BigInt + Nonce uint64 +} + +type PaymentChannelActorState struct { + From address.Address + To address.Address + + ChannelTotal types.BigInt + ToSend types.BigInt + + ClosingAt uint64 + MinCloseHeight uint64 + + LaneStates map[uint64]*LaneState + + VerifActor address.Address + VerifMethod uint64 +} + +func (pca PaymentChannelActor) Exports() []interface{} { + return []interface{}{ + 0: pca.Constructor, + 1: pca.UpdateChannelState, + 2: pca.Close, + 3: pca.Collect, + } +} + +type PCAConstructorParams struct { + To address.Address + VerifActor address.Address + VerifMethod uint64 +} + +func (pca PaymentChannelActor) Constructor(act *types.Actor, vmctx types.VMContext, params *PCAConstructorParams) ([]byte, ActorError) { + var self PaymentChannelActorState + self.From = vmctx.Origin() + self.To = params.To + self.VerifActor = params.VerifActor + self.VerifMethod = params.VerifMethod + + storage := vmctx.Storage() + c, err := storage.Put(self) + if err != nil { + return nil, err + } + + if err := storage.Commit(EmptyCBOR, c); err != nil { + return nil, err + } + + return nil, nil +} + +type SignedVoucher struct { + TimeLock uint64 + SecretPreimage []byte + Extra []byte + Lane uint64 + Nonce uint64 + Amount types.BigInt + MinCloseHeight uint64 + + Merges []Merge + + Signature types.Signature +} + +type Merge struct { + Lane uint64 + Nonce uint64 +} + +type UpdateChannelState struct { + Sv SignedVoucher + Secret []byte + Proof []byte +} + +func hash(b []byte) []byte { + panic("blake 2b hash pls") +} + +func (pca PaymentChannelActor) UpdateChannelState(act *types.Actor, vmctx types.VMContext, params *UpdateChannelState) ([]byte, ActorError) { + var self PaymentChannelActorState + oldstate := vmctx.Storage().GetHead() + storage := vmctx.Storage() + if err := storage.Get(oldstate, &self); err != nil { + return nil, err + } + + sv := params.Sv + + if err := vmctx.VerifySignature(sv.Signature, self.From); err != nil { + return nil, err + } + + if vmctx.BlockHeight() < sv.TimeLock { + return nil, aerrors.New(2, "cannot use this voucher yet!") + } + + if sv.SecretPreimage != nil { + if !bytes.Equal(hash(params.Secret), sv.SecretPreimage) { + return nil, aerrors.New(3, "Incorrect secret!") + } + } + + if sv.Extra != nil { + if self.VerifActor == address.Undef { + return nil, aerrors.New(4, "no verifActor for extra data") + } + + encoded, err := SerializeParams([]interface{}{sv.Extra, params.Proof}) + if err != nil { + return nil, err + } + + _, err = vmctx.Send(self.VerifActor, self.VerifMethod, types.NewInt(0), encoded) + if err != nil { + return nil, aerrors.New(5, "spend voucher verification failed") + } + } + + ls := self.LaneStates[sv.Lane] + if ls.Closed { + return nil, aerrors.New(6, "cannot redeem a voucher on a closed lane") + } + + if ls.Nonce > sv.Nonce { + return nil, aerrors.New(7, "voucher has an outdated nonce, cannot redeem") + } + + mergeValue := types.NewInt(0) + for _, merge := range sv.Merges { + if merge.Lane == sv.Lane { + return nil, aerrors.New(8, "voucher cannot merge its own lane") + } + + ols := self.LaneStates[merge.Lane] + + if ols.Nonce >= merge.Nonce { + return nil, aerrors.New(9, "merge in voucher has outdated nonce, cannot redeem") + } + + mergeValue = types.BigAdd(mergeValue, ols.Redeemed) + ols.Nonce = merge.Nonce + } + + ls.Nonce = sv.Nonce + balanceDelta := types.BigSub(sv.Amount, types.BigAdd(mergeValue, ls.Redeemed)) + ls.Redeemed = sv.Amount + + newSendBalance := types.BigAdd(self.ToSend, balanceDelta) + if types.BigCmp(newSendBalance, types.NewInt(0)) < 0 { + // TODO: is this impossible? + return nil, aerrors.New(10, "voucher would leave channel balance negative") + } + + if types.BigCmp(newSendBalance, self.ChannelTotal) > 0 { + return nil, aerrors.New(11, "not enough funds in channel to cover voucher") + } + + self.ToSend = newSendBalance + + if sv.MinCloseHeight != 0 { + if self.ClosingAt != 0 && self.ClosingAt < sv.MinCloseHeight { + self.ClosingAt = sv.MinCloseHeight + } + if self.MinCloseHeight < sv.MinCloseHeight { + self.MinCloseHeight = sv.MinCloseHeight + } + } + + ncid, err := storage.Put(self) + if err != nil { + return nil, err + } + if err := storage.Commit(oldstate, ncid); err != nil { + return nil, err + } + + return nil, nil +} + +func (pca PaymentChannelActor) Close(act *types.Actor, vmctx types.VMContext, params struct{}) ([]byte, aerrors.ActorError) { + var self PaymentChannelActorState + storage := vmctx.Storage() + oldstate := storage.GetHead() + if err := storage.Get(oldstate, &self); err != nil { + return nil, err + } + + if vmctx.Message().From != self.From && vmctx.Message().From != self.To { + return nil, aerrors.New(1, "not authorized to close channel") + } + + if self.ClosingAt != 0 { + return nil, aerrors.New(2, "channel already closing") + } + + self.ClosingAt = vmctx.BlockHeight() + ChannelClosingDelay + if self.ClosingAt < self.MinCloseHeight { + self.ClosingAt = self.MinCloseHeight + } + + ncid, err := storage.Put(self) + if err != nil { + return nil, err + } + if err := storage.Commit(oldstate, ncid); err != nil { + return nil, err + } + + return nil, nil +} + +func (pca PaymentChannelActor) Collect(act *types.Actor, vmctx types.VMContext, params struct{}) ([]byte, aerrors.ActorError) { + var self PaymentChannelActorState + storage := vmctx.Storage() + oldstate := storage.GetHead() + if err := storage.Get(oldstate, &self); err != nil { + return nil, err + } + + if self.ClosingAt == 0 { + return nil, aerrors.New(1, "payment channel not closing or closed") + } + + if vmctx.BlockHeight() < self.ClosingAt { + return nil, aerrors.New(2, "payment channel not closed yet") + } + _, err := vmctx.Send(self.From, 0, types.BigSub(self.ChannelTotal, self.ToSend), nil) + if err != nil { + return nil, err + } + _, err = vmctx.Send(self.To, 0, self.ToSend, nil) + if err != nil { + return nil, err + } + + self.ChannelTotal = types.NewInt(0) + self.ToSend = types.NewInt(0) + + ncid, err := storage.Put(self) + if err != nil { + return nil, err + } + if err := storage.Commit(oldstate, ncid); err != nil { + return nil, err + } + + return nil, nil +} diff --git a/chain/chain.go b/chain/chain.go index 61ed7dea7..319e37111 100644 --- a/chain/chain.go +++ b/chain/chain.go @@ -161,7 +161,7 @@ func SetupStorageMarketActor(bs bstore.Blockstore) (*types.Actor, error) { func MakeGenesisBlock(bs bstore.Blockstore, w *Wallet) (*GenesisBootstrap, error) { fmt.Println("at end of make Genesis block") - minerAddr, err := w.GenerateKey(KTSecp256k1) + minerAddr, err := w.GenerateKey(types.KTSecp256k1) if err != nil { return nil, err } diff --git a/chain/mining.go b/chain/mining.go index d0f8842c0..27b6877ec 100644 --- a/chain/mining.go +++ b/chain/mining.go @@ -46,7 +46,7 @@ func MinerCreateBlock(cs *ChainStore, miner address.Address, parents *TipSet, ti fmt.Printf("adding %d messages to block...", len(msgs)) var msgCids []cid.Cid - var blsSigs []Signature + var blsSigs []types.Signature var receipts []interface{} for _, msg := range msgs { if msg.Signature.TypeCode() == 2 { @@ -108,7 +108,7 @@ func MinerCreateBlock(cs *ChainStore, miner address.Address, parents *TipSet, ti return fullBlock, nil } -func aggregateSignatures(sigs []Signature) (Signature, error) { +func aggregateSignatures(sigs []types.Signature) (types.Signature, error) { var blsSigs []bls.Signature for _, s := range sigs { var bsig bls.Signature @@ -117,8 +117,8 @@ func aggregateSignatures(sigs []Signature) (Signature, error) { } aggSig := bls.Aggregate(blsSigs) - return Signature{ - Type: KTBLS, + return types.Signature{ + Type: types.KTBLS, Data: aggSig[:], }, nil } diff --git a/chain/types.go b/chain/types.go index c5e30c966..911ce69a0 100644 --- a/chain/types.go +++ b/chain/types.go @@ -1,7 +1,6 @@ package chain import ( - "encoding/binary" "encoding/json" "fmt" @@ -38,7 +37,7 @@ func init() { return SignedMessage{}, fmt.Errorf("signature in signed message was not bytes") } - sig, err := SignatureFromBytes(sigb) + sig, err := types.SignatureFromBytes(sigb) if err != nil { return SignedMessage{}, err } @@ -49,18 +48,6 @@ func init() { }, nil })). Complete()) - cbor.RegisterCborType(atlas.BuildEntry(Signature{}).Transform(). - TransformMarshal(atlas.MakeMarshalTransformFunc( - func(s Signature) ([]byte, error) { - buf := make([]byte, 4) - n := binary.PutUvarint(buf, uint64(s.TypeCode())) - return append(buf[:n], s.Data...), nil - })). - TransformUnmarshal(atlas.MakeUnmarshalTransformFunc( - func(x []byte) (Signature, error) { - return SignatureFromBytes(x) - })). - Complete()) cbor.RegisterCborType(atlas.BuildEntry(BlockHeader{}).UseTag(43).Transform(). TransformMarshal(atlas.MakeMarshalTransformFunc( func(blk BlockHeader) ([]interface{}, error) { @@ -140,7 +127,7 @@ type BlockHeader struct { Messages cid.Cid - BLSAggregate Signature + BLSAggregate types.Signature MessageReceipts cid.Cid } @@ -208,7 +195,7 @@ func (m *SignedMessage) Cid() cid.Cid { type SignedMessage struct { Message types.Message - Signature Signature + Signature types.Signature } func DecodeSignedMessage(data []byte) (*SignedMessage, error) { diff --git a/chain/types/signature.go b/chain/types/signature.go new file mode 100644 index 000000000..67fa4d114 --- /dev/null +++ b/chain/types/signature.go @@ -0,0 +1,92 @@ +package types + +import ( + "encoding/binary" + "fmt" + + "github.com/filecoin-project/go-lotus/chain/address" + "github.com/filecoin-project/go-lotus/lib/crypto" + cbor "github.com/ipfs/go-ipld-cbor" + "github.com/minio/blake2b-simd" + "github.com/polydawn/refmt/obj/atlas" +) + +const ( + KTSecp256k1 = "secp256k1" + KTBLS = "bls" +) + +func init() { + cbor.RegisterCborType(atlas.BuildEntry(Signature{}).Transform(). + TransformMarshal(atlas.MakeMarshalTransformFunc( + func(s Signature) ([]byte, error) { + buf := make([]byte, 4) + n := binary.PutUvarint(buf, uint64(s.TypeCode())) + return append(buf[:n], s.Data...), nil + })). + TransformUnmarshal(atlas.MakeUnmarshalTransformFunc( + func(x []byte) (Signature, error) { + return SignatureFromBytes(x) + })). + Complete()) +} + +type Signature struct { + Type string + Data []byte +} + +func SignatureFromBytes(x []byte) (Signature, error) { + val, nr := binary.Uvarint(x) + if nr != 1 { + return Signature{}, fmt.Errorf("signatures with type field longer than one byte are invalid") + } + var ts string + switch val { + case 1: + ts = KTSecp256k1 + default: + return Signature{}, fmt.Errorf("unsupported signature type: %d", val) + } + + return Signature{ + Type: ts, + Data: x[1:], + }, nil +} + +func (s *Signature) Verify(addr address.Address, msg []byte) error { + b2sum := blake2b.Sum256(msg) + + switch s.Type { + case KTSecp256k1: + pubk, err := crypto.EcRecover(b2sum[:], s.Data) + if err != nil { + return err + } + + maybeaddr, err := address.NewSecp256k1Address(pubk) + if err != nil { + return err + } + + if addr != maybeaddr { + return fmt.Errorf("signature did not match") + } + + return nil + default: + return fmt.Errorf("cannot verify signature of unsupported type: %s", s.Type) + } +} + +func (s *Signature) TypeCode() int { + switch s.Type { + case KTSecp256k1: + return 1 + case KTBLS: + return 2 + default: + panic("unsupported signature type") + } +} diff --git a/chain/types/vmcontext.go b/chain/types/vmcontext.go index b183bb7e3..7a850c29e 100644 --- a/chain/types/vmcontext.go +++ b/chain/types/vmcontext.go @@ -32,4 +32,5 @@ type VMContext interface { GasUsed() BigInt Storage() Storage StateTree() (StateTree, aerrors.ActorError) + VerifySignature(sig Signature, from address.Address) aerrors.ActorError } diff --git a/chain/vm.go b/chain/vm.go index 44f354186..6001dd7e1 100644 --- a/chain/vm.go +++ b/chain/vm.go @@ -129,6 +129,10 @@ func (vmc *VMContext) StateTree() (types.StateTree, aerrors.ActorError) { return vmc.state, nil } +func (vmctx *VMContext) VerifySignature(sig types.Signature, act address.Address) aerrors.ActorError { + panic("NYI") +} + func (vm *VM) makeVMContext(sroot cid.Cid, origin address.Address, msg *types.Message) *VMContext { cst := hamt.CSTFromBstore(vm.cs.bs) diff --git a/chain/wallet.go b/chain/wallet.go index 9a1dd5202..accb4e1e6 100644 --- a/chain/wallet.go +++ b/chain/wallet.go @@ -1,8 +1,6 @@ package chain import ( - "encoding/binary" - "fmt" "sort" "strings" @@ -17,9 +15,6 @@ import ( const ( KNamePrefix = "wallet-" - - KTSecp256k1 = "secp256k1" - KTBLS = "bls" ) type Wallet struct { @@ -36,91 +31,31 @@ func NewWallet(keystore types.KeyStore) (*Wallet, error) { return w, nil } -type Signature struct { - Type string - Data []byte -} - -func SignatureFromBytes(x []byte) (Signature, error) { - val, nr := binary.Uvarint(x) - if nr != 1 { - return Signature{}, fmt.Errorf("signatures with type field longer than one byte are invalid") - } - var ts string - switch val { - case 1: - ts = KTSecp256k1 - default: - return Signature{}, fmt.Errorf("unsupported signature type: %d", val) - } - - return Signature{ - Type: ts, - Data: x[1:], - }, nil -} - -func (s *Signature) Verify(addr address.Address, msg []byte) error { - b2sum := blake2b.Sum256(msg) - - switch s.Type { - case KTSecp256k1: - pubk, err := crypto.EcRecover(b2sum[:], s.Data) - if err != nil { - return err - } - - maybeaddr, err := address.NewSecp256k1Address(pubk) - if err != nil { - return err - } - - if addr != maybeaddr { - return fmt.Errorf("signature did not match") - } - - return nil - default: - return fmt.Errorf("cannot verify signature of unsupported type: %s", s.Type) - } -} - -func (s *Signature) TypeCode() int { - switch s.Type { - case KTSecp256k1: - return 1 - case KTBLS: - return 2 - default: - panic("unsupported signature type") - } -} - -func (w *Wallet) Sign(addr address.Address, msg []byte) (*Signature, error) { +func (w *Wallet) Sign(addr address.Address, msg []byte) (*types.Signature, error) { ki, err := w.findKey(addr) if err != nil { return nil, err } switch ki.Type { - case KTSecp256k1: + case types.KTSecp256k1: b2sum := blake2b.Sum256(msg) sig, err := crypto.Sign(ki.PrivateKey, b2sum[:]) if err != nil { return nil, err } - return &Signature{ - Type: KTSecp256k1, + return &types.Signature{ + Type: types.KTSecp256k1, Data: sig, }, nil - case KTBLS: + case types.KTBLS: var pk bls.PrivateKey copy(pk[:], ki.PrivateKey) sig := bls.PrivateKeySign(pk, msg) - return &Signature{ - Type: KTBLS, + return &types.Signature{ + Type: types.KTBLS, Data: sig[:], }, nil @@ -180,7 +115,7 @@ func (w *Wallet) ListAddrs() ([]address.Address, error) { func (w *Wallet) GenerateKey(typ string) (address.Address, error) { var k *Key switch typ { - case KTSecp256k1: + case types.KTSecp256k1: priv, err := crypto.GenerateKey() if err != nil { return address.Undef, err @@ -194,7 +129,7 @@ func (w *Wallet) GenerateKey(typ string) (address.Address, error) { if err != nil { return address.Undef, err } - case KTBLS: + case types.KTBLS: priv := bls.PrivateKeyGenerate() ki := types.KeyInfo{ Type: typ, @@ -231,7 +166,7 @@ func NewKey(keyinfo types.KeyInfo) (*Key, error) { } switch k.Type { - case KTSecp256k1: + case types.KTSecp256k1: k.PublicKey = crypto.PublicKey(k.PrivateKey) var err error @@ -240,7 +175,7 @@ func NewKey(keyinfo types.KeyInfo) (*Key, error) { return nil, xerrors.Errorf("converting Secp256k1 to address: %w", err) } - case KTBLS: + case types.KTBLS: var pk bls.PrivateKey copy(pk[:], k.PrivateKey) pub := bls.PrivateKeyPublicKey(pk) diff --git a/go.mod b/go.mod index 7b26cfbcf..1f114ebf1 100644 --- a/go.mod +++ b/go.mod @@ -69,6 +69,7 @@ require ( go4.org v0.0.0-20190313082347-94abd6928b1d // indirect golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 + google.golang.org/appengine v1.4.0 // indirect gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8 launchpad.net/gocheck v0.0.0-20140225173054-000000000087 // indirect ) diff --git a/node/impl/full.go b/node/impl/full.go index 94f6249e7..aa74b5331 100644 --- a/node/impl/full.go +++ b/node/impl/full.go @@ -123,7 +123,7 @@ func (a *FullNodeAPI) WalletBalance(ctx context.Context, addr address.Address) ( return a.Chain.GetBalance(addr) } -func (a *FullNodeAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*chain.Signature, error) { +func (a *FullNodeAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*types.Signature, error) { return a.Wallet.Sign(k, msg) }