diff --git a/db/db_test.go b/db/db_test.go index f0aebb4..a34f71c 100644 --- a/db/db_test.go +++ b/db/db_test.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/csv" "encoding/hex" - "fmt" "log" "os" "testing" @@ -13,11 +12,9 @@ import ( "github.com/linxGnu/grocksdb" ) -const tmpPath = "../resources/tmp_rocksdb/" +func TestIter(t *testing.T) { -func TestClaimDiff(t *testing.T) { - - filePath := "../resources/claim_diff.csv" + filePath := "../resources/reposted_claim.csv" log.Println(filePath) file, err := os.Open(filePath) @@ -37,7 +34,13 @@ func TestClaimDiff(t *testing.T) { if err != nil { log.Println(err) } - defer db.Close() + defer func() { + db.Close() + err = os.RemoveAll("./tmp") + if err != nil { + log.Println(err) + } + }() for _, record := range records { key, err := hex.DecodeString(record[0]) if err != nil { @@ -50,13 +53,29 @@ func TestClaimDiff(t *testing.T) { db.Put(wOpts, key, val) } // test prefix - options := NewIterateOptions().WithPrefix([]byte{prefixes.ClaimDiff}).WithIncludeValue(true) + options := NewIterateOptions().WithPrefix([]byte{prefixes.RepostedClaim}).WithIncludeValue(true) ch := Iter(db, options) var i = 0 for kv := range ch { // log.Println(kv.Key) - gotKey := kv.Key.(*prefixes.TouchedOrDeletedClaimKey).PackKey() - got := kv.Value.(*prefixes.TouchedOrDeletedClaimValue).PackValue() + gotKey := kv.Key.(*prefixes.RepostedKey).PackKey() + + keyPartial3 := prefixes.RepostedKeyPackPartial(kv.Key.(*prefixes.RepostedKey), 3) + keyPartial2 := prefixes.RepostedKeyPackPartial(kv.Key.(*prefixes.RepostedKey), 2) + keyPartial1 := prefixes.RepostedKeyPackPartial(kv.Key.(*prefixes.RepostedKey), 1) + + // Check pack partial for sanity + if !bytes.HasPrefix(gotKey, keyPartial3) { + t.Errorf("%+v should be prefix of %+v\n", keyPartial3, gotKey) + } + if !bytes.HasPrefix(gotKey, keyPartial2) { + t.Errorf("%+v should be prefix of %+v\n", keyPartial2, gotKey) + } + if !bytes.HasPrefix(gotKey, keyPartial1) { + t.Errorf("%+v should be prefix of %+v\n", keyPartial1, gotKey) + } + + got := kv.Value.(*prefixes.RepostedValue).PackValue() wantKey, err := hex.DecodeString(records[i][0]) if err != nil { log.Println(err) @@ -73,10 +92,7 @@ func TestClaimDiff(t *testing.T) { } i++ } - err = os.RemoveAll("tmp") - if err != nil { - log.Println(err) - } + // Test start / stop start, err := hex.DecodeString(records[0][0]) if err != nil { @@ -90,8 +106,7 @@ func TestClaimDiff(t *testing.T) { ch2 := Iter(db, options2) i = 0 for kv := range ch2 { - log.Println(kv.Key) - got := kv.Value.(*prefixes.TouchedOrDeletedClaimValue).PackValue() + got := kv.Value.(*prefixes.RepostedValue).PackValue() want, err := hex.DecodeString(records[i][1]) if err != nil { log.Println(err) @@ -101,203 +116,4 @@ func TestClaimDiff(t *testing.T) { } i++ } - err = os.RemoveAll("tmp") - if err != nil { - log.Println(err) - } -} - -func TestUTXO(t *testing.T) { - - filePath := "../resources/utxo.csv" - - log.Println(filePath) - file, err := os.Open(filePath) - if err != nil { - log.Println(err) - } - reader := csv.NewReader(file) - records, err := reader.ReadAll() - if err != nil { - log.Println(err) - } - - wOpts := grocksdb.NewDefaultWriteOptions() - opts := grocksdb.NewDefaultOptions() - opts.SetCreateIfMissing(true) - db, err := grocksdb.OpenDb(opts, "tmp") - if err != nil { - log.Println(err) - } - defer db.Close() - for _, record := range records { - key, err := hex.DecodeString(record[0]) - if err != nil { - log.Println(err) - } - val, err := hex.DecodeString(record[1]) - if err != nil { - log.Println(err) - } - db.Put(wOpts, key, val) - } - // test prefix - options := NewIterateOptions().WithPrefix([]byte{prefixes.UTXO}).WithIncludeValue(true) - ch := Iter(db, options) - var i = 0 - for kv := range ch { - log.Println(kv.Key) - got := kv.Value.(*prefixes.UTXOValue).PackValue() - want, err := hex.DecodeString(records[i][1]) - if err != nil { - log.Println(err) - } - if !bytes.Equal(got, want) { - t.Errorf("got: %+v, want: %+v\n", got, want) - } - i++ - } - err = os.RemoveAll("./tmp") - if err != nil { - log.Println(err) - } - // Test start / stop - start, err := hex.DecodeString(records[0][0]) - if err != nil { - log.Println(err) - } - stop, err := hex.DecodeString(records[9][0]) - if err != nil { - log.Println(err) - } - options2 := NewIterateOptions().WithStart(start).WithStop(stop).WithIncludeValue(true) - ch2 := Iter(db, options2) - i = 0 - for kv := range ch2 { - log.Println(kv.Key) - got := kv.Value.(*prefixes.UTXOValue).PackValue() - want, err := hex.DecodeString(records[i][1]) - if err != nil { - log.Println(err) - } - if !bytes.Equal(got, want) { - t.Errorf("got: %+v, want: %+v\n", got, want) - } - i++ - } - err = os.RemoveAll("./tmp") - if err != nil { - log.Println(err) - } -} - -func TestHashXUTXO(t *testing.T) { - - tests := []struct { - name string - filePath string - }{ - { - name: "Read HashX_UTXO correctly", - filePath: "../resources/hashx_utxo.csv", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - log.Println(tt.filePath) - file, err := os.Open(tt.filePath) - if err != nil { - log.Println(err) - } - reader := csv.NewReader(file) - records, err := reader.ReadAll() - if err != nil { - log.Println(err) - } - - wOpts := grocksdb.NewDefaultWriteOptions() - opts := grocksdb.NewDefaultOptions() - opts.SetCreateIfMissing(true) - db, err := grocksdb.OpenDb(opts, "tmp") - if err != nil { - log.Println(err) - } - defer db.Close() - for _, record := range records { - key, err := hex.DecodeString(record[0]) - if err != nil { - log.Println(err) - } - val, err := hex.DecodeString(record[1]) - if err != nil { - log.Println(err) - } - db.Put(wOpts, key, val) - } - start, err := hex.DecodeString(records[0][0]) - if err != nil { - log.Println(err) - } - options := NewIterateOptions().WithPrefix([]byte{prefixes.HashXUTXO}).WithStart(start).WithIncludeValue(true) - ch := Iter(db, options) - var i = 0 - for kv := range ch { - log.Println(kv.Key) - got := kv.Value.(*prefixes.HashXUTXOValue).PackValue() - want, err := hex.DecodeString(records[i][1]) - if err != nil { - log.Println(err) - } - if !bytes.Equal(got, want) { - t.Errorf("got: %+v, want: %+v\n", got, want) - } - i++ - if i > 9 { - return - } - } - err = os.RemoveAll("./tmp") - if err != nil { - log.Println(err) - } - }) - } -} - -func TestUTXOKey_String(t *testing.T) { - tests := []struct { - name string - prefix []byte - hashx []byte - txnum uint32 - nout uint16 - want string - }{ - { - name: "Converts to string", - prefix: []byte("u"), - hashx: []byte("AAAAAAAAAA"), - txnum: 0, - nout: 0, - want: "*prefixes.UTXOKey(hashX=41414141414141414141, tx_num=0, nout=0)", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - key := &prefixes.UTXOKey{ - Prefix: tt.prefix, - HashX: tt.hashx, - TxNum: tt.txnum, - Nout: tt.nout, - } - - got := fmt.Sprint(key) - log.Println(got) - if got != tt.want { - t.Errorf("got: %s, want: %s\n", got, tt.want) - } - }) - } } diff --git a/db/prefixes/prefixes.go b/db/prefixes/prefixes.go index 8203dd7..869e1de 100644 --- a/db/prefixes/prefixes.go +++ b/db/prefixes/prefixes.go @@ -747,14 +747,97 @@ class RepostedValue(typing.NamedTuple): type RepostedKey struct { Prefix []byte `json:"prefix"` RepostedClaimHash []byte `json:"reposted_claim_hash"` - TxNum int32 `json:"tx_num"` - Position int32 `json:"position"` + TxNum uint32 `json:"tx_num"` + Position uint16 `json:"position"` } type RepostedValue struct { ClaimHash []byte `json:"claim_hash"` } +func (k *RepostedKey) PackKey() []byte { + prefixLen := 1 + // b'>20sLH' + n := prefixLen + 20 + 4 + 2 + key := make([]byte, n) + copy(key, k.Prefix) + copy(key[prefixLen:], k.RepostedClaimHash) + binary.BigEndian.PutUint32(key[prefixLen+20:], k.TxNum) + binary.BigEndian.PutUint16(key[prefixLen+24:], k.Position) + + return key +} + +func (v *RepostedValue) PackValue() []byte { + // b'>20s' + value := make([]byte, 20) + copy(value, v.ClaimHash) + + return value +} + +func RepostedKeyPackPartialNFields(nFields int) func(*RepostedKey) []byte { + return func(u *RepostedKey) []byte { + return RepostedKeyPackPartial(u, nFields) + } +} + +func RepostedKeyPackPartial(k *RepostedKey, nFields int) []byte { + // Limit nFields between 0 and number of fields, we always at least need + // the prefix, and we never need to iterate past the number of fields. + if nFields > 3 { + nFields = 3 + } + if nFields < 0 { + nFields = 0 + } + + prefixLen := 1 + var n = prefixLen + for i := 0; i <= nFields; i++ { + switch i { + case 1: + n += 20 + case 2: + n += 4 + case 3: + n += 2 + } + } + + key := make([]byte, n) + + for i := 0; i <= nFields; i++ { + switch i { + case 0: + copy(key, k.Prefix) + case 1: + copy(key[prefixLen:], k.RepostedClaimHash) + case 2: + binary.BigEndian.PutUint32(key[prefixLen+20:], k.TxNum) + case 3: + binary.BigEndian.PutUint16(key[prefixLen+24:], k.Position) + } + } + + return key +} + +func RepostedKeyUnpack(key []byte) *RepostedKey { + return &RepostedKey{ + Prefix: key[:1], + RepostedClaimHash: key[1:21], + TxNum: binary.BigEndian.Uint32(key[21:]), + Position: binary.BigEndian.Uint16(key[25:]), + } +} + +func RepostedValueUnpack(value []byte) *RepostedValue { + return &RepostedValue{ + ClaimHash: value[:20], + } +} + // // TouchedOrDeletedClaimKey / TouchedOrDeletedClaimValue // @@ -848,6 +931,7 @@ func (v *TouchedOrDeletedClaimValue) PackValue() []byte { value := make([]byte, n) binary.BigEndian.PutUint32(value, touchedLen) binary.BigEndian.PutUint32(value[4:], deletedLen) + // These are sorted for consistency with the Python implementation sort.Slice(v.TouchedClaims, func(i, j int) bool { return bytes.Compare(v.TouchedClaims[i], v.TouchedClaims[j]) < 0 }) sort.Slice(v.DeletedClaims, func(i, j int) bool { return bytes.Compare(v.DeletedClaims[i], v.DeletedClaims[j]) < 0 }) @@ -917,14 +1001,10 @@ func TouchedOrDeletedClaimValueUnpack(value []byte) *TouchedOrDeletedClaimValue deletedClaims := make([][]byte, deletedLen) var j = 8 for i := 0; i < int(touchedLen); i++ { - //touchedClaims[i] = make([]byte, 20) - //copy(touchedClaims[i], value[j:j+20]) touchedClaims[i] = value[j : j+20] j += 20 } for i := 0; i < int(deletedLen); i++ { - //deletedClaims[i] = make([]byte, 20) - //copy(deletedClaims[i], value[j:j+20]) deletedClaims[i] = value[j : j+20] j += 20 } @@ -1167,7 +1247,9 @@ func UnpackGenericKey(key []byte) (byte, interface{}, error) { case ActiveAmount: case Repost: + return 0x0, nil, errors.Base("key unpack function for %v not implemented", firstByte) case RepostedClaim: + return RepostedClaim, RepostedKeyUnpack(key), nil case Undo: return 0x0, nil, errors.Base("key unpack function for %v not implemented", firstByte) @@ -1223,7 +1305,9 @@ func UnpackGenericValue(key, value []byte) (byte, interface{}, error) { case ActiveAmount: case Repost: + return 0x0, nil, nil case RepostedClaim: + return RepostedClaim, RepostedValueUnpack(value), nil case Undo: return 0x0, nil, nil diff --git a/db/prefixes/prefixes_test.go b/db/prefixes/prefixes_test.go new file mode 100644 index 0000000..66ca496 --- /dev/null +++ b/db/prefixes/prefixes_test.go @@ -0,0 +1,416 @@ +package prefixes_test + +import ( + "bytes" + "encoding/csv" + "encoding/hex" + "fmt" + "log" + "os" + "testing" + + dbpkg "github.com/lbryio/hub/db" + "github.com/lbryio/hub/db/prefixes" + "github.com/linxGnu/grocksdb" +) + +func TestRepostedClaim(t *testing.T) { + + filePath := "../../resources/reposted_claim.csv" + + log.Println(filePath) + file, err := os.Open(filePath) + if err != nil { + log.Println(err) + } + reader := csv.NewReader(file) + records, err := reader.ReadAll() + if err != nil { + log.Println(err) + } + + wOpts := grocksdb.NewDefaultWriteOptions() + opts := grocksdb.NewDefaultOptions() + opts.SetCreateIfMissing(true) + db, err := grocksdb.OpenDb(opts, "tmp") + if err != nil { + log.Println(err) + } + defer func() { + db.Close() + err = os.RemoveAll("./tmp") + if err != nil { + log.Println(err) + } + }() + for _, record := range records { + key, err := hex.DecodeString(record[0]) + if err != nil { + log.Println(err) + } + val, err := hex.DecodeString(record[1]) + if err != nil { + log.Println(err) + } + db.Put(wOpts, key, val) + } + // test prefix + options := dbpkg.NewIterateOptions().WithPrefix([]byte{prefixes.RepostedClaim}).WithIncludeValue(true) + ch := dbpkg.Iter(db, options) + var i = 0 + for kv := range ch { + // log.Println(kv.Key) + gotKey := kv.Key.(*prefixes.RepostedKey).PackKey() + + keyPartial3 := prefixes.RepostedKeyPackPartial(kv.Key.(*prefixes.RepostedKey), 3) + keyPartial2 := prefixes.RepostedKeyPackPartial(kv.Key.(*prefixes.RepostedKey), 2) + keyPartial1 := prefixes.RepostedKeyPackPartial(kv.Key.(*prefixes.RepostedKey), 1) + + // Check pack partial for sanity + if !bytes.HasPrefix(gotKey, keyPartial3) { + t.Errorf("%+v should be prefix of %+v\n", keyPartial3, gotKey) + } + if !bytes.HasPrefix(gotKey, keyPartial2) { + t.Errorf("%+v should be prefix of %+v\n", keyPartial2, gotKey) + } + if !bytes.HasPrefix(gotKey, keyPartial1) { + t.Errorf("%+v should be prefix of %+v\n", keyPartial1, gotKey) + } + + got := kv.Value.(*prefixes.RepostedValue).PackValue() + wantKey, err := hex.DecodeString(records[i][0]) + if err != nil { + log.Println(err) + } + want, err := hex.DecodeString(records[i][1]) + if err != nil { + log.Println(err) + } + if !bytes.Equal(gotKey, wantKey) { + t.Errorf("gotKey: %+v, wantKey: %+v\n", got, want) + } + if !bytes.Equal(got, want) { + t.Errorf("got: %+v, want: %+v\n", got, want) + } + i++ + } + + // Test start / stop + start, err := hex.DecodeString(records[0][0]) + if err != nil { + log.Println(err) + } + stop, err := hex.DecodeString(records[9][0]) + if err != nil { + log.Println(err) + } + options2 := dbpkg.NewIterateOptions().WithStart(start).WithStop(stop).WithIncludeValue(true) + ch2 := dbpkg.Iter(db, options2) + i = 0 + for kv := range ch2 { + got := kv.Value.(*prefixes.RepostedValue).PackValue() + want, err := hex.DecodeString(records[i][1]) + if err != nil { + log.Println(err) + } + if !bytes.Equal(got, want) { + t.Errorf("got: %+v, want: %+v\n", got, want) + } + i++ + } +} + +func TestClaimDiff(t *testing.T) { + + filePath := "../../resources/claim_diff.csv" + + log.Println(filePath) + file, err := os.Open(filePath) + if err != nil { + log.Println(err) + } + reader := csv.NewReader(file) + records, err := reader.ReadAll() + if err != nil { + log.Println(err) + } + + wOpts := grocksdb.NewDefaultWriteOptions() + opts := grocksdb.NewDefaultOptions() + opts.SetCreateIfMissing(true) + db, err := grocksdb.OpenDb(opts, "tmp") + if err != nil { + log.Println(err) + } + defer func() { + db.Close() + err = os.RemoveAll("./tmp") + if err != nil { + log.Println(err) + } + }() + for _, record := range records { + key, err := hex.DecodeString(record[0]) + if err != nil { + log.Println(err) + } + val, err := hex.DecodeString(record[1]) + if err != nil { + log.Println(err) + } + db.Put(wOpts, key, val) + } + // test prefix + options := dbpkg.NewIterateOptions().WithPrefix([]byte{prefixes.ClaimDiff}).WithIncludeValue(true) + ch := dbpkg.Iter(db, options) + var i = 0 + for kv := range ch { + // log.Println(kv.Key) + gotKey := kv.Key.(*prefixes.TouchedOrDeletedClaimKey).PackKey() + + keyPartial1 := prefixes.TouchedOrDeletedClaimKeyPackPartial(kv.Key.(*prefixes.TouchedOrDeletedClaimKey), 1) + + // Check pack partial for sanity + if !bytes.HasPrefix(gotKey, keyPartial1) { + t.Errorf("%+v should be prefix of %+v\n", keyPartial1, gotKey) + } + + got := kv.Value.(*prefixes.TouchedOrDeletedClaimValue).PackValue() + wantKey, err := hex.DecodeString(records[i][0]) + if err != nil { + log.Println(err) + } + want, err := hex.DecodeString(records[i][1]) + if err != nil { + log.Println(err) + } + if !bytes.Equal(gotKey, wantKey) { + t.Errorf("gotKey: %+v, wantKey: %+v\n", got, want) + } + if !bytes.Equal(got, want) { + t.Errorf("got: %+v, want: %+v\n", got, want) + } + i++ + } + + // Test start / stop + start, err := hex.DecodeString(records[0][0]) + if err != nil { + log.Println(err) + } + stop, err := hex.DecodeString(records[9][0]) + if err != nil { + log.Println(err) + } + options2 := dbpkg.NewIterateOptions().WithStart(start).WithStop(stop).WithIncludeValue(true) + ch2 := dbpkg.Iter(db, options2) + i = 0 + for kv := range ch2 { + log.Println(kv.Key) + got := kv.Value.(*prefixes.TouchedOrDeletedClaimValue).PackValue() + want, err := hex.DecodeString(records[i][1]) + if err != nil { + log.Println(err) + } + if !bytes.Equal(got, want) { + t.Errorf("got: %+v, want: %+v\n", got, want) + } + i++ + } +} + +func TestUTXO(t *testing.T) { + + filePath := "../../resources/utxo.csv" + + log.Println(filePath) + file, err := os.Open(filePath) + if err != nil { + log.Println(err) + } + reader := csv.NewReader(file) + records, err := reader.ReadAll() + if err != nil { + log.Println(err) + } + + wOpts := grocksdb.NewDefaultWriteOptions() + opts := grocksdb.NewDefaultOptions() + opts.SetCreateIfMissing(true) + db, err := grocksdb.OpenDb(opts, "tmp") + if err != nil { + log.Println(err) + } + defer func() { + db.Close() + err = os.RemoveAll("./tmp") + if err != nil { + log.Println(err) + } + }() + for _, record := range records { + key, err := hex.DecodeString(record[0]) + if err != nil { + log.Println(err) + } + val, err := hex.DecodeString(record[1]) + if err != nil { + log.Println(err) + } + db.Put(wOpts, key, val) + } + // test prefix + options := dbpkg.NewIterateOptions().WithPrefix([]byte{prefixes.UTXO}).WithIncludeValue(true) + ch := dbpkg.Iter(db, options) + var i = 0 + for kv := range ch { + log.Println(kv.Key) + got := kv.Value.(*prefixes.UTXOValue).PackValue() + want, err := hex.DecodeString(records[i][1]) + if err != nil { + log.Println(err) + } + if !bytes.Equal(got, want) { + t.Errorf("got: %+v, want: %+v\n", got, want) + } + i++ + } + + // Test start / stop + start, err := hex.DecodeString(records[0][0]) + if err != nil { + log.Println(err) + } + stop, err := hex.DecodeString(records[9][0]) + if err != nil { + log.Println(err) + } + options2 := dbpkg.NewIterateOptions().WithStart(start).WithStop(stop).WithIncludeValue(true) + ch2 := dbpkg.Iter(db, options2) + i = 0 + for kv := range ch2 { + log.Println(kv.Key) + got := kv.Value.(*prefixes.UTXOValue).PackValue() + want, err := hex.DecodeString(records[i][1]) + if err != nil { + log.Println(err) + } + if !bytes.Equal(got, want) { + t.Errorf("got: %+v, want: %+v\n", got, want) + } + i++ + } +} + +func TestHashXUTXO(t *testing.T) { + + tests := []struct { + name string + filePath string + }{ + { + name: "Read HashX_UTXO correctly", + filePath: "../../resources/hashx_utxo.csv", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + log.Println(tt.filePath) + file, err := os.Open(tt.filePath) + if err != nil { + log.Println(err) + } + reader := csv.NewReader(file) + records, err := reader.ReadAll() + if err != nil { + log.Println(err) + } + + wOpts := grocksdb.NewDefaultWriteOptions() + opts := grocksdb.NewDefaultOptions() + opts.SetCreateIfMissing(true) + db, err := grocksdb.OpenDb(opts, "tmp") + if err != nil { + log.Println(err) + } + defer func() { + db.Close() + err = os.RemoveAll("./tmp") + if err != nil { + log.Println(err) + } + }() + for _, record := range records { + key, err := hex.DecodeString(record[0]) + if err != nil { + log.Println(err) + } + val, err := hex.DecodeString(record[1]) + if err != nil { + log.Println(err) + } + db.Put(wOpts, key, val) + } + start, err := hex.DecodeString(records[0][0]) + if err != nil { + log.Println(err) + } + options := dbpkg.NewIterateOptions().WithPrefix([]byte{prefixes.HashXUTXO}).WithStart(start).WithIncludeValue(true) + ch := dbpkg.Iter(db, options) + var i = 0 + for kv := range ch { + log.Println(kv.Key) + got := kv.Value.(*prefixes.HashXUTXOValue).PackValue() + want, err := hex.DecodeString(records[i][1]) + if err != nil { + log.Println(err) + } + if !bytes.Equal(got, want) { + t.Errorf("got: %+v, want: %+v\n", got, want) + } + i++ + if i > 9 { + return + } + } + }) + } +} + +func TestUTXOKey_String(t *testing.T) { + tests := []struct { + name string + prefix []byte + hashx []byte + txnum uint32 + nout uint16 + want string + }{ + { + name: "Converts to string", + prefix: []byte("u"), + hashx: []byte("AAAAAAAAAA"), + txnum: 0, + nout: 0, + want: "*prefixes.UTXOKey(hashX=41414141414141414141, tx_num=0, nout=0)", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + key := &prefixes.UTXOKey{ + Prefix: tt.prefix, + HashX: tt.hashx, + TxNum: tt.txnum, + Nout: tt.nout, + } + + got := fmt.Sprint(key) + log.Println(got) + if got != tt.want { + t.Errorf("got: %s, want: %s\n", got, tt.want) + } + }) + } +} diff --git a/resources/reposted_claim.csv b/resources/reposted_claim.csv new file mode 100644 index 0000000..2544bb7 --- /dev/null +++ b/resources/reposted_claim.csv @@ -0,0 +1,10 @@ +5700003eb3f3f17af2cf79b286dc1952f6c3df2e1d03947d150000,4dd814e8ae0fc8feead86bf6c723bcc45b224c44 +570000997ae50674ed318475cb207bf1902e01de7b02d931f70000,70e7073d180053a3e8815116e68466b8a9819eaa +570000997ae50674ed318475cb207bf1902e01de7b031b5d5b0000,1d89ae62063d38c9ad5833721af2a2a8670a60ff +5700009c47ca0687ea07155efc2a6ba803180066f1029e8f850000,028adb1480ac50803d11ebd97d54a21b8a7b7839 +5700009d1409ca5962d3d2c8f638cd75a312af03c4015f8a440000,ca28a84248c434bb6f2d6484cae2fbd8f841362b +570000bcdff7762b712ac3a74634a0b27bcdd6b7e400c721410000,033c9374b2dd89eb357769ff234742c7d9e91ee4 +570000cda50bcdc44f55045c8c0ce4ba094a17ef6d012a443d0000,0a23389dfba22c2a955e7283e0a23c44590c429a +570001034f15a28f221d469e42edc38b48a244cabd0329f5a60000,b30442f45c7c0dd4595e492cf02529c5fc6c58ac +57000115df1d87a9346c1402dfba75fe04c4a66e36032e99280000,b13a360dda378ed53c5a502a0769cd841b85ea60 +570001205a75aeb993d1e47d370c40301b1c77a2f60249e1fa0000,7e243aee3dbefe46c285dadd354dea427cc167e3