diff --git a/pkg/contract_summary/summary.go b/pkg/contract_summary/summary.go index c684a635..805ae375 100644 --- a/pkg/contract_summary/summary.go +++ b/pkg/contract_summary/summary.go @@ -11,13 +11,13 @@ import ( ) type ContractSummary struct { - Contract core.Contract ContractHash string NumberOfTransactions int LastTransaction *core.Transaction blockChain core.Blockchain Attributes core.ContractAttributes BlockNumber *big.Int + WatchedContract core.WatchedContract } var NewContractNotWatchedErr = func(contractHash string) error { @@ -35,20 +35,20 @@ func NewSummary(blockchain core.Blockchain, repository repositories.Repository, func (contractSummary ContractSummary) GetStateAttribute(attributeName string) interface{} { var result interface{} - result, _ = contractSummary.blockChain.GetAttribute(contractSummary.Contract, attributeName, contractSummary.BlockNumber) + result, _ = contractSummary.blockChain.GetAttribute(contractSummary.WatchedContract, attributeName, contractSummary.BlockNumber) return result } func newContractSummary(blockchain core.Blockchain, watchedContract core.WatchedContract, blockNumber *big.Int) ContractSummary { - contract, _ := blockchain.GetContract(watchedContract.Hash) + attributes, _ := blockchain.GetAttributes(watchedContract) return ContractSummary{ blockChain: blockchain, - Contract: contract, ContractHash: watchedContract.Hash, NumberOfTransactions: len(watchedContract.Transactions), LastTransaction: lastTransaction(watchedContract), - Attributes: contract.Attributes, + Attributes: attributes, BlockNumber: blockNumber, + WatchedContract: watchedContract, } } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 1e9d84dd..3d9cf53b 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -7,7 +7,6 @@ type Blockchain interface { SubscribeToBlocks(blocks chan Block) StartListening() StopListening() - GetContract(contractHash string) (Contract, error) - GetContractAttributes(contractHash string) (ContractAttributes, error) - GetAttribute(contract Contract, attributeName string, blockNumber *big.Int) (interface{}, error) + GetAttributes(watchedContract WatchedContract) (ContractAttributes, error) + GetAttribute(watchedContract WatchedContract, attributeName string, blockNumber *big.Int) (interface{}, error) } diff --git a/pkg/fakes/blockchain.go b/pkg/fakes/blockchain.go index aed35794..6a4403b4 100644 --- a/pkg/fakes/blockchain.go +++ b/pkg/fakes/blockchain.go @@ -15,21 +15,12 @@ type Blockchain struct { WasToldToStop bool } -func (blockchain *Blockchain) GetContract(contractHash string) (core.Contract, error) { - contractAttributes, err := blockchain.GetContractAttributes(contractHash) - contract := core.Contract{ - Attributes: contractAttributes, - Hash: contractHash, - } - return contract, err -} - -func (blockchain *Blockchain) GetAttribute(contract core.Contract, attributeName string, blockNumber *big.Int) (interface{}, error) { +func (blockchain *Blockchain) GetAttribute(watchedContract core.WatchedContract, attributeName string, blockNumber *big.Int) (interface{}, error) { var result interface{} if blockNumber == nil { - result = blockchain.contractAttributes[contract.Hash+"-1"][attributeName] + result = blockchain.contractAttributes[watchedContract.Hash+"-1"][attributeName] } else { - result = blockchain.contractAttributes[contract.Hash+blockNumber.String()][attributeName] + result = blockchain.contractAttributes[watchedContract.Hash+blockNumber.String()][attributeName] } return result, nil } @@ -84,9 +75,9 @@ func (blockchain *Blockchain) SetContractStateAttribute(contractHash string, blo blockchain.contractAttributes[key][attributeName] = attributeValue } -func (blockchain *Blockchain) GetContractAttributes(contractHash string) (core.ContractAttributes, error) { +func (blockchain *Blockchain) GetAttributes(watchedContract core.WatchedContract) (core.ContractAttributes, error) { var contractAttributes core.ContractAttributes - attributes, ok := blockchain.contractAttributes[contractHash+"-1"] + attributes, ok := blockchain.contractAttributes[watchedContract.Hash+"-1"] if ok { for key, _ := range attributes { contractAttributes = append(contractAttributes, core.ContractAttribute{Name: key, Type: "string"}) diff --git a/pkg/geth/abi.go b/pkg/geth/abi.go index 1da6a569..705f715e 100644 --- a/pkg/geth/abi.go +++ b/pkg/geth/abi.go @@ -18,6 +18,10 @@ func ParseAbiFile(abiFilePath string) (abi.ABI, error) { if err != nil { return abi.ABI{}, ErrMissingAbiFile } + return ParseAbi(abiString) +} + +func ParseAbi(abiString string) (abi.ABI, error) { parsedAbi, err := abi.JSON(strings.NewReader(abiString)) if err != nil { return abi.ABI{}, ErrInvalidAbiFile diff --git a/pkg/geth/contract.go b/pkg/geth/contract.go index a5932da7..339d7f6e 100644 --- a/pkg/geth/contract.go +++ b/pkg/geth/contract.go @@ -13,7 +13,6 @@ import ( "github.com/8thlight/vulcanizedb/pkg/config" "github.com/8thlight/vulcanizedb/pkg/core" "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" ) @@ -21,30 +20,8 @@ var ( ErrInvalidStateAttribute = errors.New("invalid state attribute") ) -func (blockchain *GethBlockchain) GetContract(contractHash string) (core.Contract, error) { - attributes, err := blockchain.GetContractAttributes(contractHash) - if err != nil { - return core.Contract{}, err - } else { - contract := core.Contract{ - Attributes: attributes, - Hash: contractHash, - } - return contract, nil - } -} - -func (blockchain *GethBlockchain) getParseAbi(contract core.Contract) (abi.ABI, error) { - abiFilePath := filepath.Join(config.ProjectRoot(), "contracts", "public", fmt.Sprintf("%s.json", contract.Hash)) - parsed, err := ParseAbiFile(abiFilePath) - if err != nil { - return abi.ABI{}, err - } - return parsed, nil -} - -func (blockchain *GethBlockchain) GetAttribute(contract core.Contract, attributeName string, blockNumber *big.Int) (interface{}, error) { - parsed, err := blockchain.getParseAbi(contract) +func (blockchain *GethBlockchain) GetAttribute(watchedContract core.WatchedContract, attributeName string, blockNumber *big.Int) (interface{}, error) { + parsed, err := ParseAbi(watchedContract.Abi) var result interface{} if err != nil { return result, err @@ -53,7 +30,7 @@ func (blockchain *GethBlockchain) GetAttribute(contract core.Contract, attribute if err != nil { return nil, ErrInvalidStateAttribute } - output, err := callContract(contract, input, err, blockchain, blockNumber) + output, err := callContract(watchedContract.Hash, input, blockchain, blockNumber) if err != nil { return nil, err } @@ -63,13 +40,27 @@ func (blockchain *GethBlockchain) GetAttribute(contract core.Contract, attribute } return result, nil } -func callContract(contract core.Contract, input []byte, err error, blockchain *GethBlockchain, blockNumber *big.Int) ([]byte, error) { - to := common.HexToAddress(contract.Hash) + +func callContract(contractHash string, input []byte, blockchain *GethBlockchain, blockNumber *big.Int) ([]byte, error) { + to := common.HexToAddress(contractHash) msg := ethereum.CallMsg{To: &to, Data: input} return blockchain.client.CallContract(context.Background(), msg, blockNumber) } -func (blockchain *GethBlockchain) GetContractAttributes(contractHash string) (core.ContractAttributes, error) { +func (blockchain *GethBlockchain) GetAttributes(watchedContract core.WatchedContract) (core.ContractAttributes, error) { + parsed, _ := ParseAbi(watchedContract.Abi) + var contractAttributes core.ContractAttributes + for _, abiElement := range parsed.Methods { + if (len(abiElement.Outputs) > 0) && (len(abiElement.Inputs) == 0) && abiElement.Const { + attributeType := abiElement.Outputs[0].Type.String() + contractAttributes = append(contractAttributes, core.ContractAttribute{abiElement.Name, attributeType}) + } + } + sort.Sort(contractAttributes) + return contractAttributes, nil +} + +func (blockchain *GethBlockchain) GetContractAttributesOld(contractHash string) (core.ContractAttributes, error) { abiFilePath := filepath.Join(config.ProjectRoot(), "contracts", "public", fmt.Sprintf("%s.json", contractHash)) parsed, _ := ParseAbiFile(abiFilePath) var contractAttributes core.ContractAttributes diff --git a/pkg/geth/contract_test.go b/pkg/geth/contract_test.go index a4ea1125..b35045d7 100644 --- a/pkg/geth/contract_test.go +++ b/pkg/geth/contract_test.go @@ -16,14 +16,13 @@ var _ = Describe("Reading contracts", func() { It("returns a string attribute for a real contract", func() { config, _ := cfg.NewConfig("public") blockchain := geth.NewGethBlockchain(config.Client.IPCPath) - contractHash := "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07" + watchedContract := testing.SampleWatchedContract() - contract, err := blockchain.GetContract(contractHash) - //contractAttributes, _ := blockchain.GetContractAttributes(contractHash) + contractAttributes, err := blockchain.GetAttributes(watchedContract) Expect(err).To(BeNil()) - Expect(len(contract.Attributes)).NotTo(Equal(0)) - symbolAttribute := *testing.FindAttribute(contract.Attributes, "symbol") + Expect(len(contractAttributes)).NotTo(Equal(0)) + symbolAttribute := *testing.FindAttribute(contractAttributes, "symbol") Expect(symbolAttribute.Name).To(Equal("symbol")) Expect(symbolAttribute.Type).To(Equal("string")) }) @@ -31,24 +30,24 @@ var _ = Describe("Reading contracts", func() { It("does not return an attribute that takes an input", func() { config, _ := cfg.NewConfig("public") blockchain := geth.NewGethBlockchain(config.Client.IPCPath) - contractHash := "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07" + watchedContract := testing.SampleWatchedContract() - contract, err := blockchain.GetContract(contractHash) + contractAttributes, err := blockchain.GetAttributes(watchedContract) Expect(err).To(BeNil()) - attribute := testing.FindAttribute(contract.Attributes, "balanceOf") + attribute := testing.FindAttribute(contractAttributes, "balanceOf") Expect(attribute).To(BeNil()) }) It("does not return an attribute that is not constant", func() { config, _ := cfg.NewConfig("public") blockchain := geth.NewGethBlockchain(config.Client.IPCPath) - contractHash := "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07" + watchedContract := testing.SampleWatchedContract() - contract, err := blockchain.GetContract(contractHash) + contractAttributes, err := blockchain.GetAttributes(watchedContract) Expect(err).To(BeNil()) - attribute := testing.FindAttribute(contract.Attributes, "unpause") + attribute := testing.FindAttribute(contractAttributes, "unpause") Expect(attribute).To(BeNil()) }) }) @@ -57,10 +56,9 @@ var _ = Describe("Reading contracts", func() { It("returns the correct attribute for a real contract", func() { config, _ := cfg.NewConfig("public") blockchain := geth.NewGethBlockchain(config.Client.IPCPath) - contractHash := "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07" - contract, _ := blockchain.GetContract(contractHash) - name, err := blockchain.GetAttribute(contract, "name", nil) + watchedContract := testing.SampleWatchedContract() + name, err := blockchain.GetAttribute(watchedContract, "name", nil) Expect(err).To(BeNil()) Expect(name).To(Equal("OMGToken")) @@ -69,10 +67,9 @@ var _ = Describe("Reading contracts", func() { It("returns the correct attribute for a real contract", func() { config, _ := cfg.NewConfig("public") blockchain := geth.NewGethBlockchain(config.Client.IPCPath) - contractHash := "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07" + watchedContract := testing.SampleWatchedContract() - contract, _ := blockchain.GetContract(contractHash) - name, err := blockchain.GetAttribute(contract, "name", nil) + name, err := blockchain.GetAttribute(watchedContract, "name", nil) Expect(err).To(BeNil()) Expect(name).To(Equal("OMGToken")) @@ -81,34 +78,20 @@ var _ = Describe("Reading contracts", func() { It("returns the correct attribute for a real contract at a specific block height", func() { config, _ := cfg.NewConfig("public") blockchain := geth.NewGethBlockchain(config.Client.IPCPath) - contractHash := "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07" + watchedContract := testing.SampleWatchedContract() - contract, _ := blockchain.GetContract(contractHash) - name, err := blockchain.GetAttribute(contract, "name", big.NewInt(4652791)) + name, err := blockchain.GetAttribute(watchedContract, "name", big.NewInt(4652791)) Expect(name).To(Equal("OMGToken")) Expect(err).To(BeNil()) }) - It("returns an error when there is no ABI for the given contract", func() { - config, _ := cfg.NewConfig("public") - blockchain := geth.NewGethBlockchain(config.Client.IPCPath) - contractHash := "MISSINGHASH" - - contract, _ := blockchain.GetContract(contractHash) - name, err := blockchain.GetAttribute(contract, "name", nil) - - Expect(err).To(Equal(geth.ErrMissingAbiFile)) - Expect(name).To(BeNil()) - }) - It("returns an error when asking for an attribute that does not exist", func() { config, _ := cfg.NewConfig("public") blockchain := geth.NewGethBlockchain(config.Client.IPCPath) - contractHash := "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07" + watchedContract := testing.SampleWatchedContract() - contract, _ := blockchain.GetContract(contractHash) - name, err := blockchain.GetAttribute(contract, "missing_attribute", nil) + name, err := blockchain.GetAttribute(watchedContract, "missing_attribute", nil) Expect(err).To(Equal(geth.ErrInvalidStateAttribute)) Expect(name).To(BeNil()) diff --git a/pkg/geth/testing/contract_attributes.go b/pkg/geth/testing/contract_attributes.go deleted file mode 100644 index a6cd7de3..00000000 --- a/pkg/geth/testing/contract_attributes.go +++ /dev/null @@ -1,14 +0,0 @@ -package testing - -import ( - "github.com/8thlight/vulcanizedb/pkg/core" -) - -func FindAttribute(contractAttributes core.ContractAttributes, attributeName string) *core.ContractAttribute { - for _, contractAttribute := range contractAttributes { - if contractAttribute.Name == attributeName { - return &contractAttribute - } - } - return nil -} diff --git a/pkg/geth/testing/helpers.go b/pkg/geth/testing/helpers.go new file mode 100644 index 00000000..db8dc9b5 --- /dev/null +++ b/pkg/geth/testing/helpers.go @@ -0,0 +1,31 @@ +package testing + +import ( + "path/filepath" + + "github.com/8thlight/vulcanizedb/pkg/config" + "github.com/8thlight/vulcanizedb/pkg/core" + "github.com/8thlight/vulcanizedb/pkg/geth" +) + +func FindAttribute(contractAttributes core.ContractAttributes, attributeName string) *core.ContractAttribute { + for _, contractAttribute := range contractAttributes { + if contractAttribute.Name == attributeName { + return &contractAttribute + } + } + return nil +} + +func SampleWatchedContract() core.WatchedContract { + return core.WatchedContract{ + Abi: sampleAbiFileContents(), + Hash: "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07", + } +} + +func sampleAbiFileContents() string { + abiFilepath := filepath.Join(config.ProjectRoot(), "pkg", "geth", "testing", "sample_abi.json") + abiFileContents, _ := geth.ReadAbiFile(abiFilepath) + return abiFileContents +} diff --git a/pkg/geth/testing/sample_abi.json b/pkg/geth/testing/sample_abi.json new file mode 100644 index 00000000..3d80565a --- /dev/null +++ b/pkg/geth/testing/sample_abi.json @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"mintingFinished","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"finishMinting","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_releaseTime","type":"uint256"}],"name":"mintTimelocked","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[],"name":"MintFinished","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]