From 3915abc6a36db3a9da84f472a72473b6fda1344b Mon Sep 17 00:00:00 2001 From: Paladz Date: Fri, 19 Apr 2019 10:06:09 +0800 Subject: [PATCH] add the index (#1716) * add the index * delete test data * return the data --- blockchain/query/annotated.go | 2 ++ blockchain/txbuilder/actions.go | 3 +- protocol/vm/vmutil/script.go | 27 ++++++++++++++++- wallet/annotated.go | 6 +++- wallet/indexer.go | 64 +++++++++++------------------------------ wallet/wallet_test.go | 30 ------------------- 6 files changed, 51 insertions(+), 81 deletions(-) diff --git a/blockchain/query/annotated.go b/blockchain/query/annotated.go index 30d197f0..cee1d495 100644 --- a/blockchain/query/annotated.go +++ b/blockchain/query/annotated.go @@ -54,6 +54,8 @@ type AnnotatedOutput struct { AccountAlias string `json:"account_alias,omitempty"` ControlProgram chainjson.HexBytes `json:"control_program"` Address string `json:"address,omitempty"` + Index string `json:"index"` + RetireData chainjson.HexBytes `json:"retire_data"` } //AnnotatedAccount means an annotated account. diff --git a/blockchain/txbuilder/actions.go b/blockchain/txbuilder/actions.go index ab3f0ff5..60a0dbd0 100644 --- a/blockchain/txbuilder/actions.go +++ b/blockchain/txbuilder/actions.go @@ -112,6 +112,7 @@ func DecodeRetireAction(data []byte) (Action, error) { type retireAction struct { bc.AssetAmount Arbitrary json.HexBytes `json:"arbitrary"` + Index string `json:"index"` } func (a *retireAction) Build(ctx context.Context, b *TemplateBuilder) error { @@ -126,7 +127,7 @@ func (a *retireAction) Build(ctx context.Context, b *TemplateBuilder) error { return MissingFieldsError(missing...) } - program, err := vmutil.RetireProgram(a.Arbitrary) + program, err := vmutil.RetireProgram(a.Index, a.Arbitrary) if err != nil { return err } diff --git a/protocol/vm/vmutil/script.go b/protocol/vm/vmutil/script.go index 1f0d82f9..cc23ab6d 100644 --- a/protocol/vm/vmutil/script.go +++ b/protocol/vm/vmutil/script.go @@ -56,9 +56,12 @@ func P2WSHProgram(hash []byte) ([]byte, error) { } // RetireProgram generates the script for retire output -func RetireProgram(comment []byte) ([]byte, error) { +func RetireProgram(index string, comment []byte) ([]byte, error) { builder := NewBuilder() builder.AddOp(vm.OP_FAIL) + if index != "" { + builder.AddData([]byte(index)) + } if len(comment) != 0 { builder.AddData(comment) } @@ -146,3 +149,25 @@ func GetIssuanceProgramRestrictHeight(program []byte) (int64, error) { } return 0, nil } + +func GetRetireIndex(program []byte) string { + insts, err := vm.ParseProgram(program) + if err != nil { + return "" + } + + if len(insts) == 3 && insts[0].Op == vm.OP_FAIL && insts[1].IsPushdata() && insts[2].IsPushdata() { + return string(insts[1].Data) + } + + return "" +} + +func GetRetireData(program []byte) []byte { + insts, err := vm.ParseProgram(program) + if err != nil { + return []byte{} + } + + return insts[len(insts)-1].Data +} diff --git a/wallet/annotated.go b/wallet/annotated.go index 263cb6bc..0e6869e7 100644 --- a/wallet/annotated.go +++ b/wallet/annotated.go @@ -14,10 +14,10 @@ import ( "github.com/bytom/consensus" "github.com/bytom/consensus/segwit" "github.com/bytom/crypto/sha3pool" + dbm "github.com/bytom/database/leveldb" "github.com/bytom/protocol/bc" "github.com/bytom/protocol/bc/types" "github.com/bytom/protocol/vm/vmutil" - dbm "github.com/bytom/database/leveldb" ) // annotateTxs adds asset data to transactions @@ -258,6 +258,10 @@ func (w *Wallet) BuildAnnotatedOutput(tx *types.Tx, idx int) *query.AnnotatedOut if vmutil.IsUnspendable(out.ControlProgram) { out.Type = "retire" + out.RetireData = vmutil.GetRetireData(out.ControlProgram) + if index := vmutil.GetRetireIndex(out.ControlProgram); index != "" { + out.Index = index + } } else { out.Type = "control" } diff --git a/wallet/indexer.go b/wallet/indexer.go index 7bc68d22..04d9643c 100644 --- a/wallet/indexer.go +++ b/wallet/indexer.go @@ -1,7 +1,6 @@ package wallet import ( - "encoding/binary" "encoding/json" "fmt" "sort" @@ -49,21 +48,6 @@ func calcGlobalTxIndexKey(txID string) []byte { return []byte(GlobalTxIndexPrefix + txID) } -func calcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte { - txIdx := make([]byte, 40) - copy(txIdx[:32], blockHash.Bytes()) - binary.BigEndian.PutUint64(txIdx[32:], position) - return txIdx -} - -func parseGlobalTxIdx(globalTxIdx []byte) (*bc.Hash, uint64) { - var hashBytes [32]byte - copy(hashBytes[:], globalTxIdx[:32]) - hash := bc.NewHash(hashBytes) - position := binary.BigEndian.Uint64(globalTxIdx[32:]) - return &hash, position -} - // deleteTransaction delete transactions when orphan block rollback func (w *Wallet) deleteTransactions(batch dbm.Batch, height uint64) { tmpTx := query.AnnotatedTx{} @@ -134,19 +118,15 @@ func (w *Wallet) indexTransactions(batch dbm.Batch, b *types.Block, txStatus *bc batch.Set(calcAnnotatedKey(formatKey(b.Height, uint32(tx.Position))), rawTx) batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(b.Height, uint32(tx.Position)))) + for _, out := range tx.Outputs { + if out.Index != "" { + batch.Set(calcGlobalTxIndexKey(out.Index), []byte(formatKey(b.Height, uint32(tx.Position)))) + } + } + // delete unconfirmed transaction batch.Delete(calcUnconfirmedTxKey(tx.ID.String())) } - - if !w.TxIndexFlag { - return nil - } - - for position, globalTx := range b.Transactions { - blockHash := b.BlockHeader.Hash() - batch.Set(calcGlobalTxIndexKey(globalTx.ID.String()), calcGlobalTxIndex(&blockHash, uint64(position))) - } - return nil } @@ -169,11 +149,9 @@ func (w *Wallet) filterAccountTxs(b *types.Block, txStatus *bc.TransactionStatus func (w *Wallet) GetTransactionByTxID(txID string) (*query.AnnotatedTx, error) { if annotatedTx, err := w.getAccountTxByTxID(txID); err == nil { return annotatedTx, nil - } else if !w.TxIndexFlag { - return nil, err } - return w.getGlobalTxByTxID(txID) + return w.getGlobalTxByIndex(txID) } func (w *Wallet) getAccountTxByTxID(txID string) (*query.AnnotatedTx, error) { @@ -192,30 +170,20 @@ func (w *Wallet) getAccountTxByTxID(txID string) (*query.AnnotatedTx, error) { return annotatedTx, nil } -func (w *Wallet) getGlobalTxByTxID(txID string) (*query.AnnotatedTx, error) { - globalTxIdx := w.DB.Get(calcGlobalTxIndexKey(txID)) - if globalTxIdx == nil { - return nil, fmt.Errorf("No transaction(tx_id=%s) ", txID) - } - - blockHash, pos := parseGlobalTxIdx(globalTxIdx) - block, err := w.chain.GetBlockByHash(blockHash) - if err != nil { - return nil, err - } - - txStatus, err := w.chain.GetTransactionStatus(blockHash) - if err != nil { - return nil, err +func (w *Wallet) getGlobalTxByIndex(index string) (*query.AnnotatedTx, error) { + annotatedTx := &query.AnnotatedTx{} + formatKey := w.DB.Get(calcGlobalTxIndexKey(index)) + if formatKey == nil { + return nil, errAccntTxIDNotFound } - statusFail, err := txStatus.GetStatus(int(pos)) - if err != nil { + txInfo := w.DB.Get(calcAnnotatedKey(string(formatKey))) + if err := json.Unmarshal(txInfo, annotatedTx); err != nil { return nil, err } - tx := block.Transactions[int(pos)] - return w.buildAnnotatedTransaction(tx, block, statusFail, int(pos)), nil + annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx}) + return annotatedTx, nil } // GetTransactionsSummary get transactions summary diff --git a/wallet/wallet_test.go b/wallet/wallet_test.go index 15d53ec2..44349d0e 100644 --- a/wallet/wallet_test.go +++ b/wallet/wallet_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "io/ioutil" "os" - "reflect" "testing" "time" @@ -24,26 +23,6 @@ import ( "github.com/bytom/protocol/bc/types" ) -func TestEncodeDecodeGlobalTxIndex(t *testing.T) { - want := &struct { - BlockHash bc.Hash - Position uint64 - }{ - BlockHash: bc.NewHash([32]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20}), - Position: 1, - } - - globalTxIdx := calcGlobalTxIndex(&want.BlockHash, want.Position) - blockHashGot, positionGot := parseGlobalTxIdx(globalTxIdx) - if *blockHashGot != want.BlockHash { - t.Errorf("blockHash mismatch. Get: %v. Expect: %v", *blockHashGot, want.BlockHash) - } - - if positionGot != want.Position { - t.Errorf("position mismatch. Get: %v. Expect: %v", positionGot, want.Position) - } -} - func TestWalletVersion(t *testing.T) { // prepare wallet dirPath, err := ioutil.TempDir(".", "") @@ -190,15 +169,6 @@ func TestWalletUpdate(t *testing.T) { if wants[0].ID != tx.ID { t.Fatal("account txID mismatch") } - - for position, tx := range block.Transactions { - get := w.DB.Get(calcGlobalTxIndexKey(tx.ID.String())) - bh := block.BlockHeader.Hash() - expect := calcGlobalTxIndex(&bh, uint64(position)) - if !reflect.DeepEqual(get, expect) { - t.Fatalf("position#%d: compare retrieved globalTxIdx err", position) - } - } } func TestMemPoolTxQueryLoop(t *testing.T) { -- 2.11.0