diff --git a/bchain/baseparser.go b/bchain/baseparser.go index 8b747ed9..791f3b9f 100644 --- a/bchain/baseparser.go +++ b/bchain/baseparser.go @@ -42,6 +42,9 @@ func (p *BaseParser) KeepBlockAddresses() int { // PackTxid packs txid to byte array func (p *BaseParser) PackTxid(txid string) ([]byte, error) { + if txid == "" { + return nil, ErrTxidMissing + } return hex.DecodeString(txid) } diff --git a/bchain/types.go b/bchain/types.go index 46effc48..ddac7be6 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -14,6 +14,9 @@ var ( // ErrAddressMissing is returned if address is not specified // for example To address in ethereum can be missing in case of contract transaction ErrAddressMissing = errors.New("Address missing") + // ErrTxidMissing is returned if txid is not specified + // for example coinbase transactions in Bitcoin + ErrTxidMissing = errors.New("Txid missing") ) type ScriptSig struct { diff --git a/db/rocksdb.go b/db/rocksdb.go index b155fe52..fd2f33e8 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -335,25 +335,25 @@ func appendPackedAddrID(txAddrs []byte, addrID []byte, n uint32, remaining int) return txAddrs } -func findAndRemoveUnspentAddr(unspentAddrs []byte, vout uint32) ([]byte, uint32, []byte) { +func findAndRemoveUnspentAddr(unspentAddrs []byte, vout uint32) ([]byte, []byte) { // the addresses are packed as lenaddrID addrID vout, where lenaddrID and vout are varints for i := 0; i < len(unspentAddrs); { l, lv1 := unpackVarint(unspentAddrs[i:]) // index of vout of address in unspentAddrs j := i + int(l) + lv1 if j >= len(unspentAddrs) { - glog.Error("rocksdb: Inconsistent data in unspentAddrs") - return nil, 0, unspentAddrs + glog.Error("rocksdb: Inconsistent data in unspentAddrs ", hex.EncodeToString(unspentAddrs), ", ", vout) + return nil, unspentAddrs } n, lv2 := unpackVarint(unspentAddrs[j:]) if uint32(n) == vout { addrID := append([]byte(nil), unspentAddrs[i+lv1:j]...) unspentAddrs = append(unspentAddrs[:i], unspentAddrs[j+lv2:]...) - return addrID, uint32(n), unspentAddrs + return addrID, unspentAddrs } - i += j + lv2 + i = j + lv2 } - return nil, 0, unspentAddrs + return nil, unspentAddrs } func (d *RocksDB) writeAddressesUTXO(wb *gorocksdb.WriteBatch, block *bchain.Block, op int) error { @@ -397,6 +397,10 @@ func (d *RocksDB) writeAddressesUTXO(wb *gorocksdb.WriteBatch, block *bchain.Blo for i, input := range tx.Vin { btxID, err := d.chainParser.PackTxid(input.Txid) if err != nil { + // do not process inputs without input txid + if err == bchain.ErrTxidMissing { + continue + } return err } // try to find the tx in current block @@ -412,7 +416,7 @@ func (d *RocksDB) writeAddressesUTXO(wb *gorocksdb.WriteBatch, block *bchain.Blo } } var addrID []byte - addrID, _, unspentAddrs = findAndRemoveUnspentAddr(unspentAddrs, input.Vout) + addrID, unspentAddrs = findAndRemoveUnspentAddr(unspentAddrs, input.Vout) if addrID == nil { glog.Warningf("rocksdb: height %d, tx %v vout %v in inputs but missing in unspentTxs", block.Height, tx.Txid, input.Vout) continue diff --git a/db/rocksdb_test.go b/db/rocksdb_test.go index 9fc84241..ea2ef808 100644 --- a/db/rocksdb_test.go +++ b/db/rocksdb_test.go @@ -491,3 +491,61 @@ func TestRocksDB_Index_UTXO(t *testing.T) { // disconnect the 2nd block, verify that the db contains only the 1st block } + +func Test_findAndRemoveUnspentAddr(t *testing.T) { + type args struct { + unspentAddrs string + vout uint32 + } + tests := []struct { + name string + args args + want string + want2 string + }{ + { + name: "3", + args: args{ + unspentAddrs: "029c0010517a0115887452870212709393588893935687040e64635167006868060e76519351880087080a7b7b0115870a3276a9144150837fb91d9461d6b95059842ab85262c2923f88ac0c08636751680e04578710029112026114", + vout: 3, + }, + want: "64635167006868", + want2: "029c0010517a0115887452870212709393588893935687040e76519351880087080a7b7b0115870a3276a9144150837fb91d9461d6b95059842ab85262c2923f88ac0c08636751680e04578710029112026114", + }, + { + name: "10", + args: args{ + unspentAddrs: "029c0010517a0115887452870212709393588893935687040e64635167006868060e76519351880087080a7b7b0115870a3276a9144150837fb91d9461d6b95059842ab85262c2923f88ac0c08636751680e04578710029112026114", + vout: 10, + }, + want: "61", + want2: "029c0010517a0115887452870212709393588893935687040e64635167006868060e76519351880087080a7b7b0115870a3276a9144150837fb91d9461d6b95059842ab85262c2923f88ac0c08636751680e04578710029112", + }, + { + name: "not there", + args: args{ + unspentAddrs: "029c0010517a0115887452870212709393588893935687040e64635167006868060e76519351880087080a7b7b0115870a3276a9144150837fb91d9461d6b95059842ab85262c2923f88ac0c08636751680e04578710029112026114", + vout: 11, + }, + want: "", + want2: "029c0010517a0115887452870212709393588893935687040e64635167006868060e76519351880087080a7b7b0115870a3276a9144150837fb91d9461d6b95059842ab85262c2923f88ac0c08636751680e04578710029112026114", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b, err := hex.DecodeString(tt.args.unspentAddrs) + if err != nil { + panic(err) + } + got, got2 := findAndRemoveUnspentAddr(b, tt.args.vout) + h := hex.EncodeToString(got) + if !reflect.DeepEqual(h, tt.want) { + t.Errorf("findAndRemoveUnspentAddr() got = %v, want %v", h, tt.want) + } + h2 := hex.EncodeToString(got2) + if !reflect.DeepEqual(h2, tt.want2) { + t.Errorf("findAndRemoveUnspentAddr() got2 = %v, want %v", h2, tt.want2) + } + }) + } +}