import (
"context"
- "encoding/json"
+ stdjson "encoding/json"
"github.com/bytom/bytom/blockchain/signers"
"github.com/bytom/bytom/blockchain/txbuilder"
"github.com/bytom/bytom/common"
"github.com/bytom/bytom/consensus"
"github.com/bytom/bytom/crypto/ed25519/chainkd"
+ "github.com/bytom/bytom/encoding/json"
"github.com/bytom/bytom/errors"
"github.com/bytom/bytom/protocol/bc"
"github.com/bytom/bytom/protocol/bc/types"
//DecodeSpendAction unmarshal JSON-encoded data of spend action
func (m *Manager) DecodeSpendAction(data []byte) (txbuilder.Action, error) {
a := &spendAction{accounts: m}
- return a, json.Unmarshal(data, a)
+ return a, stdjson.Unmarshal(data, a)
}
type spendAction struct {
utxos := []*UTXO{}
for gasAmount := uint64(0); reservedAmount < gasAmount+amount; gasAmount = calcMergeGas(len(utxos)) {
reserveAmount := amount + gasAmount - reservedAmount
- res, err := m.utxoKeeper.Reserve(accountID, consensus.BTMAssetID, reserveAmount, useUnconfirmed, builder.MaxTime())
+ res, err := m.utxoKeeper.Reserve(accountID, consensus.BTMAssetID, reserveAmount, useUnconfirmed, nil, builder.MaxTime())
if err != nil {
return nil, err
}
return errors.Wrap(err, "get account info")
}
- res, err := a.accounts.utxoKeeper.Reserve(a.AccountID, a.AssetId, a.Amount, a.UseUnconfirmed, b.MaxTime())
+ res, err := a.accounts.utxoKeeper.Reserve(a.AccountID, a.AssetId, a.Amount, a.UseUnconfirmed, nil, b.MaxTime())
if err != nil {
return errors.Wrap(err, "reserving utxos")
}
//DecodeSpendUTXOAction unmarshal JSON-encoded data of spend utxo action
func (m *Manager) DecodeSpendUTXOAction(data []byte) (txbuilder.Action, error) {
a := &spendUTXOAction{accounts: m}
- return a, json.Unmarshal(data, a)
+ return a, stdjson.Unmarshal(data, a)
}
type spendUTXOAction struct {
// UtxoToInputs convert an utxo to the txinput
func UtxoToInputs(signer *signers.Signer, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram, u.StateData)
+ if u.Vote != nil {
+ txInput = types.NewVetoInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram, u.Vote, nil)
+ }
sigInst := &txbuilder.SigningInstruction{}
if signer == nil {
return txInput, sigInst, nil
return txInput, sigInst, nil
}
+//DecodeVetoAction unmarshal JSON-encoded data of spend action
+func (m *Manager) DecodeVetoAction(data []byte) (txbuilder.Action, error) {
+ a := &vetoAction{accounts: m}
+ return a, stdjson.Unmarshal(data, a)
+}
+
+type vetoAction struct {
+ accounts *Manager
+ bc.AssetAmount
+ AccountID string `json:"account_id"`
+ Vote json.HexBytes `json:"vote"`
+ UseUnconfirmed bool `json:"use_unconfirmed"`
+}
+
+func (a *vetoAction) ActionType() string {
+ return "veto"
+}
+
+func (a *vetoAction) Build(ctx context.Context, b *txbuilder.TemplateBuilder) error {
+ var missing []string
+ if a.AccountID == "" {
+ missing = append(missing, "account_id")
+ }
+ if a.AssetId.IsZero() {
+ missing = append(missing, "asset_id")
+ }
+ if len(missing) > 0 {
+ return txbuilder.MissingFieldsError(missing...)
+ }
+
+ acct, err := a.accounts.FindByID(a.AccountID)
+ if err != nil {
+ return errors.Wrap(err, "get account info")
+ }
+
+ res, err := a.accounts.utxoKeeper.Reserve(a.AccountID, a.AssetId, a.Amount, a.UseUnconfirmed, a.Vote, b.MaxTime())
+ if err != nil {
+ return errors.Wrap(err, "reserving utxos")
+ }
+
+ // Cancel the reservation if the build gets rolled back.
+ b.OnRollback(func() { a.accounts.utxoKeeper.Cancel(res.id) })
+ for _, r := range res.utxos {
+ txInput, sigInst, err := UtxoToInputs(acct.Signer, r)
+ if err != nil {
+ return errors.Wrap(err, "creating inputs")
+ }
+
+ if err = b.AddInput(txInput, sigInst); err != nil {
+ return errors.Wrap(err, "adding inputs")
+ }
+ }
+
+ if res.change > 0 {
+ acp, err := a.accounts.CreateAddress(a.AccountID, true)
+ if err != nil {
+ return errors.Wrap(err, "creating control program")
+ }
+
+ // Don't insert the control program until callbacks are executed.
+ a.accounts.insertControlProgramDelayed(b, acp)
+ if err = b.AddOutput(types.NewVoteOutput(*a.AssetId, res.change, acp.ControlProgram, a.Vote, nil)); err != nil {
+ return errors.Wrap(err, "adding change voteOutput")
+ }
+ }
+ return nil
+}
+
// insertControlProgramDelayed takes a template builder and an account
// control program that hasn't been inserted to the database yet. It
// registers callbacks on the TemplateBuilder so that all of the template's
package account
import (
+ "bytes"
"container/list"
"encoding/json"
"sort"
Amount uint64
SourcePos uint64
ControlProgram []byte
+ Vote []byte
StateData [][]byte
AccountID string
Address string
}
}
-func (uk *utxoKeeper) Reserve(accountID string, assetID *bc.AssetID, amount uint64, useUnconfirmed bool, exp time.Time) (*reservation, error) {
+func (uk *utxoKeeper) Reserve(accountID string, assetID *bc.AssetID, amount uint64, useUnconfirmed bool, vote []byte, exp time.Time) (*reservation, error) {
uk.mtx.Lock()
defer uk.mtx.Unlock()
- utxos, immatureAmount := uk.findUtxos(accountID, assetID, useUnconfirmed)
+ utxos, immatureAmount := uk.findUtxos(accountID, assetID, useUnconfirmed, vote)
optUtxos, optAmount, reservedAmount := uk.optUTXOs(utxos, amount)
if optAmount+reservedAmount+immatureAmount < amount {
return nil, ErrInsufficient
}
}
-func (uk *utxoKeeper) findUtxos(accountID string, assetID *bc.AssetID, useUnconfirmed bool) ([]*UTXO, uint64) {
+func (uk *utxoKeeper) findUtxos(accountID string, assetID *bc.AssetID, useUnconfirmed bool, vote []byte) ([]*UTXO, uint64) {
immatureAmount := uint64(0)
currentHeight := uk.currentHeight()
utxos := []*UTXO{}
appendUtxo := func(u *UTXO) {
- if u.AccountID != accountID || u.AssetID != *assetID {
+ if u.AccountID != accountID || u.AssetID != *assetID || !bytes.Equal(u.Vote, vote) {
return
}
if u.ValidHeight > currentHeight {
}
for i, c := range cases {
- if _, err := c.before.Reserve("testAccount", &bc.AssetID{}, c.reserveAmount, true, c.exp); err != c.err {
+ if _, err := c.before.Reserve("testAccount", &bc.AssetID{}, c.reserveAmount, true, nil, c.exp); err != c.err {
t.Errorf("case %d: got error %v want error %v", i, err, c.err)
}
checkUtxoKeeperEqual(t, i, &c.before, &c.after)
testDB.Set(StandardUTXOKey(u.OutputID), data)
}
- gotUtxos, immatureAmount := c.uk.findUtxos("testAccount", &bc.AssetID{}, c.useUnconfirmed)
+ gotUtxos, immatureAmount := c.uk.findUtxos("testAccount", &bc.AssetID{}, c.useUnconfirmed, nil)
if !testutil.DeepEqual(gotUtxos, c.wantUtxos) {
t.Errorf("case %d: got %v want %v", i, gotUtxos, c.wantUtxos)
}
"control_program": txbuilder.DecodeControlProgramAction,
"issue": a.wallet.AssetReg.DecodeIssueAction,
"retire": txbuilder.DecodeRetireAction,
+ "vote_output": txbuilder.DecodeVoteOutputAction,
"register_contract": txbuilder.DecodeRegisterAction,
"spend_account": a.wallet.AccountMgr.DecodeSpendAction,
"spend_account_unspent_output": a.wallet.AccountMgr.DecodeSpendUTXOAction,
+ "veto": a.wallet.AccountMgr.DecodeVetoAction,
}
decoder, ok := decoders[action]
return decoder, ok
func (a *registerAction) ActionType() string {
return "register_contract"
}
+
+// DecodeVoteOutputAction convert input data to action struct
+func DecodeVoteOutputAction(data []byte) (Action, error) {
+ a := new(voteOutputAction)
+ err := stdjson.Unmarshal(data, a)
+ return a, err
+}
+
+type voteOutputAction struct {
+ bc.AssetAmount
+ Address string `json:"address"`
+ Vote json.HexBytes `json:"vote"`
+}
+
+func (a *voteOutputAction) Build(ctx context.Context, b *TemplateBuilder) error {
+ var missing []string
+ if a.Address == "" {
+ missing = append(missing, "address")
+ }
+ if a.AssetId.IsZero() {
+ missing = append(missing, "asset_id")
+ }
+ if a.Amount == 0 {
+ missing = append(missing, "amount")
+ }
+ if len(a.Vote) == 0 {
+ missing = append(missing, "vote")
+ }
+ if len(missing) > 0 {
+ return MissingFieldsError(missing...)
+ }
+
+ address, err := common.DecodeAddress(a.Address, &consensus.ActiveNetParams)
+ if err != nil {
+ return err
+ }
+
+ redeemContract := address.ScriptAddress()
+ program := []byte{}
+ switch address.(type) {
+ case *common.AddressWitnessPubKeyHash:
+ program, err = vmutil.P2WPKHProgram(redeemContract)
+ case *common.AddressWitnessScriptHash:
+ program, err = vmutil.P2WSHProgram(redeemContract)
+ default:
+ return errors.New("unsupport address type")
+ }
+ if err != nil {
+ return err
+ }
+
+ out := types.NewVoteOutput(*a.AssetId, a.Amount, program, a.Vote, nil)
+ return b.AddOutput(out)
+}
+
+func (a *voteOutputAction) ActionType() string {
+ return "vote_output"
+}
+
// config parameter for coinbase reward
CoinbasePendingBlockNumber = uint64(100)
+ VotePendingBlockNumber = uint64(181440)
+ MinVoteOutputAmount = uint64(100000000)
subsidyReductionInterval = uint64(840000)
baseSubsidy = uint64(41250000000)
InitialBlockSubsidy = uint64(140700041250000000)
return node, nil
}
-func applyTransactions(target *state.Checkpoint, transactions []*types.Tx) error {
- for _, tx := range transactions {
+func applyTransactions(target *state.Checkpoint, transactions []*types.Tx) error {for _, tx := range transactions {
for _, input := range tx.Inputs {
if vetoInput, ok := input.TypedInput.(*types.VetoInput); ok {
if err := processVeto(vetoInput, target); err != nil {
guarantyNum := checkpoint.Guaranties[pubKey]
guarantyNum, ok := checked.SubUint64(guarantyNum, guarantyArgs.Amount)
if !ok {
- return errOverflow
+ return checked.ErrOverflow
}
checkpoint.Guaranties[pubKey] = guarantyNum
guarantyNum := checkpoint.Guaranties[pubKey]
guarantyNum, ok := checked.AddUint64(guarantyNum, guarantyArgs.Amount)
if !ok {
- return errOverflow
+ return checked.ErrOverflow
}
checkpoint.Guaranties[pubKey] = guarantyNum
voteNum := checkpoint.Votes[pubKey]
voteNum, ok := checked.SubUint64(voteNum, input.Amount)
if !ok {
- return errOverflow
+ return checked.ErrOverflow
}
checkpoint.Votes[pubKey] = voteNum
voteNum := checkpoint.Votes[pubKey]
voteNum, ok := checked.AddUint64(voteNum, output.Amount)
if !ok {
- return errOverflow
+ return checked.ErrOverflow
}
checkpoint.Votes[pubKey] = voteNum
return nil
}
+ oldBestHash := c.bestChain()
if err := c.authVerification(v, targetNode.checkpoint, validators); err != nil {
return err
}
- return c.tryRollback(c.bestChain())
+ return c.tryRollback(oldBestHash)
}
func (c *Casper) authVerification(v *Verification, target *state.Checkpoint, validators map[string]*state.Validator) error {
}
return iss, nil
}
+
+// VetoInput try to get the veto entry by given hash
+func (tx *Tx) VetoInput(id Hash) (*VetoInput, error) {
+ e, ok := tx.Entries[id]
+ if !ok || e == nil {
+ return nil, errors.Wrapf(ErrMissingEntry, "id %x", id.Bytes())
+ }
+ sp, ok := e.(*VetoInput)
+ if !ok {
+ return nil, errors.Wrapf(ErrEntryType, "entry %x has unexpected type %T", id.Bytes(), e)
+ }
+ return sp, nil
+}
+
+// VoteOutput try to get the vote output entry by given hash
+func (tx *Tx) VoteOutput(id Hash) (*VoteOutput, error) {
+ e, ok := tx.Entries[id]
+ if !ok || e == nil {
+ return nil, errors.Wrapf(ErrMissingEntry, "id %x", id.Bytes())
+ }
+ o, ok := e.(*VoteOutput)
+ if !ok {
+ return nil, errors.Wrapf(ErrEntryType, "entry %x has unexpected type %T", id.Bytes(), e)
+ }
+ return o, nil
+}
errSpanHeightInVerification = errors.New("validator publish vote within the span of its other votes")
errVoteToNonValidator = errors.New("pubKey of vote is not validator")
errGuarantyLessThanMinimum = errors.New("guaranty less than minimum")
- errOverflow = errors.New("arithmetic overflow/underflow")
)
-const minGuaranty = 1E14
+const minGuaranty = 0
// Casper is BFT based proof of stack consensus algorithm, it provides safety and liveness in theory,
// it's design mainly refers to https://github.com/ethereum/research/blob/master/papers/casper-basics/casper_basics.pdf
return nil
}
- for pubKey, mortgageNum := range c.Guaranties {
+ // todo use vote num to generate validators in test version
+ for pubKey, mortgageNum := range c.Votes {
if mortgageNum >= minMortgage {
validators = append(validators, &Validator{
PubKey: pubKey,
ErrUnbalanced = errors.New("unbalanced asset amount between input and output")
ErrOverGasCredit = errors.New("all gas credit has been spend")
ErrGasCalculate = errors.New("gas usage calculate got a math error")
+ ErrVotePubKey = errors.New("invalid public key of vote")
+ ErrVoteOutputAmount = errors.New("invalid vote amount")
+ ErrVoteOutputAseet = errors.New("incorrect asset_id while checking vote asset")
)
// GasState record the gas usage status
if err = checkValidSrc(&vs2, e.Source); err != nil {
return errors.Wrap(err, "checking retirement source")
}
+ case *bc.VoteOutput:
+ if len(e.Vote) != 64 {
+ return ErrVotePubKey
+ }
+
+ vs2 := *vs
+ vs2.sourcePos = 0
+ if err = checkValidSrc(&vs2, e.Source); err != nil {
+ return errors.Wrap(err, "checking vote output source")
+ }
+ if e.Source.Value.Amount < consensus.MinVoteOutputAmount {
+ return ErrVoteOutputAmount
+ }
+
+ if *e.Source.Value.AssetId != *consensus.BTMAssetID {
+ return ErrVoteOutputAseet
+ }
case *bc.Issuance:
computedAssetID := e.WitnessAssetDefinition.ComputeAssetID()
if computedAssetID != *e.Value.AssetId {
if err = checkValidDest(&vs2, e.WitnessDestination); err != nil {
return errors.Wrap(err, "checking spend destination")
}
+ case *bc.VetoInput:
+ if e.SpentOutputId == nil {
+ return errors.Wrap(ErrMissingField, "vetoInput without vetoInput output ID")
+ }
+
+ voteOutput, err := vs.tx.VoteOutput(*e.SpentOutputId)
+ if err != nil {
+ return errors.Wrap(err, "getting vetoInput prevout")
+ }
+
+ if len(voteOutput.Vote) != 64 {
+ return ErrVotePubKey
+ }
+
+ gasLeft, err := vm.Verify(NewTxVMContext(vs, e, voteOutput.ControlProgram, voteOutput.StateData, 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 := voteOutput.Source.Value.Equal(e.WitnessDestination.Value)
+ if err != nil {
+ return err
+ }
+ if !eq {
+ return errors.WithDetailf(
+ ErrMismatchedValue,
+ "previous output is for %d unit(s) of %x, vetoInput wants %d unit(s) of %x",
+ voteOutput.Source.Value.Amount,
+ voteOutput.Source.Value.AssetId.Bytes(),
+ e.WitnessDestination.Value.Amount,
+ e.WitnessDestination.Value.AssetId.Bytes(),
+ )
+ }
+ vs2 := *vs
+ vs2.destPos = 0
+ if err = checkValidDest(&vs2, e.WitnessDestination); err != nil {
+ return errors.Wrap(err, "checking vetoInput destination")
+ }
case *bc.Coinbase:
if vs.block == nil || len(vs.block.Transactions) == 0 || vs.block.Transactions[0] != vs.tx {
}
dest = ref.WitnessDestination
+ case *bc.VetoInput:
+ if vs.Position != 0 {
+ return errors.Wrapf(ErrPosition, "invalid position %d for veto-input source", vs.Position)
+ }
+ dest = ref.WitnessDestination
+
case *bc.Mux:
if vs.Position >= uint64(len(ref.WitnessDestinations)) {
return errors.Wrapf(ErrPosition, "invalid position %d for %d-destination mux source", vs.Position, len(ref.WitnessDestinations))
}
src = ref.Source
+ case *bc.VoteOutput:
+ if vd.Position != 0 {
+ return errors.Wrapf(ErrPosition, "invalid position %d for output destination", vd.Position)
+ }
+ src = ref.Source
+
case *bc.Mux:
if vd.Position >= uint64(len(ref.Sources)) {
return errors.Wrapf(ErrPosition, "invalid position %d for %d-source mux destination", vd.Position, len(ref.Sources))
import (
"encoding/json"
+
log "github.com/sirupsen/logrus"
"github.com/bytom/bytom/account"
"github.com/bytom/bytom/crypto/sha3pool"
dbm "github.com/bytom/bytom/database/leveldb"
"github.com/bytom/bytom/errors"
+ "github.com/bytom/bytom/protocol/bc"
"github.com/bytom/bytom/protocol/bc/types"
)
}
func (w *Wallet) attachUtxos(batch dbm.Batch, b *types.Block) {
- for txIndex, tx := range b.Transactions {
- //hand update the transaction input utxos
+ for _, tx := range b.Transactions {
+ // hand update the transaction input utxos
inputUtxos := txInToUtxos(tx)
for _, inputUtxo := range inputUtxos {
if segwit.IsP2WScript(inputUtxo.ControlProgram) {
}
}
- //hand update the transaction output utxos
- validHeight := uint64(0)
- if txIndex == 0 {
- validHeight = b.Height + consensus.CoinbasePendingBlockNumber
- }
- outputUtxos := txOutToUtxos(tx, validHeight)
+ // hand update the transaction output utxos
+ outputUtxos := txOutToUtxos(tx, b.Height)
utxos := w.filterAccountUtxo(outputUtxos)
if err := batchSaveUtxos(utxos, batch); err != nil {
log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on batchSaveUtxos")
func txInToUtxos(tx *types.Tx) []*account.UTXO {
utxos := []*account.UTXO{}
for _, inpID := range tx.Tx.InputIDs {
- sp, err := tx.Spend(inpID)
- if err != nil {
+ var utxo *account.UTXO
+ e, ok := tx.Entries[inpID]
+ if !ok {
continue
}
- resOut, err := tx.Output(*sp.SpentOutputId)
- if err != nil {
- log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut")
+ switch inp := e.(type) {
+ case *bc.Spend:
+ resOut, err := tx.Output(*inp.SpentOutputId)
+ if err != nil {
+ log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut")
+ continue
+ }
+
+ utxo = &account.UTXO{
+ OutputID: *inp.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.VetoInput:
+ resOut, err := tx.VoteOutput(*inp.SpentOutputId)
+ if err != nil {
+ log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for vetoInput")
+ continue
+ }
+ if *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
+ continue
+ }
+ utxo = &account.UTXO{
+ OutputID: *inp.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:
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
}
-func txOutToUtxos(tx *types.Tx, vaildHeight uint64) []*account.UTXO {
+func txOutToUtxos(tx *types.Tx, blockHeight uint64) []*account.UTXO {
utxos := []*account.UTXO{}
for i, out := range tx.Outputs {
- bcOut, err := tx.Output(*tx.ResultIds[i])
- if err != nil {
+ validHeight := uint64(0)
+ entryOutput, ok := tx.Entries[*tx.ResultIds[i]]
+ if !ok {
+ log.WithFields(log.Fields{"module": logModule}).Error("txOutToUtxos fail on get entryOutput")
continue
}
- utxo := &account.UTXO{
- OutputID: *tx.OutputID(i),
- AssetID: *out.AssetAmount.AssetId,
- Amount: out.Amount,
- ControlProgram: out.ControlProgram,
- SourceID: *bcOut.Source.Ref,
- SourcePos: bcOut.Source.Position,
- ValidHeight: vaildHeight,
+ var utxo *account.UTXO
+ switch bcOut := entryOutput.(type) {
+ case *bc.Output:
+ if out.AssetAmount.Amount == uint64(0) {
+ continue
+ }
+
+ if tx.Inputs[0].InputType() == types.CoinbaseInputType {
+ validHeight = blockHeight + consensus.CoinbasePendingBlockNumber
+ }
+
+ 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: validHeight,
+ }
+
+ case *bc.VoteOutput:
+ voteValidHeight := blockHeight + consensus.VotePendingBlockNumber
+ if validHeight < voteValidHeight {
+ validHeight = voteValidHeight
+ }
+
+ 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: validHeight,
+ Vote: bcOut.Vote,
+ }
+
+ default:
+ log.WithFields(log.Fields{"module": logModule}).Warn("txOutToUtxos fail on get bcOut")
+ continue
}
utxos = append(utxos, utxo)
}
cases := []struct {
tx *types.Tx
statusFail bool
- vaildHeight uint64
+ blockHeight uint64
wantUtxos []*account.UTXO
}{
{
},
}),
statusFail: false,
- vaildHeight: 98,
+ blockHeight: 0,
wantUtxos: []*account.UTXO{
&account.UTXO{
OutputID: bc.NewHash([32]byte{0x9c, 0xab, 0x55, 0xdc, 0x72, 0xb1, 0x42, 0x6d, 0x2a, 0x41, 0x92, 0xc3, 0x40, 0x32, 0x29, 0xf4, 0xa4, 0x11, 0xae, 0x54, 0x41, 0x54, 0x1a, 0xfe, 0x7c, 0x93, 0x4b, 0x8f, 0x6c, 0x61, 0x69, 0x9f}),
ControlProgram: []byte{0x51},
SourceID: bc.NewHash([32]byte{0xb4, 0x7e, 0x94, 0x31, 0x88, 0xfe, 0xd3, 0xe9, 0xac, 0x99, 0x7c, 0xfc, 0x99, 0x6d, 0xd7, 0x4d, 0x04, 0x10, 0x77, 0xcb, 0x1c, 0xf8, 0x95, 0x14, 0x00, 0xe3, 0x42, 0x00, 0x8d, 0x05, 0xec, 0xdc}),
SourcePos: 0,
- ValidHeight: 98,
+ ValidHeight: 100,
},
},
},
},
}),
statusFail: false,
- vaildHeight: 0,
+ blockHeight: 0,
wantUtxos: []*account.UTXO{
&account.UTXO{
OutputID: bc.NewHash([32]byte{0x49, 0x33, 0x66, 0x49, 0x4b, 0xaa, 0x57, 0x26, 0xc7, 0x21, 0x74, 0x75, 0x4e, 0x15, 0x59, 0xa4, 0x24, 0xa1, 0x92, 0xda, 0xb1, 0x88, 0x8f, 0xea, 0x51, 0xaf, 0xcf, 0x95, 0x21, 0xab, 0xe4, 0xe2}),
},
}),
statusFail: true,
- vaildHeight: 0,
+ blockHeight: 0,
wantUtxos: []*account.UTXO{
&account.UTXO{
OutputID: bc.NewHash([32]byte{0x49, 0x33, 0x66, 0x49, 0x4b, 0xaa, 0x57, 0x26, 0xc7, 0x21, 0x74, 0x75, 0x4e, 0x15, 0x59, 0xa4, 0x24, 0xa1, 0x92, 0xda, 0xb1, 0x88, 0x8f, 0xea, 0x51, 0xaf, 0xcf, 0x95, 0x21, 0xab, 0xe4, 0xe2}),
}
for i, c := range cases {
- if gotUtxos := txOutToUtxos(c.tx, c.vaildHeight); !testutil.DeepEqual(gotUtxos, c.wantUtxos) {
+ if gotUtxos := txOutToUtxos(c.tx, c.blockHeight); !testutil.DeepEqual(gotUtxos, c.wantUtxos) {
for k, v := range gotUtxos {
data, _ := json.Marshal(v)