From: wz Date: Mon, 20 May 2019 06:37:55 +0000 (+0800) Subject: V0.1 votetx utxo (#73) X-Git-Tag: v1.0.5~208^2~112 X-Git-Url: http://git.osdn.net/view?p=bytom%2Fvapor.git;a=commitdiff_plain;h=7673e01ba118c9c021aff983f0fbe56ec125d9e3;ds=sidebyside V0.1 votetx utxo (#73) * Modify utxo for votetx * fix review * fix test --- diff --git a/account/utxo_keeper.go b/account/utxo_keeper.go index 2e169cf7..27055269 100644 --- a/account/utxo_keeper.go +++ b/account/utxo_keeper.go @@ -34,6 +34,7 @@ type UTXO struct { Amount uint64 SourcePos uint64 ControlProgram []byte + Vote []byte AccountID string Address string ControlProgramIndex uint64 diff --git a/protocol/bc/types/map.go b/protocol/bc/types/map.go index 8ec61676..4a795b5b 100644 --- a/protocol/bc/types/map.go +++ b/protocol/bc/types/map.go @@ -152,8 +152,13 @@ func mapTx(tx *TxData) (headerID bc.Hash, hdr *bc.TxHeader, entryMap map[bc.Hash // connect the inputs to the mux for _, spend := range spends { - spentOutput := entryMap[*spend.SpentOutputId].(*bc.IntraChainOutput) - spend.SetDestination(&muxID, spentOutput.Source.Value, spend.Ordinal) + switch spentOutput := entryMap[*spend.SpentOutputId].(type) { + case *bc.IntraChainOutput: + spend.SetDestination(&muxID, spentOutput.Source.Value, spend.Ordinal) + + case *bc.VoteOutput: + spend.SetDestination(&muxID, spentOutput.Source.Value, spend.Ordinal) + } } for _, crossIn := range crossIns { diff --git a/protocol/state/utxo_view.go b/protocol/state/utxo_view.go index 885f0acf..d1051783 100644 --- a/protocol/state/utxo_view.go +++ b/protocol/state/utxo_view.go @@ -1,10 +1,9 @@ package state import ( - "errors" - "github.com/vapor/consensus" "github.com/vapor/database/storage" + "github.com/vapor/errors" "github.com/vapor/protocol/bc" ) @@ -22,11 +21,22 @@ func NewUtxoViewpoint() *UtxoViewpoint { func (view *UtxoViewpoint) ApplyTransaction(block *bc.Block, tx *bc.Tx, statusFail bool) error { for _, prevout := range tx.SpentOutputIDs { - spentOutput, err := tx.IntraChainOutput(prevout) + assetID := bc.AssetID{} + entryOutput, err := tx.Entry(prevout) if err != nil { return err } - if statusFail && *spentOutput.Source.Value.AssetId != *consensus.BTMAssetID { + + switch output := entryOutput.(type) { + case *bc.IntraChainOutput: + assetID = *output.Source.Value.AssetId + case *bc.VoteOutput: + assetID = *output.Source.Value.AssetId + default: + return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput) + } + + if statusFail && assetID != *consensus.BTMAssetID { continue } @@ -44,12 +54,23 @@ func (view *UtxoViewpoint) ApplyTransaction(block *bc.Block, tx *bc.Tx, statusFa } for _, id := range tx.TxHeader.ResultIds { - output, err := tx.IntraChainOutput(*id) + assetID := bc.AssetID{} + entryOutput, err := tx.Entry(*id) if err != nil { - // error due to it's a retirement, utxo doesn't care this output type so skip it continue } - if statusFail && *output.Source.Value.AssetId != *consensus.BTMAssetID { + + switch output := entryOutput.(type) { + case *bc.IntraChainOutput: + assetID = *output.Source.Value.AssetId + case *bc.VoteOutput: + assetID = *output.Source.Value.AssetId + default: + // due to it's a retirement, utxo doesn't care this output type so skip it + continue + } + + if statusFail && assetID != *consensus.BTMAssetID { continue } @@ -82,11 +103,22 @@ func (view *UtxoViewpoint) CanSpend(hash *bc.Hash) bool { func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error { for _, prevout := range tx.SpentOutputIDs { - spentOutput, err := tx.IntraChainOutput(prevout) + assetID := bc.AssetID{} + entryOutput, err := tx.Entry(prevout) if err != nil { return err } - if statusFail && *spentOutput.Source.Value.AssetId != *consensus.BTMAssetID { + + switch output := entryOutput.(type) { + case *bc.IntraChainOutput: + assetID = *output.Source.Value.AssetId + case *bc.VoteOutput: + assetID = *output.Source.Value.AssetId + default: + return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput) + } + + if statusFail && assetID != *consensus.BTMAssetID { continue } @@ -102,12 +134,23 @@ func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error { } for _, id := range tx.TxHeader.ResultIds { - output, err := tx.IntraChainOutput(*id) + assetID := bc.AssetID{} + entryOutput, err := tx.Entry(*id) if err != nil { - // error due to it's a retirement, utxo doesn't care this output type so skip it continue } - if statusFail && *output.Source.Value.AssetId != *consensus.BTMAssetID { + + switch output := entryOutput.(type) { + case *bc.IntraChainOutput: + assetID = *output.Source.Value.AssetId + case *bc.VoteOutput: + assetID = *output.Source.Value.AssetId + default: + // due to it's a retirement, utxo doesn't care this output type so skip it + continue + } + + if statusFail && assetID != *consensus.BTMAssetID { continue } diff --git a/protocol/validation/tx.go b/protocol/validation/tx.go index 6e0acd24..9225eb2f 100644 --- a/protocol/validation/tx.go +++ b/protocol/validation/tx.go @@ -254,18 +254,35 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { if e.SpentOutputId == nil { return errors.Wrap(ErrMissingField, "spend without spent output ID") } - spentOutput, err := vs.tx.IntraChainOutput(*e.SpentOutputId) + var ( + controlProgram *bc.Program + value *bc.AssetAmount + ) + entryOutput, err := vs.tx.Entry(*e.SpentOutputId) if err != nil { return errors.Wrap(err, "getting spend prevout") } - gasLeft, err := vm.Verify(NewTxVMContext(vs, e, spentOutput.ControlProgram, e.WitnessArguments), vs.gasStatus.GasLeft) + + switch output := entryOutput.(type) { + case *bc.IntraChainOutput: + controlProgram = output.ControlProgram + value = output.Source.Value + case *bc.VoteOutput: + controlProgram = output.ControlProgram + value = output.Source.Value + default: + return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", e.SpentOutputId.Bytes(), entryOutput) + } + + gasLeft, err := vm.Verify(NewTxVMContext(vs, e, controlProgram, e.WitnessArguments), vs.gasStatus.GasLeft) if err != nil { return errors.Wrap(err, "checking control program") } if err = vs.gasStatus.updateUsage(gasLeft); err != nil { return err } - eq, err := spentOutput.Source.Value.Equal(e.WitnessDestination.Value) + + eq, err := value.Equal(e.WitnessDestination.Value) if err != nil { return err } @@ -273,8 +290,8 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { return errors.WithDetailf( ErrMismatchedValue, "previous output is for %d unit(s) of %x, spend wants %d unit(s) of %x", - spentOutput.Source.Value.Amount, - spentOutput.Source.Value.AssetId.Bytes(), + value.Amount, + value.AssetId.Bytes(), e.WitnessDestination.Value.Amount, e.WitnessDestination.Value.AssetId.Bytes(), ) diff --git a/wallet/utxo.go b/wallet/utxo.go index 7316623f..20775fdd 100644 --- a/wallet/utxo.go +++ b/wallet/utxo.go @@ -170,24 +170,47 @@ func txInToUtxos(tx *types.Tx, statusFail bool) []*account.UTXO { continue } - resOut, err := tx.IntraChainOutput(*sp.SpentOutputId) + entryOutput, err := tx.Entry(*sp.SpentOutputId) if err != nil { - log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut") + log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get entryOutput") continue } - if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID { + utxo := &account.UTXO{} + switch resOut := entryOutput.(type) { + case *bc.IntraChainOutput: + if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID { + continue + } + utxo = &account.UTXO{ + OutputID: *sp.SpentOutputId, + AssetID: *resOut.Source.Value.AssetId, + Amount: resOut.Source.Value.Amount, + ControlProgram: resOut.ControlProgram.Code, + SourceID: *resOut.Source.Ref, + SourcePos: resOut.Source.Position, + } + + case *bc.VoteOutput: + if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID { + continue + } + utxo = &account.UTXO{ + OutputID: *sp.SpentOutputId, + AssetID: *resOut.Source.Value.AssetId, + Amount: resOut.Source.Value.Amount, + ControlProgram: resOut.ControlProgram.Code, + SourceID: *resOut.Source.Ref, + SourcePos: resOut.Source.Position, + Vote: resOut.Vote, + } + + default: + log.WithFields(log.Fields{"module": logModule}).Error("txInToUtxos fail on get resOut") continue } - utxos = append(utxos, &account.UTXO{ - OutputID: *sp.SpentOutputId, - AssetID: *resOut.Source.Value.AssetId, - Amount: resOut.Source.Value.Amount, - ControlProgram: resOut.ControlProgram.Code, - SourceID: *resOut.Source.Ref, - SourcePos: resOut.Source.Position, - }) + utxos = append(utxos, utxo) } return utxos } @@ -195,24 +218,49 @@ func txInToUtxos(tx *types.Tx, statusFail bool) []*account.UTXO { func txOutToUtxos(tx *types.Tx, statusFail bool, vaildHeight uint64) []*account.UTXO { utxos := []*account.UTXO{} for i, out := range tx.Outputs { - bcOut, err := tx.IntraChainOutput(*tx.ResultIds[i]) + entryOutput, err := tx.Entry(*tx.ResultIds[i]) if err != nil { + log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txOutToUtxos fail on get entryOutput") continue } - if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID { + utxo := &account.UTXO{} + switch bcOut := entryOutput.(type) { + case *bc.IntraChainOutput: + if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID { + continue + } + utxo = &account.UTXO{ + OutputID: *tx.OutputID(i), + AssetID: *out.AssetAmount().AssetId, + Amount: out.AssetAmount().Amount, + ControlProgram: out.ControlProgram(), + SourceID: *bcOut.Source.Ref, + SourcePos: bcOut.Source.Position, + ValidHeight: vaildHeight, + } + + case *bc.VoteOutput: + if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID { + continue + } + utxo = &account.UTXO{ + OutputID: *tx.OutputID(i), + AssetID: *out.AssetAmount().AssetId, + Amount: out.AssetAmount().Amount, + ControlProgram: out.ControlProgram(), + SourceID: *bcOut.Source.Ref, + SourcePos: bcOut.Source.Position, + ValidHeight: vaildHeight, + Vote: bcOut.Vote, + } + + default: + log.WithFields(log.Fields{"module": logModule}).Error("txOutToUtxos fail on get bcOut") continue } - utxos = append(utxos, &account.UTXO{ - OutputID: *tx.OutputID(i), - AssetID: *out.AssetAmount().AssetId, - Amount: out.AssetAmount().Amount, - ControlProgram: out.ControlProgram(), - SourceID: *bcOut.Source.Ref, - SourcePos: bcOut.Source.Position, - ValidHeight: vaildHeight, - }) + utxos = append(utxos, utxo) } return utxos } diff --git a/wallet/utxo_test.go b/wallet/utxo_test.go index 825bf334..5f9928de 100644 --- a/wallet/utxo_test.go +++ b/wallet/utxo_test.go @@ -18,7 +18,10 @@ import ( func TestGetAccountUtxos(t *testing.T) { testDB := dbm.NewDB("testdb", "leveldb", "temp") - defer os.RemoveAll("temp") + defer func() { + testDB.Close() + os.RemoveAll("temp") + }() cases := []struct { dbUtxos map[string]*account.UTXO @@ -207,7 +210,10 @@ func TestGetAccountUtxos(t *testing.T) { func TestFilterAccountUtxo(t *testing.T) { testDB := dbm.NewDB("testdb", "leveldb", "temp") - defer os.RemoveAll("temp") + defer func() { + testDB.Close() + os.RemoveAll("temp") + }() cases := []struct { dbPrograms map[string]*account.CtrlProgram @@ -475,6 +481,28 @@ func TestTxInToUtxos(t *testing.T) { }, }, }, + { + tx: types.NewTx(types.TxData{ + Inputs: []*types.TxInput{ + types.NewUnvoteInput([][]byte{}, bc.Hash{V0: 1}, bc.AssetID{V0: 1}, 1, 1, []byte{0x51}, []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269")), + }, + Outputs: []*types.TxOutput{ + types.NewIntraChainOutput(bc.AssetID{V0: 1}, 1, []byte{0x51}), + }, + }), + statusFail: false, + wantUtxos: []*account.UTXO{ + &account.UTXO{ + OutputID: bc.NewHash([32]byte{0x95, 0x23, 0x06, 0xa5, 0x2f, 0xc4, 0xe2, 0x36, 0x03, 0x0f, 0xe3, 0xe6, 0xb8, 0x0b, 0xcc, 0x3c, 0x1e, 0x17, 0x3e, 0x25, 0x95, 0xd0, 0xbf, 0x08, 0x11, 0x73, 0x06, 0xd4, 0x64, 0x9c, 0xfb, 0x3b}), + AssetID: bc.AssetID{V0: 1}, + Amount: 1, + ControlProgram: []byte{0x51}, + Vote: []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"), + SourceID: bc.Hash{V0: 1}, + SourcePos: 1, + }, + }, + }, } for i, c := range cases {