migration file to create bite; create bite repository
add transaction index and raw log to bite table work on converter for bite event update bite repository, replace guy with 32byte lad; create bite converter to entity update field type for bite event; start on bite transformer finish bite event transformer
This commit is contained in:
parent
a179a4f7df
commit
985fa49178
@ -27,6 +27,10 @@ Vulcanize DB is a set of tools that make it easier for developers to write appli
|
|||||||
|
|
||||||
* See below for configuring additional environments
|
* See below for configuring additional environments
|
||||||
|
|
||||||
|
## Create a migration file (up and down)
|
||||||
|
1. install migrate library: https://github.com/golang-migrate/migrate
|
||||||
|
1. `migrate create -ext sql -seq -dir db/migrations/ -digits 10 create_bite_table`
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
- To use a local Ethereum node, copy `environments/public.toml.example` to
|
- To use a local Ethereum node, copy `environments/public.toml.example` to
|
||||||
`environments/public.toml` and update the `ipcPath` and `levelDbPath`.
|
`environments/public.toml` and update the `ipcPath` and `levelDbPath`.
|
||||||
|
1
db/migrations/1534295713_create_bite_table.down.sql
Normal file
1
db/migrations/1534295713_create_bite_table.down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE maker.bite;
|
13
db/migrations/1534295713_create_bite_table.up.sql
Normal file
13
db/migrations/1534295713_create_bite_table.up.sql
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
CREATE TABLE maker.bite (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
header_id INTEGER NOT NULL REFERENCES headers (id) ON DELETE CASCADE,
|
||||||
|
ilk bytea,
|
||||||
|
lad bytea,
|
||||||
|
ink VARCHAR,
|
||||||
|
art VARCHAR,
|
||||||
|
iArt VARCHAR,
|
||||||
|
tab NUMERIC,
|
||||||
|
flip VARCHAR,
|
||||||
|
tx_idx INTEGER NOT NUll,
|
||||||
|
raw_log JSONB
|
||||||
|
)
|
@ -40,6 +40,45 @@ SET default_tablespace = '';
|
|||||||
|
|
||||||
SET default_with_oids = false;
|
SET default_with_oids = false;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: bite; Type: TABLE; Schema: maker; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE maker.bite (
|
||||||
|
id integer NOT NULL,
|
||||||
|
header_id integer NOT NULL,
|
||||||
|
ilk bytea,
|
||||||
|
lad bytea,
|
||||||
|
ink character varying,
|
||||||
|
art character varying,
|
||||||
|
iart character varying,
|
||||||
|
tab numeric,
|
||||||
|
flip character varying,
|
||||||
|
tx_idx integer NOT NULL,
|
||||||
|
raw_log jsonb
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: bite_id_seq; Type: SEQUENCE; Schema: maker; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE maker.bite_id_seq
|
||||||
|
AS integer
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: bite_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE maker.bite_id_seq OWNED BY maker.bite.id;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: flip_kick; Type: TABLE; Schema: maker; Owner: -
|
-- Name: flip_kick; Type: TABLE; Schema: maker; Owner: -
|
||||||
--
|
--
|
||||||
@ -564,6 +603,13 @@ CREATE VIEW public.watched_event_logs AS
|
|||||||
WHERE ((((log_filters.topic0)::text = (logs.topic0)::text) OR (log_filters.topic0 IS NULL)) AND (((log_filters.topic1)::text = (logs.topic1)::text) OR (log_filters.topic1 IS NULL)) AND (((log_filters.topic2)::text = (logs.topic2)::text) OR (log_filters.topic2 IS NULL)) AND (((log_filters.topic3)::text = (logs.topic3)::text) OR (log_filters.topic3 IS NULL)));
|
WHERE ((((log_filters.topic0)::text = (logs.topic0)::text) OR (log_filters.topic0 IS NULL)) AND (((log_filters.topic1)::text = (logs.topic1)::text) OR (log_filters.topic1 IS NULL)) AND (((log_filters.topic2)::text = (logs.topic2)::text) OR (log_filters.topic2 IS NULL)) AND (((log_filters.topic3)::text = (logs.topic3)::text) OR (log_filters.topic3 IS NULL)));
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: bite id; Type: DEFAULT; Schema: maker; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY maker.bite ALTER COLUMN id SET DEFAULT nextval('maker.bite_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: flip_kick db_id; Type: DEFAULT; Schema: maker; Owner: -
|
-- Name: flip_kick db_id; Type: DEFAULT; Schema: maker; Owner: -
|
||||||
--
|
--
|
||||||
@ -655,6 +701,14 @@ ALTER TABLE ONLY public.transactions ALTER COLUMN id SET DEFAULT nextval('public
|
|||||||
ALTER TABLE ONLY public.watched_contracts ALTER COLUMN contract_id SET DEFAULT nextval('public.watched_contracts_contract_id_seq'::regclass);
|
ALTER TABLE ONLY public.watched_contracts ALTER COLUMN contract_id SET DEFAULT nextval('public.watched_contracts_contract_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: bite bite_pkey; Type: CONSTRAINT; Schema: maker; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY maker.bite
|
||||||
|
ADD CONSTRAINT bite_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: flip_kick flip_kick_id_key; Type: CONSTRAINT; Schema: maker; Owner: -
|
-- Name: flip_kick flip_kick_id_key; Type: CONSTRAINT; Schema: maker; Owner: -
|
||||||
--
|
--
|
||||||
@ -850,6 +904,14 @@ CREATE INDEX tx_from_index ON public.transactions USING btree (tx_from);
|
|||||||
CREATE INDEX tx_to_index ON public.transactions USING btree (tx_to);
|
CREATE INDEX tx_to_index ON public.transactions USING btree (tx_to);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: bite bite_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY maker.bite
|
||||||
|
ADD CONSTRAINT bite_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: flip_kick flip_kick_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: -
|
-- Name: flip_kick flip_kick_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: -
|
||||||
--
|
--
|
||||||
|
27
pkg/transformers/bite/bite_suite_test.go
Normal file
27
pkg/transformers/bite/bite_suite_test.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2018 Vulcanize
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package bite_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBite(t *testing.T) {
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, "Bite Suite")
|
||||||
|
}
|
29
pkg/transformers/bite/config.go
Normal file
29
pkg/transformers/bite/config.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Vulcanize
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
var BiteConfig = shared.TransformerConfig{
|
||||||
|
ContractAddresses: "0xe0f0fa6982c59d8aa4ae0134bfe048327bd788cacf758b643ca41f055ffce76c", //this is a temporary address deployed locally
|
||||||
|
ContractAbi: BiteABI,
|
||||||
|
Topics: []string{BiteSignature},
|
||||||
|
StartingBlockNumber: 0,
|
||||||
|
EndingBlockNumber: 100,
|
||||||
|
}
|
22
pkg/transformers/bite/constants.go
Normal file
22
pkg/transformers/bite/constants.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Vulcanize
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bite
|
||||||
|
|
||||||
|
var (
|
||||||
|
BiteABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vow\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"flips\",\"outputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"lad\",\"type\":\"bytes32\"},{\"name\":\"ink\",\"type\":\"uint256\"},{\"name\":\"tab\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"nflip\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"ilks\",\"outputs\":[{\"name\":\"flip\",\"type\":\"address\"},{\"name\":\"chop\",\"type\":\"uint256\"},{\"name\":\"lump\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"pit\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"vat_\",\"type\":\"address\"},{\"name\":\"pit_\",\"type\":\"address\"},{\"name\":\"vow_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"lad\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"ink\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"art\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"tab\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"flip\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"iArt\",\"type\":\"uint256\"}],\"name\":\"Bite\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"what\",\"type\":\"bytes32\"},{\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"what\",\"type\":\"bytes32\"},{\"name\":\"flip\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"lad\",\"type\":\"bytes32\"}],\"name\":\"bite\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"n\",\"type\":\"uint256\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"flip\",\"outputs\":[{\"name\":\"id\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
|
||||||
|
BiteSignature = "0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"
|
||||||
|
)
|
83
pkg/transformers/bite/converter.go
Normal file
83
pkg/transformers/bite/converter.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Vulcanize
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/geth"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/utilities"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Converter interface {
|
||||||
|
ToEntity(contractAddress string, contractAbi string, ethLog types.Log) (BiteEntity, error)
|
||||||
|
ToModel(flipKick BiteEntity) (BiteModel, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BiteConverter struct{}
|
||||||
|
|
||||||
|
func (BiteConverter) ToEntity(contractAddress string, contractAbi string, ethLog types.Log) (BiteEntity, error) {
|
||||||
|
entity := BiteEntity{}
|
||||||
|
address := common.HexToAddress(contractAddress)
|
||||||
|
abi, err := geth.ParseAbi(contractAbi)
|
||||||
|
if err != nil {
|
||||||
|
return entity, err
|
||||||
|
}
|
||||||
|
|
||||||
|
contract := bind.NewBoundContract(address, abi, nil, nil, nil)
|
||||||
|
|
||||||
|
err = contract.UnpackLog(&entity, "Bite", ethLog)
|
||||||
|
if err != nil {
|
||||||
|
return entity, err
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.Raw = ethLog
|
||||||
|
|
||||||
|
return entity, nil
|
||||||
|
}
|
||||||
|
func (converter BiteConverter) ToModel(entity BiteEntity) (BiteModel, error) {
|
||||||
|
|
||||||
|
id := entity.Id.String()
|
||||||
|
ilk := entity.Ilk[:]
|
||||||
|
lad := entity.Lad[:]
|
||||||
|
ink := entity.Ink.String()
|
||||||
|
art := entity.Art.String()
|
||||||
|
iArt := entity.IArt.String()
|
||||||
|
tab := entity.Tab.String()
|
||||||
|
flip := entity.Flip.String()
|
||||||
|
txIdx := entity.TransactionIndex
|
||||||
|
rawLogJson, err := json.Marshal(entity.Raw)
|
||||||
|
rawLogString := string(rawLogJson)
|
||||||
|
if err != nil {
|
||||||
|
return BiteModel{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return BiteModel{
|
||||||
|
Id: utilities.ConvertNilToEmptyString(id),
|
||||||
|
Ilk: ilk,
|
||||||
|
Lad: lad,
|
||||||
|
Ink: utilities.ConvertNilToEmptyString(ink),
|
||||||
|
Art: utilities.ConvertNilToEmptyString(art),
|
||||||
|
IArt: utilities.ConvertNilToEmptyString(iArt),
|
||||||
|
Tab: utilities.ConvertNilToEmptyString(tab),
|
||||||
|
Flip: utilities.ConvertNilToEmptyString(flip),
|
||||||
|
TransactionIndex: txIdx,
|
||||||
|
Raw: rawLogString,
|
||||||
|
}, nil
|
||||||
|
}
|
97
pkg/transformers/bite/converter_test.go
Normal file
97
pkg/transformers/bite/converter_test.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Vulcanize
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bite_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/bite"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Bite Converter", func() {
|
||||||
|
var converter = bite.BiteConverter{}
|
||||||
|
|
||||||
|
Describe("ToEntity", func() {
|
||||||
|
It("converts an eth log to a bite entity", func() {
|
||||||
|
entity, err := converter.ToEntity(test_data.TemporaryBiteAddress, bite.BiteABI, test_data.EthBiteLog)
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(entity.Ilk).To(Equal(test_data.BiteEntity.Ilk))
|
||||||
|
Expect(entity.Lad).To(Equal(test_data.BiteEntity.Lad))
|
||||||
|
Expect(entity.Ink).To(Equal(test_data.BiteEntity.Ink))
|
||||||
|
Expect(entity.Art).To(Equal(test_data.BiteEntity.Art))
|
||||||
|
Expect(entity.Tab).To(Equal(test_data.BiteEntity.Tab))
|
||||||
|
Expect(entity.Flip).To(Equal(test_data.BiteEntity.Flip))
|
||||||
|
Expect(entity.IArt).To(Equal(test_data.BiteEntity.IArt))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns an error if converting log to entity fails", func() {
|
||||||
|
_, err := converter.ToEntity(test_data.TemporaryBiteAddress, "error abi", test_data.EthBiteLog)
|
||||||
|
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Describe("ToModel", func() {
|
||||||
|
var emptyEntity = bite.BiteEntity{}
|
||||||
|
var emptyRawLog string
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
emptyEntity.Id = big.NewInt(1)
|
||||||
|
var emptyRawLogJson, err = json.Marshal(types.Log{})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
emptyRawLogJson, err = json.Marshal(types.Log{})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
emptyRawLog = string(emptyRawLogJson)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("converts an Entity to a Model", func() {
|
||||||
|
model, err := converter.ToModel(test_data.BiteEntity)
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(model).To(Equal(test_data.BiteModel))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("handles nil values", func() {
|
||||||
|
emptyEntity.Id = big.NewInt(1)
|
||||||
|
emptyLog, err := json.Marshal(types.Log{})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
expectedModel := bite.BiteModel{
|
||||||
|
Id: "1",
|
||||||
|
Ilk: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Lad: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Ink: "",
|
||||||
|
Art: "",
|
||||||
|
IArt: "",
|
||||||
|
Tab: "",
|
||||||
|
Flip: "",
|
||||||
|
TransactionIndex: 0,
|
||||||
|
Raw: string(emptyLog),
|
||||||
|
}
|
||||||
|
model, err := converter.ToModel(emptyEntity)
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(model).To(Equal(expectedModel))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
33
pkg/transformers/bite/entity.go
Normal file
33
pkg/transformers/bite/entity.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2018 Vulcanize
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package bite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BiteEntity struct {
|
||||||
|
Id *big.Int
|
||||||
|
Ilk [32]byte
|
||||||
|
Lad [32]byte
|
||||||
|
Ink *big.Int
|
||||||
|
Art *big.Int
|
||||||
|
Tab *big.Int
|
||||||
|
Flip *big.Int
|
||||||
|
IArt *big.Int
|
||||||
|
TransactionIndex uint
|
||||||
|
Raw types.Log
|
||||||
|
}
|
83
pkg/transformers/bite/integration_test.go
Normal file
83
pkg/transformers/bite/integration_test.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Vulcanize
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bite_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
|
||||||
|
"github.com/vulcanize/vulcanizedb/test_config"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/geth/client"
|
||||||
|
rpc2 "github.com/vulcanize/vulcanizedb/pkg/geth/converters/rpc"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/geth/node"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/geth"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/bite"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Integration tests", func() {
|
||||||
|
It("Fetches bite event logs from a local test chain", func() {
|
||||||
|
ipcPath := test_config.TestClient.IPCPath
|
||||||
|
|
||||||
|
rawRpcClient, err := rpc.Dial(ipcPath)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
rpcClient := client.NewRpcClient(rawRpcClient, ipcPath)
|
||||||
|
ethClient := ethclient.NewClient(rawRpcClient)
|
||||||
|
blockChainClient := client.NewEthClient(ethClient)
|
||||||
|
realNode := node.MakeNode(rpcClient)
|
||||||
|
transactionConverter := rpc2.NewRpcTransactionConverter(ethClient)
|
||||||
|
realBlockChain := geth.NewBlockChain(blockChainClient, realNode, transactionConverter)
|
||||||
|
realFetcher := shared.NewFetcher(realBlockChain)
|
||||||
|
topic0 := common.HexToHash(bite.BiteSignature)
|
||||||
|
topics := [][]common.Hash{{topic0}}
|
||||||
|
|
||||||
|
result, err := realFetcher.FetchLogs(test_data.TemporaryBiteAddress, topics, int64(26))
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(len(result) > 0).To(BeTrue())
|
||||||
|
Expect(result[0].Address).To(Equal(common.HexToAddress(test_data.TemporaryBiteAddress)))
|
||||||
|
Expect(result[0].TxHash).To(Equal(test_data.EthBiteLog.TxHash))
|
||||||
|
Expect(result[0].BlockNumber).To(Equal(test_data.EthBiteLog.BlockNumber))
|
||||||
|
Expect(result[0].Topics).To(Equal(test_data.EthBiteLog.Topics))
|
||||||
|
Expect(result[0].Index).To(Equal(test_data.EthBiteLog.Index))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("unpacks an event log", func() {
|
||||||
|
address := common.HexToAddress(test_data.TemporaryBiteAddress)
|
||||||
|
abi, err := geth.ParseAbi(bite.BiteABI)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
contract := bind.NewBoundContract(address, abi, nil, nil, nil)
|
||||||
|
entity := &bite.BiteEntity{}
|
||||||
|
|
||||||
|
var eventLog = test_data.EthBiteLog
|
||||||
|
|
||||||
|
err = contract.UnpackLog(entity, "Bite", eventLog)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
expectedEntity := test_data.BiteEntity
|
||||||
|
Expect(entity.Art).To(Equal(expectedEntity.Art))
|
||||||
|
Expect(entity.Ilk).To(Equal(expectedEntity.Ilk))
|
||||||
|
Expect(entity.Ink).To(Equal(expectedEntity.Ink))
|
||||||
|
})
|
||||||
|
})
|
28
pkg/transformers/bite/model.go
Normal file
28
pkg/transformers/bite/model.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2018 Vulcanize
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package bite
|
||||||
|
|
||||||
|
type BiteModel struct {
|
||||||
|
Id string
|
||||||
|
Ilk []byte
|
||||||
|
Lad []byte
|
||||||
|
Ink string
|
||||||
|
Art string
|
||||||
|
IArt string
|
||||||
|
Tab string
|
||||||
|
Flip string
|
||||||
|
TransactionIndex uint `db:"tx_idx"`
|
||||||
|
Raw string `db:"raw_log"`
|
||||||
|
}
|
49
pkg/transformers/bite/repository.go
Normal file
49
pkg/transformers/bite/repository.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package bite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Repository interface {
|
||||||
|
Create(headerID int64, model BiteModel) error
|
||||||
|
MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BiteRepository struct {
|
||||||
|
db *postgres.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBiteRepository(db *postgres.DB) Repository {
|
||||||
|
return BiteRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository BiteRepository) Create(headerID int64, model BiteModel) error {
|
||||||
|
_, err := repository.db.Exec(
|
||||||
|
`INSERT into maker.bite (header_id, id, ilk, lad, ink, art, iart, tab, flip, tx_idx, raw_log)
|
||||||
|
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
|
||||||
|
headerID, model.Id, model.Ilk, model.Lad, model.Ink, model.Art, model.IArt, model.Tab, model.Flip, model.TransactionIndex, model.Raw,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (repository BiteRepository) MissingHeaders(startingBlockNumber int64, endingBlockNumber int64) ([]core.Header, error) {
|
||||||
|
var result []core.Header
|
||||||
|
err := repository.db.Select(
|
||||||
|
&result,
|
||||||
|
`SELECT headers.id, headers.block_number FROM headers
|
||||||
|
LEFT JOIN maker.bite on headers.id = header_id
|
||||||
|
WHERE header_id ISNULL
|
||||||
|
AND headers.block_number >= $1
|
||||||
|
AND headers.block_number <= $2
|
||||||
|
AND headers.eth_node_fingerprint = $3`,
|
||||||
|
startingBlockNumber,
|
||||||
|
endingBlockNumber,
|
||||||
|
repository.db.Node.ID,
|
||||||
|
)
|
||||||
|
return result, err
|
||||||
|
}
|
122
pkg/transformers/bite/repository_test.go
Normal file
122
pkg/transformers/bite/repository_test.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// Copyright 2018 Vulcanize
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package bite_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"database/sql"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/bite"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
|
||||||
|
"github.com/vulcanize/vulcanizedb/test_config"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Bite repository", func() {
|
||||||
|
Describe("Create", func() {
|
||||||
|
It("persists a bite record", func() {
|
||||||
|
node := core.Node{}
|
||||||
|
db := test_config.NewTestDB(node)
|
||||||
|
test_config.CleanTestDB(db)
|
||||||
|
headerRepository := repositories.NewHeaderRepository(db)
|
||||||
|
headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
biteRepository := bite.NewBiteRepository(db)
|
||||||
|
|
||||||
|
err = biteRepository.Create(headerID, test_data.BiteModel)
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
var dbBite bite.BiteModel
|
||||||
|
err = db.Get(&dbBite, `SELECT id, ilk, lad, ink, art, tab, flip, tx_idx, raw_log FROM maker.bite WHERE header_id = $1`, headerID)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(dbBite.Id).To(Equal(test_data.BiteModel.Id))
|
||||||
|
Expect(dbBite.Ilk).To(Equal(test_data.BiteModel.Ilk))
|
||||||
|
Expect(dbBite.Lad).To(Equal(test_data.BiteModel.Lad))
|
||||||
|
Expect(dbBite.Art).To(Equal(test_data.BiteModel.Art))
|
||||||
|
Expect(dbBite.Tab).To(Equal(test_data.BiteModel.Tab))
|
||||||
|
Expect(dbBite.Flip).To(Equal(test_data.BiteModel.Flip))
|
||||||
|
Expect(dbBite.TransactionIndex).To(Equal(test_data.BiteModel.TransactionIndex))
|
||||||
|
Expect(dbBite.Raw).To(MatchJSON(test_data.BiteModel.Raw))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("does not duplicate bite events", func() {
|
||||||
|
node := core.Node{}
|
||||||
|
db := test_config.NewTestDB(node)
|
||||||
|
test_config.CleanTestDB(db)
|
||||||
|
headerRepository := repositories.NewHeaderRepository(db)
|
||||||
|
headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
biteRepository := bite.NewBiteRepository(db)
|
||||||
|
err = biteRepository.Create(headerID, test_data.BiteModel)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
err = biteRepository.Create(headerID, test_data.BiteModel)
|
||||||
|
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
Expect(err.Error()).To(ContainSubstring("pq: duplicate key value violates unique constraint"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("removes bite if corresponding header is deleted", func() {
|
||||||
|
node := core.Node{}
|
||||||
|
db := test_config.NewTestDB(node)
|
||||||
|
test_config.CleanTestDB(db)
|
||||||
|
headerRepository := repositories.NewHeaderRepository(db)
|
||||||
|
headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
biteRepository := bite.NewBiteRepository(db)
|
||||||
|
err = biteRepository.Create(headerID, test_data.BiteModel)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
_, err = db.Exec(`DELETE FROM headers WHERE id = $1`, headerID)
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
var dbBite bite.BiteModel
|
||||||
|
err = db.Get(&dbBite, `SELECT id, ilk, lad, ink, art, tab, flip, tx_idx, raw_log FROM maker.bite WHERE header_id = $1`, headerID)
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
Expect(err).To(MatchError(sql.ErrNoRows))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Describe("MissingHeaders", func() {
|
||||||
|
It("returns headers with no associated bite event", func() {
|
||||||
|
node := core.Node{}
|
||||||
|
db := test_config.NewTestDB(node)
|
||||||
|
test_config.CleanTestDB(db)
|
||||||
|
headerRepository := repositories.NewHeaderRepository(db)
|
||||||
|
startingBlockNumber := int64(1)
|
||||||
|
biteBlockNumber := int64(2)
|
||||||
|
endingBlockNumber := int64(3)
|
||||||
|
blockNumbers := []int64{startingBlockNumber, biteBlockNumber, endingBlockNumber, endingBlockNumber + 1}
|
||||||
|
var headerIDs []int64
|
||||||
|
for _, n := range blockNumbers {
|
||||||
|
headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: n})
|
||||||
|
headerIDs = append(headerIDs, headerID)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
biteRepository := bite.NewBiteRepository(db)
|
||||||
|
err := biteRepository.Create(headerIDs[1], test_data.BiteModel)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
headers, err := biteRepository.MissingHeaders(startingBlockNumber, endingBlockNumber)
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(len(headers)).To(Equal(2))
|
||||||
|
Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber)))
|
||||||
|
Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
88
pkg/transformers/bite/transformer.go
Normal file
88
pkg/transformers/bite/transformer.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Vulcanize
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BiteTransformer struct {
|
||||||
|
Repository Repository
|
||||||
|
Fetcher shared.LogFetcher
|
||||||
|
Converter Converter
|
||||||
|
Config shared.TransformerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type BiteTransformerInitializer struct {
|
||||||
|
Config shared.TransformerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i BiteTransformerInitializer) NewBiteTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer {
|
||||||
|
fetcher := shared.NewFetcher(blockChain)
|
||||||
|
repository := NewBiteRepository(db)
|
||||||
|
transformer := BiteTransformer{
|
||||||
|
Fetcher: fetcher,
|
||||||
|
Repository: repository,
|
||||||
|
Converter: BiteConverter{},
|
||||||
|
Config: i.Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BiteTransformer) Execute() error {
|
||||||
|
config := b.Config
|
||||||
|
topics := [][]common.Hash{{common.HexToHash(shared.BiteSignature)}}
|
||||||
|
|
||||||
|
missingHeaders, err := b.Repository.MissingHeaders(config.StartingBlockNumber, config.EndingBlockNumber)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error fetching missing headers:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, header := range missingHeaders {
|
||||||
|
ethLogs, err := b.Fetcher.FetchLogs(config.ContractAddresses, topics, header.BlockNumber)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error fetching matching logs:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ethLog := range ethLogs {
|
||||||
|
entity, err := b.Converter.ToEntity(config.ContractAddresses, config.ContractAbi, ethLog)
|
||||||
|
model, err := b.Converter.ToModel(entity)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error converting logs:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = b.Repository.Create(header.Id, model)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error persisting bite record:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (b BiteTransformer) SetConfig(config shared.TransformerConfig) {
|
||||||
|
b.Config = config
|
||||||
|
}
|
144
pkg/transformers/bite/transfromer_test.go
Normal file
144
pkg/transformers/bite/transfromer_test.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Vulcanize
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bite_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/bite"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks"
|
||||||
|
bite_mocks "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/bite"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Bite Transformer", func() {
|
||||||
|
var repository bite_mocks.MockBiteRepository
|
||||||
|
var fetcher mocks.MockLogFetcher
|
||||||
|
var converter bite_mocks.MockBiteConverter
|
||||||
|
var transformer bite.BiteTransformer
|
||||||
|
var blockNumber1 = rand.Int63()
|
||||||
|
var blockNumber2 = rand.Int63()
|
||||||
|
var testConfig shared.TransformerConfig
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
repository = bite_mocks.MockBiteRepository{}
|
||||||
|
fetcher = mocks.MockLogFetcher{}
|
||||||
|
converter = bite_mocks.MockBiteConverter{}
|
||||||
|
|
||||||
|
transformer = bite.BiteTransformer{
|
||||||
|
Repository: &repository,
|
||||||
|
Fetcher: &fetcher,
|
||||||
|
Converter: &converter,
|
||||||
|
Config: bite.BiteConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
testConfig = shared.TransformerConfig{
|
||||||
|
ContractAddresses: "0x12345",
|
||||||
|
ContractAbi: "test abi",
|
||||||
|
Topics: []string{shared.BiteSignature},
|
||||||
|
StartingBlockNumber: blockNumber1,
|
||||||
|
EndingBlockNumber: blockNumber2,
|
||||||
|
}
|
||||||
|
transformer.SetConfig(testConfig)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("gets missing headers for blocks in the configured range", func() {
|
||||||
|
err := transformer.Execute()
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(repository.PassedStartingBlockNumber).To(Equal(bite.BiteConfig.StartingBlockNumber))
|
||||||
|
Expect(repository.PassedEndingBlockNumber).To(Equal(bite.BiteConfig.EndingBlockNumber))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns an error if it fails to get missing headers", func() {
|
||||||
|
repository.SetMissingHeadersErr(fakes.FakeError)
|
||||||
|
err := transformer.Execute()
|
||||||
|
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("fetches eth logs for each missing header", func() {
|
||||||
|
repository.SetMissingHeaders([]core.Header{{BlockNumber: blockNumber1}, {BlockNumber: blockNumber2}})
|
||||||
|
expectedTopics := [][]common.Hash{{common.HexToHash(shared.BiteSignature)}}
|
||||||
|
err := transformer.Execute()
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(fetcher.FetchedBlocks).To(Equal([]int64{blockNumber1, blockNumber2}))
|
||||||
|
Expect(fetcher.FetchedTopics).To(Equal(expectedTopics))
|
||||||
|
Expect(fetcher.FetchedContractAddress).To(Equal(bite.BiteConfig.ContractAddresses))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns an error if fetching logs fails", func() {
|
||||||
|
repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}})
|
||||||
|
fetcher.SetFetcherError(fakes.FakeError)
|
||||||
|
err := transformer.Execute()
|
||||||
|
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("converts an eth log to an Entity", func() {
|
||||||
|
repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}})
|
||||||
|
fetcher.SetFetchedLogs([]types.Log{test_data.EthBiteLog})
|
||||||
|
err := transformer.Execute()
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(converter.ConverterContract).To(Equal(bite.BiteConfig.ContractAddresses))
|
||||||
|
Expect(converter.ConverterAbi).To(Equal(bite.BiteConfig.ContractAbi))
|
||||||
|
Expect(converter.LogsToConvert).To(Equal([]types.Log{test_data.EthBiteLog}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns an error if converter fails", func() {
|
||||||
|
headerId := int64(1)
|
||||||
|
repository.SetMissingHeaders([]core.Header{{BlockNumber: blockNumber1, Id: headerId}})
|
||||||
|
fetcher.SetFetchedLogs([]types.Log{test_data.EthBiteLog})
|
||||||
|
converter.SetConverterError(fakes.FakeError)
|
||||||
|
|
||||||
|
err := transformer.Execute()
|
||||||
|
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("persists the bite record", func() {
|
||||||
|
headerId := int64(1)
|
||||||
|
repository.SetMissingHeaders([]core.Header{{BlockNumber: blockNumber1, Id: headerId}})
|
||||||
|
fetcher.SetFetchedLogs([]types.Log{test_data.EthBiteLog})
|
||||||
|
|
||||||
|
err := transformer.Execute()
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(repository.PassedHeaderID).To(Equal(headerId))
|
||||||
|
Expect(repository.PassedBiteModel).To(Equal(test_data.BiteModel))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns error if persisting bite record fails", func() {
|
||||||
|
repository.SetMissingHeaders([]core.Header{{BlockNumber: blockNumber1}})
|
||||||
|
fetcher.SetFetchedLogs([]types.Log{test_data.EthBiteLog})
|
||||||
|
repository.SetCreateError(fakes.FakeError)
|
||||||
|
err := transformer.Execute()
|
||||||
|
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
|
})
|
||||||
|
})
|
File diff suppressed because one or more lines are too long
74
pkg/transformers/test_data/bite.go
Normal file
74
pkg/transformers/test_data/bite.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package test_data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/bite"
|
||||||
|
"math/big"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
TemporaryBiteAddress = "0x4ac9588a53dc6008058c86eed71a5c91da793a07"
|
||||||
|
TemporaryBiteBlockHash = common.HexToHash("0xd130caaccc9203ca63eb149faeb013aed21f0317ce23489c0486da2f9adcd0eb")
|
||||||
|
TemporaryBiteBlockNumber = int64(26)
|
||||||
|
TemporaryBiteData = "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005"
|
||||||
|
TemporaryBiteTransaction = "0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
biteInk = big.NewInt(1)
|
||||||
|
biteArt = big.NewInt(2)
|
||||||
|
biteTab = big.NewInt(3)
|
||||||
|
biteFlip = big.NewInt(4)
|
||||||
|
biteIArt = big.NewInt(5)
|
||||||
|
biteRawJson, _ = json.Marshal(EthBiteLog)
|
||||||
|
biteRawString = string(biteRawJson)
|
||||||
|
biteIlk = [32]byte{102, 97, 107, 101, 32, 105, 108, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
biteLad = [32]byte{102, 97, 107, 101, 32, 108, 97, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
biteId = int64(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
var EthBiteLog = types.Log{
|
||||||
|
Address: common.HexToAddress(TemporaryBiteAddress),
|
||||||
|
Topics: []common.Hash{
|
||||||
|
common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
|
||||||
|
common.HexToHash("0x66616b6520696c6b000000000000000000000000000000000000000000000000"),
|
||||||
|
common.HexToHash("0x66616b65206c6164000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
Data: hexutil.MustDecode(TemporaryBiteData),
|
||||||
|
BlockNumber: uint64(TemporaryBiteBlockNumber),
|
||||||
|
TxHash: common.HexToHash(TemporaryBiteTransaction),
|
||||||
|
TxIndex: 111,
|
||||||
|
BlockHash: TemporaryBiteBlockHash,
|
||||||
|
Index: 0,
|
||||||
|
Removed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
var BiteEntity = bite.BiteEntity{
|
||||||
|
Id: big.NewInt(biteId),
|
||||||
|
Ilk: biteIlk,
|
||||||
|
Lad: biteLad,
|
||||||
|
Ink: biteInk,
|
||||||
|
Art: biteArt,
|
||||||
|
Tab: biteTab,
|
||||||
|
Flip: biteFlip,
|
||||||
|
IArt: biteIArt,
|
||||||
|
TransactionIndex: EthBiteLog.TxIndex,
|
||||||
|
Raw: EthBiteLog,
|
||||||
|
}
|
||||||
|
|
||||||
|
var BiteModel = bite.BiteModel{
|
||||||
|
Id: strconv.FormatInt(biteId, 10),
|
||||||
|
Ilk: biteIlk[:],
|
||||||
|
Lad: biteLad[:],
|
||||||
|
Ink: biteInk.String(),
|
||||||
|
Art: biteArt.String(),
|
||||||
|
Tab: biteTab.String(),
|
||||||
|
Flip: biteFlip.String(),
|
||||||
|
IArt: biteIArt.String(),
|
||||||
|
TransactionIndex: EthBiteLog.TxIndex,
|
||||||
|
Raw: biteRawString,
|
||||||
|
}
|
44
pkg/transformers/test_data/mocks/bite/converter.go
Normal file
44
pkg/transformers/test_data/mocks/bite/converter.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2018 Vulcanize
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package bite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
. "github.com/vulcanize/vulcanizedb/pkg/transformers/bite"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockBiteConverter struct {
|
||||||
|
ConverterContract string
|
||||||
|
ConverterAbi string
|
||||||
|
LogsToConvert []types.Log
|
||||||
|
EntitiesToConvert []BiteEntity
|
||||||
|
ConverterError error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mbc *MockBiteConverter) ToEntity(contractAddress string, contractAbi string, ethLog types.Log) (BiteEntity, error) {
|
||||||
|
mbc.ConverterContract = contractAddress
|
||||||
|
mbc.ConverterAbi = contractAbi
|
||||||
|
mbc.LogsToConvert = append(mbc.LogsToConvert, ethLog)
|
||||||
|
return test_data.BiteEntity, mbc.ConverterError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mbc *MockBiteConverter) ToModel(entity BiteEntity) (BiteModel, error) {
|
||||||
|
mbc.EntitiesToConvert = append(mbc.EntitiesToConvert, entity)
|
||||||
|
return test_data.BiteModel, mbc.ConverterError
|
||||||
|
}
|
||||||
|
func (mbc *MockBiteConverter) SetConverterError(err error) {
|
||||||
|
mbc.ConverterError = err
|
||||||
|
}
|
55
pkg/transformers/test_data/mocks/bite/repository.go
Normal file
55
pkg/transformers/test_data/mocks/bite/repository.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2018 Vulcanize
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package bite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/transformers/bite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockBiteRepository struct {
|
||||||
|
createError error
|
||||||
|
PassedEndingBlockNumber int64
|
||||||
|
PassedBiteModel bite.BiteModel
|
||||||
|
PassedHeaderID int64
|
||||||
|
PassedStartingBlockNumber int64
|
||||||
|
PassedTransactionIndex uint
|
||||||
|
missingHeaders []core.Header
|
||||||
|
missingHeadersErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository *MockBiteRepository) SetCreateError(err error) {
|
||||||
|
repository.createError = err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository *MockBiteRepository) SetMissingHeadersErr(err error) {
|
||||||
|
repository.missingHeadersErr = err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository *MockBiteRepository) SetMissingHeaders(headers []core.Header) {
|
||||||
|
repository.missingHeaders = headers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository *MockBiteRepository) Create(headerID int64, model bite.BiteModel) error {
|
||||||
|
repository.PassedHeaderID = headerID
|
||||||
|
repository.PassedBiteModel = model
|
||||||
|
return repository.createError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository *MockBiteRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) {
|
||||||
|
repository.PassedStartingBlockNumber = startingBlockNumber
|
||||||
|
repository.PassedEndingBlockNumber = endingBlockNumber
|
||||||
|
return repository.missingHeaders, repository.missingHeadersErr
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user