blockservice ethdb tests

This commit is contained in:
Ian Norden 2020-07-09 14:49:01 -05:00
parent 09277325ef
commit 8daba4f4fa
15 changed files with 350 additions and 78 deletions

View File

@ -19,10 +19,12 @@ package ipfsethdb
import ( import (
"errors" "errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/hashicorp/golang-lru" "github.com/hashicorp/golang-lru"
"github.com/ipfs/go-block-format" "github.com/ipfs/go-block-format"
"github.com/ipfs/go-blockservice" "github.com/ipfs/go-blockservice"
"github.com/ipfs/go-cid/_rsrch/cidiface"
) )
var ( var (
@ -64,7 +66,8 @@ func NewBatch(bs blockservice.BlockService, capacity int) (ethdb.Batch, error) {
// but this adds additional overhead to every Put/Delete // but this adds additional overhead to every Put/Delete
func (b *Batch) Put(key []byte, value []byte) (err error) { func (b *Batch) Put(key []byte, value []byte) (err error) {
b.valueSize += len(value) b.valueSize += len(value)
if b.putCache.Add(key, value) { strKey := common.Bytes2Hex(key)
if b.putCache.Add(strKey, value) {
return EvictionWarningErr return EvictionWarningErr
} }
return nil return nil
@ -73,7 +76,8 @@ func (b *Batch) Put(key []byte, value []byte) (err error) {
// Delete satisfies the ethdb.Batch interface // Delete satisfies the ethdb.Batch interface
// Delete removes the key from the key-value data store // Delete removes the key from the key-value data store
func (b *Batch) Delete(key []byte) (err error) { func (b *Batch) Delete(key []byte) (err error) {
if b.deleteCache.Add(key, true) { strKey := common.Bytes2Hex(key)
if b.deleteCache.Add(strKey, true) {
return EvictionWarningErr return EvictionWarningErr
} }
return nil return nil
@ -92,7 +96,7 @@ func (b *Batch) Write() error {
puts := make([]blocks.Block, b.putCache.Len()) puts := make([]blocks.Block, b.putCache.Len())
for i, key := range b.putCache.Keys() { for i, key := range b.putCache.Keys() {
val, _ := b.putCache.Get(key) // don't need to check "ok"s, the key is known and val is always []byte val, _ := b.putCache.Get(key) // don't need to check "ok"s, the key is known and val is always []byte
b, err := NewBlock(key.([]byte), val.([]byte)) b, err := NewBlock(common.Hex2Bytes(key.(string)), val.([]byte))
if err != nil { if err != nil {
return err return err
} }
@ -102,7 +106,8 @@ func (b *Batch) Write() error {
return err return err
} }
for _, key := range b.deleteCache.Keys() { for _, key := range b.deleteCache.Keys() {
c, err := Keccak256ToCid(key.([]byte)) // we are using state codec because we don't know the codec and at this level the codec doesn't matter, the datastore key is multihash-only derived
c, err := Keccak256ToCid(common.Hex2Bytes(key.(string)), cid.EthStateTrie)
if err != nil { if err != nil {
return err return err
} }

114
batch_test.go Normal file
View File

@ -0,0 +1,114 @@
// VulcanizeDB
// Copyright © 2019 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package ipfsethdb_test
import (
"math/big"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
ipfsethdb "github.com/vulcanize/ipfs-ethdb"
)
var (
batch ethdb.Batch
testHeader2 = types.Header{Number: big.NewInt(2)}
testValue2, _ = rlp.EncodeToBytes(testHeader2)
testEthKey2 = testHeader2.Hash().Bytes()
)
var _ = Describe("Batch", func() {
BeforeEach(func() {
blockService = ipfsethdb.NewMockBlockservice()
batch, err = ipfsethdb.NewBatch(blockService, 1024)
Expect(err).ToNot(HaveOccurred())
database = ipfsethdb.NewDatabase(blockService)
})
Describe("Put/Write", func() {
It("adds the key-value pair to the batch", func() {
_, err = database.Get(testEthKey)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("block not found"))
_, err = database.Get(testEthKey2)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("block not found"))
err = batch.Put(testEthKey, testValue)
Expect(err).ToNot(HaveOccurred())
err = batch.Put(testEthKey2, testValue2)
Expect(err).ToNot(HaveOccurred())
err = batch.Write()
Expect(err).ToNot(HaveOccurred())
val, err := database.Get(testEthKey)
Expect(err).ToNot(HaveOccurred())
Expect(val).To(Equal(testValue))
val2, err := database.Get(testEthKey2)
Expect(err).ToNot(HaveOccurred())
Expect(val2).To(Equal(testValue2))
})
})
Describe("Delete/Reset/Write", func() {
It("deletes the key-value pair in the batch", func() {
err = batch.Put(testEthKey, testValue)
Expect(err).ToNot(HaveOccurred())
err = batch.Put(testEthKey2, testValue2)
Expect(err).ToNot(HaveOccurred())
err = batch.Write()
Expect(err).ToNot(HaveOccurred())
batch.Reset()
err = batch.Delete(testEthKey)
Expect(err).ToNot(HaveOccurred())
err = batch.Delete(testEthKey2)
Expect(err).ToNot(HaveOccurred())
err = batch.Write()
Expect(err).ToNot(HaveOccurred())
_, err = database.Get(testEthKey)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("block not found"))
_, err = database.Get(testEthKey2)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("block not found"))
})
})
Describe("ValueSize/Reset", func() {
It("returns the size of data in the batch queued for write", func() {
err = batch.Put(testEthKey, testValue)
Expect(err).ToNot(HaveOccurred())
err = batch.Put(testEthKey2, testValue2)
Expect(err).ToNot(HaveOccurred())
err = batch.Write()
Expect(err).ToNot(HaveOccurred())
size := batch.ValueSize()
Expect(size).To(Equal(len(testValue) + len(testValue2)))
batch.Reset()
size = batch.ValueSize()
Expect(size).To(Equal(0))
})
})
})

View File

@ -20,6 +20,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/ipfs/go-cid/_rsrch/cidiface"
"strconv" "strconv"
"strings" "strings"
@ -57,7 +58,8 @@ func NewDatabase(bs blockservice.BlockService) ethdb.Database {
// Has retrieves if a key is present in the key-value data store // Has retrieves if a key is present in the key-value data store
// This only operates on the local blockstore not through the exchange // This only operates on the local blockstore not through the exchange
func (d *Database) Has(key []byte) (bool, error) { func (d *Database) Has(key []byte) (bool, error) {
c, err := Keccak256ToCid(key) // we are using cidv0 because we don't know the codec and codec doesn't matter, the datastore key is multihash-only derived // we are using state codec because we don't know the codec and at this level the codec doesn't matter, the datastore key is multihash-only derived
c, err := Keccak256ToCid(key, cid.EthStateTrie)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -67,12 +69,16 @@ func (d *Database) Has(key []byte) (bool, error) {
// Get satisfies the ethdb.KeyValueReader interface // Get satisfies the ethdb.KeyValueReader interface
// Get retrieves the given key if it's present in the key-value data store // Get retrieves the given key if it's present in the key-value data store
func (d *Database) Get(key []byte) ([]byte, error) { func (d *Database) Get(key []byte) ([]byte, error) {
c, err := Keccak256ToCid(key) // we are using state codec because we don't know the codec and at this level the codec doesn't matter, the datastore key is multihash-only derived
c, err := Keccak256ToCid(key, cid.EthStateTrie)
if err != nil { if err != nil {
return nil, err return nil, err
} }
block, err := d.blockService.GetBlock(context.Background(), c) block, err := d.blockService.GetBlock(context.Background(), c)
return block.RawData(), err if err != nil {
return nil, err
}
return block.RawData(), nil
} }
// Put satisfies the ethdb.KeyValueWriter interface // Put satisfies the ethdb.KeyValueWriter interface
@ -89,7 +95,8 @@ func (d *Database) Put(key []byte, value []byte) error {
// Delete satisfies the ethdb.KeyValueWriter interface // Delete satisfies the ethdb.KeyValueWriter interface
// Delete removes the key from the key-value data store // Delete removes the key from the key-value data store
func (d *Database) Delete(key []byte) error { func (d *Database) Delete(key []byte) error {
c, err := Keccak256ToCid(key) // we are using state codec because we don't know the codec and at this level the codec doesn't matter, the datastore key is multihash-only derived
c, err := Keccak256ToCid(key, cid.EthStateTrie)
if err != nil { if err != nil {
return err return err
} }

106
database_test.go Normal file
View File

@ -0,0 +1,106 @@
// VulcanizeDB
// Copyright © 2019 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package ipfsethdb_test
import (
"math/big"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ipfs/go-blockservice"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
ipfsethdb "github.com/vulcanize/ipfs-ethdb"
)
var (
database ethdb.Database
blockService blockservice.BlockService
err error
testHeader = types.Header{Number: big.NewInt(1337)}
testValue, _ = rlp.EncodeToBytes(testHeader)
testEthKey = testHeader.Hash().Bytes()
)
var _ = Describe("Database", func() {
BeforeEach(func() {
blockService = ipfsethdb.NewMockBlockservice()
database = ipfsethdb.NewDatabase(blockService)
})
Describe("Has", func() {
It("returns false if a key-pair doesn't exist in the db", func() {
has, err := database.Has(testEthKey)
Expect(err).ToNot(HaveOccurred())
Expect(has).ToNot(BeTrue())
})
It("returns true if a key-pair exists in the db", func() {
err := database.Put(testEthKey, testValue)
Expect(err).ToNot(HaveOccurred())
has, err := database.Has(testEthKey)
Expect(err).ToNot(HaveOccurred())
Expect(has).To(BeTrue())
})
})
Describe("Get", func() {
It("throws an err if the key-pair doesn't exist in the db", func() {
_, err = database.Get(testEthKey)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("block not found"))
})
It("returns the value associated with the key, if the pair exists", func() {
err := database.Put(testEthKey, testValue)
Expect(err).ToNot(HaveOccurred())
val, err := database.Get(testEthKey)
Expect(err).ToNot(HaveOccurred())
Expect(val).To(Equal(testValue))
})
})
Describe("Put", func() {
It("persists the key-value pair in the database", func() {
_, err = database.Get(testEthKey)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("block not found"))
err = database.Put(testEthKey, testValue)
Expect(err).ToNot(HaveOccurred())
val, err := database.Get(testEthKey)
Expect(err).ToNot(HaveOccurred())
Expect(val).To(Equal(testValue))
})
})
Describe("Delete", func() {
It("removes the key-value pair from the database", func() {
err = database.Put(testEthKey, testValue)
Expect(err).ToNot(HaveOccurred())
val, err := database.Get(testEthKey)
Expect(err).ToNot(HaveOccurred())
Expect(val).To(Equal(testValue))
err = database.Delete(testEthKey)
Expect(err).ToNot(HaveOccurred())
_, err = database.Get(testEthKey)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("block not found"))
})
})
})

3
go.mod
View File

@ -1,4 +1,4 @@
module github.com/vulcanize/pg-ipfs-ethdb module github.com/vulcanize/ipfs-ethdb
go 1.13 go 1.13
@ -10,6 +10,7 @@ require (
github.com/ipfs/go-cid v0.0.5 github.com/ipfs/go-cid v0.0.5
github.com/ipfs/go-ipfs-blockstore v1.0.0 github.com/ipfs/go-ipfs-blockstore v1.0.0
github.com/ipfs/go-ipfs-ds-help v1.0.0 github.com/ipfs/go-ipfs-ds-help v1.0.0
github.com/ipfs/go-ipfs-exchange-interface v0.0.1
github.com/jmoiron/sqlx v1.2.0 github.com/jmoiron/sqlx v1.2.0
github.com/lib/pq v1.0.0 github.com/lib/pq v1.0.0
github.com/multiformats/go-multihash v0.0.13 github.com/multiformats/go-multihash v0.0.13

6
go.sum
View File

@ -117,7 +117,6 @@ github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67Fexh
github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
github.com/ipfs/go-datastore v0.0.5/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.0.5/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=
github.com/ipfs/go-datastore v0.4.1 h1:W4ZfzyhNi3xmuU5dQhjfuRn/wFuqEE1KnOmmQiOevEY=
github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
github.com/ipfs/go-datastore v0.4.4 h1:rjvQ9+muFaJ+QZ7dN5B1MSDNQ0JVZKkkES/rMZmA8X8= github.com/ipfs/go-datastore v0.4.4 h1:rjvQ9+muFaJ+QZ7dN5B1MSDNQ0JVZKkkES/rMZmA8X8=
github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
@ -155,7 +154,6 @@ github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH
github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs=
github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs=
github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8 h1:bspPhN+oKYFk5fcGNuQzp6IGzYQSenLEgH3s6jkXrWw=
github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY=
github.com/jbenet/goprocess v0.1.3 h1:YKyIEECS/XvcfHtBzxtjBBbWK+MbvA6dG8ASiqwvr10= github.com/jbenet/goprocess v0.1.3 h1:YKyIEECS/XvcfHtBzxtjBBbWK+MbvA6dG8ASiqwvr10=
github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
@ -222,14 +220,12 @@ github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw
github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@ -275,11 +271,9 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=

View File

@ -18,6 +18,7 @@ package ipfsethdb
import ( import (
"context" "context"
"github.com/ipfs/go-cid/_rsrch/cidiface"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ipfs/go-blockservice" "github.com/ipfs/go-blockservice"
@ -72,7 +73,8 @@ func (i *Iterator) Key() []byte {
// The caller should not modify the contents of the returned slice // The caller should not modify the contents of the returned slice
// and its contents may change on the next call to Next // and its contents may change on the next call to Next
func (i *Iterator) Value() []byte { func (i *Iterator) Value() []byte {
c, err := Keccak256ToCid(i.currentKey) // we are using state codec because we don't know the codec and at this level the codec doesn't matter, the datastore key is multihash-only derived
c, err := Keccak256ToCid(i.currentKey, cid.EthStateTrie)
if err != nil { if err != nil {
i.err = err i.err = err
return nil return nil

View File

@ -19,8 +19,6 @@ package pgipfsethdb
import ( import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/vulcanize/pg-ipfs-ethdb"
) )
// Batch is the type that satisfies the ethdb.Batch interface for PG-IPFS Ethereum data using a direct Postgres connection // Batch is the type that satisfies the ethdb.Batch interface for PG-IPFS Ethereum data using a direct Postgres connection
@ -31,20 +29,23 @@ type Batch struct {
} }
// NewBatch returns a ethdb.Batch interface for PG-IPFS // NewBatch returns a ethdb.Batch interface for PG-IPFS
func NewBatch(db *sqlx.DB) ethdb.Batch { //
return &Batch{ func NewBatch(db *sqlx.DB, tx *sqlx.Tx) ethdb.Batch {
b := &Batch{
db: db, db: db,
tx: tx,
} }
if tx == nil {
b.Reset()
}
return b
} }
// Put satisfies the ethdb.Batch interface // Put satisfies the ethdb.Batch interface
// Put inserts the given value into the key-value data store // Put inserts the given value into the key-value data store
// Key is expected to be the keccak256 hash of value // Key is expected to be the keccak256 hash of value
func (b *Batch) Put(key []byte, value []byte) (err error) { func (b *Batch) Put(key []byte, value []byte) (err error) {
if b.tx == nil { mhKey, err := MultihashKeyFromKeccak256(key)
b.Reset()
}
mhKey, err := ipfsethdb.MultihashKeyFromKeccak256(key)
if err != nil { if err != nil {
return err return err
} }
@ -58,10 +59,7 @@ func (b *Batch) Put(key []byte, value []byte) (err error) {
// Delete satisfies the ethdb.Batch interface // Delete satisfies the ethdb.Batch interface
// Delete removes the key from the key-value data store // Delete removes the key from the key-value data store
func (b *Batch) Delete(key []byte) (err error) { func (b *Batch) Delete(key []byte) (err error) {
if b.tx == nil { mhKey, err := MultihashKeyFromKeccak256(key)
b.Reset()
}
mhKey, err := ipfsethdb.MultihashKeyFromKeccak256(key)
if err != nil { if err != nil {
return err return err
} }

View File

@ -25,8 +25,7 @@ import (
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/vulcanize/pg-ipfs-ethdb" pgipfsethdb "github.com/vulcanize/ipfs-ethdb/postgres"
"github.com/vulcanize/pg-ipfs-ethdb/postgres"
) )
var ( var (
@ -38,13 +37,13 @@ var (
var _ = Describe("Batch", func() { var _ = Describe("Batch", func() {
BeforeEach(func() { BeforeEach(func() {
db, err = ipfsethdb.TestDB() db, err = pgipfsethdb.TestDB()
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
database = pgipfsethdb.NewDatabase(db) database = pgipfsethdb.NewDatabase(db)
batch = database.NewBatch() batch = database.NewBatch()
}) })
AfterEach(func() { AfterEach(func() {
err = ipfsethdb.ResetTestDB(db) err = pgipfsethdb.ResetTestDB(db)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
}) })
@ -75,7 +74,6 @@ var _ = Describe("Batch", func() {
Describe("Delete/Reset/Write", func() { Describe("Delete/Reset/Write", func() {
It("deletes the key-value pair in the batch", func() { It("deletes the key-value pair in the batch", func() {
_, err = db.Exec("INSERT into public.blocks (key, data) VALUES ($1, $2)", testMhKey, testValue)
err = batch.Put(testEthKey, testValue) err = batch.Put(testEthKey, testValue)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
err = batch.Put(testEthKey2, testValue2) err = batch.Put(testEthKey2, testValue2)

View File

@ -23,8 +23,6 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/vulcanize/pg-ipfs-ethdb"
) )
var errNotSupported = errors.New("this operation is not supported") var errNotSupported = errors.New("this operation is not supported")
@ -59,7 +57,7 @@ func NewDatabase(db *sqlx.DB) ethdb.Database {
// Has satisfies the ethdb.KeyValueReader interface // Has satisfies the ethdb.KeyValueReader interface
// Has retrieves if a key is present in the key-value data store // Has retrieves if a key is present in the key-value data store
func (d *Database) Has(key []byte) (bool, error) { func (d *Database) Has(key []byte) (bool, error) {
mhKey, err := ipfsethdb.MultihashKeyFromKeccak256(key) mhKey, err := MultihashKeyFromKeccak256(key)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -70,7 +68,7 @@ func (d *Database) Has(key []byte) (bool, error) {
// Get satisfies the ethdb.KeyValueReader interface // Get satisfies the ethdb.KeyValueReader interface
// Get retrieves the given key if it's present in the key-value data store // Get retrieves the given key if it's present in the key-value data store
func (d *Database) Get(key []byte) ([]byte, error) { func (d *Database) Get(key []byte) ([]byte, error) {
mhKey, err := ipfsethdb.MultihashKeyFromKeccak256(key) mhKey, err := MultihashKeyFromKeccak256(key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -82,7 +80,7 @@ func (d *Database) Get(key []byte) ([]byte, error) {
// Put inserts the given value into the key-value data store // Put inserts the given value into the key-value data store
// Key is expected to be the keccak256 hash of value // Key is expected to be the keccak256 hash of value
func (d *Database) Put(key []byte, value []byte) error { func (d *Database) Put(key []byte, value []byte) error {
mhKey, err := ipfsethdb.MultihashKeyFromKeccak256(key) mhKey, err := MultihashKeyFromKeccak256(key)
if err != nil { if err != nil {
return err return err
} }
@ -93,7 +91,7 @@ func (d *Database) Put(key []byte, value []byte) error {
// Delete satisfies the ethdb.KeyValueWriter interface // Delete satisfies the ethdb.KeyValueWriter interface
// Delete removes the key from the key-value data store // Delete removes the key from the key-value data store
func (d *Database) Delete(key []byte) error { func (d *Database) Delete(key []byte) error {
mhKey, err := ipfsethdb.MultihashKeyFromKeccak256(key) mhKey, err := MultihashKeyFromKeccak256(key)
if err != nil { if err != nil {
return err return err
} }

View File

@ -26,8 +26,7 @@ import (
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/vulcanize/pg-ipfs-ethdb" pgipfsethdb "github.com/vulcanize/ipfs-ethdb/postgres"
"github.com/vulcanize/pg-ipfs-ethdb/postgres"
) )
var ( var (
@ -37,17 +36,17 @@ var (
testHeader = types.Header{Number: big.NewInt(1337)} testHeader = types.Header{Number: big.NewInt(1337)}
testValue, _ = rlp.EncodeToBytes(testHeader) testValue, _ = rlp.EncodeToBytes(testHeader)
testEthKey = testHeader.Hash().Bytes() testEthKey = testHeader.Hash().Bytes()
testMhKey, _ = ipfsethdb.MultihashKeyFromKeccak256(testEthKey) testMhKey, _ = pgipfsethdb.MultihashKeyFromKeccak256(testEthKey)
) )
var _ = Describe("Database", func() { var _ = Describe("Database", func() {
BeforeEach(func() { BeforeEach(func() {
db, err = ipfsethdb.TestDB() db, err = pgipfsethdb.TestDB()
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
database = pgipfsethdb.NewDatabase(db) database = pgipfsethdb.NewDatabase(db)
}) })
AfterEach(func() { AfterEach(func() {
err = ipfsethdb.ResetTestDB(db) err = pgipfsethdb.ResetTestDB(db)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
}) })

View File

@ -19,8 +19,6 @@ package pgipfsethdb
import ( import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/vulcanize/pg-ipfs-ethdb"
) )
// Iterator is the type that satisfies the ethdb.Iterator interface for PG-IPFS Ethereum data using a direct Postgres connection // Iterator is the type that satisfies the ethdb.Iterator interface for PG-IPFS Ethereum data using a direct Postgres connection
@ -72,7 +70,7 @@ func (i *Iterator) Key() []byte {
// The caller should not modify the contents of the returned slice // The caller should not modify the contents of the returned slice
// and its contents may change on the next call to Next // and its contents may change on the next call to Next
func (i *Iterator) Value() []byte { func (i *Iterator) Value() []byte {
mhKey, err := ipfsethdb.MultihashKeyFromKeccak256(i.currentKey) mhKey, err := MultihashKeyFromKeccak256(i.currentKey)
if err != nil { if err != nil {
i.err = err i.err = err
return nil return nil

49
postgres/util.go Normal file
View File

@ -0,0 +1,49 @@
// VulcanizeDB
// Copyright © 2019 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package pgipfsethdb
import (
"github.com/ipfs/go-ipfs-blockstore"
"github.com/ipfs/go-ipfs-ds-help"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq" //postgres driver
"github.com/multiformats/go-multihash"
)
// MultihashKeyFromKeccak256 converts keccak256 hash bytes into a blockstore-prefixed multihash db key string
func MultihashKeyFromKeccak256(h []byte) (string, error) {
mh, err := multihash.Encode(h, multihash.KECCAK_256)
if err != nil {
return "", err
}
dbKey := dshelp.MultihashToDsKey(mh)
return blockstore.BlockPrefix.String() + dbKey.String(), nil
}
// TestDB connect to the testing database
// it assumes the database has the IPFS public.blocks table present
// DO NOT use a production db for the test db, as it will remove all contents of the public.blocks table
func TestDB() (*sqlx.DB, error) {
connectStr := "postgresql://localhost:5432/vulcanize_testing?sslmode=disable"
return sqlx.Connect("postgres", connectStr)
}
// ResetTestDB drops all rows in the test db public.blocks table
func ResetTestDB(db *sqlx.DB) error {
_, err := db.Exec("TRUNCATE public.blocks")
return err
}

29
suite_test.go Normal file
View File

@ -0,0 +1,29 @@
// VulcanizeDB
// Copyright © 2020 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package ipfsethdb
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestIPFSETHDB(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "IPFS ethdb test")
}

36
util.go
View File

@ -19,49 +19,23 @@ package ipfsethdb
import ( import (
"github.com/ipfs/go-block-format" "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/ipfs/go-ipfs-blockstore"
"github.com/ipfs/go-ipfs-ds-help"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq" //postgres driver _ "github.com/lib/pq" //postgres driver
"github.com/multiformats/go-multihash" "github.com/multiformats/go-multihash"
) )
// MultihashKeyFromKeccak256 converts keccak256 hash bytes into a blockstore-prefixed multihash db key string // Keccak256ToCid takes a keccak256 hash and returns its cid v1 using the provided codec.
func MultihashKeyFromKeccak256(h []byte) (string, error) { func Keccak256ToCid(h []byte, codec uint64) (cid.Cid, error) {
mh, err := multihash.Encode(h, multihash.KECCAK_256)
if err != nil {
return "", err
}
dbKey := dshelp.MultihashToDsKey(mh)
return blockstore.BlockPrefix.String() + dbKey.String(), nil
}
// TestDB connect to the testing database
// it assumes the database has the IPFS public.blocks table present
// DO NOT use a production db for the test db, as it will remove all contents of the public.blocks table
func TestDB() (*sqlx.DB, error) {
connectStr := "postgresql://localhost:5432/vulcanize_testing?sslmode=disable"
return sqlx.Connect("postgres", connectStr)
}
// ResetTestDB drops all rows in the test db public.blocks table
func ResetTestDB(db *sqlx.DB) error {
_, err := db.Exec("TRUNCATE public.blocks")
return err
}
// Keccak256ToCid takes a keccak256 hash and returns its cid v0 using the provided codec.
func Keccak256ToCid(h []byte) (cid.Cid, error) {
buf, err := multihash.Encode(h, multihash.KECCAK_256) buf, err := multihash.Encode(h, multihash.KECCAK_256)
if err != nil { if err != nil {
return cid.Cid{}, err return cid.Cid{}, err
} }
return cid.NewCidV0(multihash.Multihash(buf)), nil return cid.NewCidV1(codec ,multihash.Multihash(buf)), nil
} }
// NewBlock takes a keccak256 hash key and the rlp []byte value it was derived from and creates an ipfs block object // NewBlock takes a keccak256 hash key and the rlp []byte value it was derived from and creates an ipfs block object
func NewBlock(key, value []byte) (blocks.Block, error) { func NewBlock(key, value []byte) (blocks.Block, error) {
c, err := Keccak256ToCid(key) // we are using cidv0 because we don't know the codec and codec doesn't matter, the datastore key is multihash-only derived // we are using state codec because we don't know the codec and at this level the codec doesn't matter, the datastore key is multihash-only derived
c, err := Keccak256ToCid(key, cid.EthStateTrie)
if err != nil { if err != nil {
return nil, err return nil, err
} }