Merge pull request #410 from filecoin-project/feat/multisig-lockout

Add linear vesting to multisig
This commit is contained in:
Łukasz Magiera 2019-10-25 17:34:41 +02:00 committed by GitHub
commit cf2bbeb775
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 98 additions and 6 deletions

View File

@ -16,10 +16,29 @@ type MultiSigActorState struct {
Required uint64
NextTxID uint64
InitialBalance types.BigInt
StartingBlock uint64
UnlockDuration uint64
//TODO: make this map/sharray/whatever
Transactions []MTransaction
}
func (msas MultiSigActorState) canSpend(act *types.Actor, amnt types.BigInt, height uint64) bool {
if msas.UnlockDuration == 0 {
return true
}
offset := height - msas.StartingBlock
if offset > msas.UnlockDuration {
return true
}
minBalance := types.BigDiv(msas.InitialBalance, types.NewInt(msas.UnlockDuration))
minBalance = types.BigMul(minBalance, types.NewInt(offset))
return !minBalance.LessThan(types.BigSub(act.Balance, amnt))
}
func (msas MultiSigActorState) isSigner(addr address.Address) bool {
for _, s := range msas.Signers {
if s == addr {
@ -92,6 +111,7 @@ func (msa MultiSigActor) Exports() []interface{} {
type MultiSigConstructorParams struct {
Signers []address.Address
Required uint64
UnlockDuration uint64
}
func (MultiSigActor) MultiSigConstructor(act *types.Actor, vmctx types.VMContext,
@ -100,6 +120,13 @@ func (MultiSigActor) MultiSigConstructor(act *types.Actor, vmctx types.VMContext
Signers: params.Signers,
Required: params.Required,
}
if params.UnlockDuration != 0 {
self.InitialBalance = vmctx.Message().Value
self.UnlockDuration = params.UnlockDuration
self.StartingBlock = vmctx.BlockHeight()
}
head, err := vmctx.Storage().Put(self)
if err != nil {
return nil, aerrors.Wrap(err, "could not put new head")
@ -183,6 +210,9 @@ func (msa MultiSigActor) Propose(act *types.Actor, vmctx types.VMContext,
}
if self.Required == 1 {
if !self.canSpend(act, tx.Value, vmctx.BlockHeight()) {
return nil, aerrors.New(100, "transaction amount exceeds available")
}
_, err := vmctx.Send(tx.To, tx.Method, tx.Value, tx.Params)
if aerrors.IsFatal(err) {
return nil, err
@ -229,6 +259,9 @@ func (msa MultiSigActor) Approve(act *types.Actor, vmctx types.VMContext,
}
tx.Approved = append(tx.Approved, vmctx.Message().From)
if uint64(len(tx.Approved)) >= self.Required {
if !self.canSpend(act, tx.Value, vmctx.BlockHeight()) {
return nil, aerrors.New(100, "transaction amount exceeds available")
}
_, err := vmctx.Send(tx.To, tx.Method, tx.Value, tx.Params)
if aerrors.IsFatal(err) {
return nil, err

View File

@ -963,7 +963,7 @@ func (t *MultiSigActorState) MarshalCBOR(w io.Writer) error {
_, err := w.Write(cbg.CborNull)
return err
}
if _, err := w.Write([]byte{132}); err != nil {
if _, err := w.Write([]byte{135}); err != nil {
return err
}
@ -987,6 +987,21 @@ func (t *MultiSigActorState) MarshalCBOR(w io.Writer) error {
return err
}
// t.t.InitialBalance (types.BigInt)
if err := t.InitialBalance.MarshalCBOR(w); err != nil {
return err
}
// t.t.StartingBlock (uint64)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.StartingBlock)); err != nil {
return err
}
// t.t.UnlockDuration (uint64)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.UnlockDuration)); err != nil {
return err
}
// t.t.Transactions ([]actors.MTransaction)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Transactions)))); err != nil {
return err
@ -1010,7 +1025,7 @@ func (t *MultiSigActorState) UnmarshalCBOR(r io.Reader) error {
return fmt.Errorf("cbor input should be of type array")
}
if extra != 4 {
if extra != 7 {
return fmt.Errorf("cbor input had wrong number of fields")
}
@ -1060,6 +1075,35 @@ func (t *MultiSigActorState) UnmarshalCBOR(r io.Reader) error {
return fmt.Errorf("wrong type for uint64 field")
}
t.NextTxID = extra
// t.t.InitialBalance (types.BigInt)
{
if err := t.InitialBalance.UnmarshalCBOR(br); err != nil {
return err
}
}
// t.t.StartingBlock (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.StartingBlock = extra
// t.t.UnlockDuration (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.UnlockDuration = extra
// t.t.Transactions ([]actors.MTransaction)
maj, extra, err = cbg.CborReadHeader(br)
@ -1094,7 +1138,7 @@ func (t *MultiSigConstructorParams) MarshalCBOR(w io.Writer) error {
_, err := w.Write(cbg.CborNull)
return err
}
if _, err := w.Write([]byte{130}); err != nil {
if _, err := w.Write([]byte{131}); err != nil {
return err
}
@ -1112,6 +1156,11 @@ func (t *MultiSigConstructorParams) MarshalCBOR(w io.Writer) error {
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.Required)); err != nil {
return err
}
// t.t.UnlockDuration (uint64)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.UnlockDuration)); err != nil {
return err
}
return nil
}
@ -1126,7 +1175,7 @@ func (t *MultiSigConstructorParams) UnmarshalCBOR(r io.Reader) error {
return fmt.Errorf("cbor input should be of type array")
}
if extra != 2 {
if extra != 3 {
return fmt.Errorf("cbor input had wrong number of fields")
}
@ -1166,6 +1215,16 @@ func (t *MultiSigConstructorParams) UnmarshalCBOR(r io.Reader) error {
return fmt.Errorf("wrong type for uint64 field")
}
t.Required = extra
// t.t.UnlockDuration (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.UnlockDuration = extra
return nil
}