diff --git a/core/blockchain.go b/core/blockchain.go
index fed6b4b74..77295b888 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -20,7 +20,6 @@ package core
import (
"errors"
"fmt"
- "github.com/ethereum/go-ethereum/statediff"
"io"
"math/big"
mrand "math/rand"
@@ -45,6 +44,7 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru"
+ "github.com/ethereum/go-ethereum/statediff"
)
var (
@@ -73,7 +73,7 @@ type CacheConfig struct {
TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory
TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
- StateDiff bool // Whether or not to calculate and persist state diffs
+ StateDiff statediff.Config // Settings for state diff extraction
}
// BlockChain represents the canonical chain given a database with a genesis
@@ -177,11 +177,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))
- if cacheConfig.StateDiff {
- bc.diffExtractor = statediff.NewExtractor(db)
+ var err error
+ if cacheConfig.StateDiff.On {
+ bc.diffExtractor, err = statediff.NewExtractor(db, cacheConfig.StateDiff)
+ if err != nil {
+ return nil, err
+ }
}
- var err error
bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.getProcInterrupt)
if err != nil {
return nil, err
@@ -1214,8 +1217,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
proctime := time.Since(bstart)
// If extracting statediffs, do so now
- if bc.cacheConfig.StateDiff {
- bc.diffExtractor.Extract(*parent, *block)
+ if bc.cacheConfig.StateDiff.On {
+ // Currently not doing anything with returned cid...
+ _, err = bc.diffExtractor.ExtractStateDiff(*parent, *block)
+ if err != nil {
+ return i, events, coalescedLogs, err
+ }
}
// Write the block to the chain and get the status.
diff --git a/statediff/builder.go b/statediff/builder.go
index 9017c240d..433353591 100644
--- a/statediff/builder.go
+++ b/statediff/builder.go
@@ -78,7 +78,7 @@ func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, block
updatedKeys := findIntersection(createKeys, deleteKeys)
// Build and return the statediff
- updatedAccounts, err := sdb.buildDiffIncremental(creations, deletions, &updatedKeys)
+ updatedAccounts, err := sdb.buildDiffIncremental(creations, deletions, updatedKeys)
if err != nil {
return nil, err
}
@@ -133,6 +133,7 @@ func (sdb *builder) collectDiffNodes(a, b trie.NodeIterator) (map[common.Address
break
}
}
+
return diffAccounts, nil
}
@@ -140,66 +141,63 @@ func (sdb *builder) buildDiffEventual(accounts map[common.Address]*state.Account
accountDiffs := make(map[common.Address]AccountDiffEventual)
for addr, val := range accounts {
sr := val.Root
- if storageDiffs, err := sdb.buildStorageDiffsEventual(sr, created); err != nil {
+ storageDiffs, err := sdb.buildStorageDiffsEventual(sr, created)
+ if err != nil {
log.Error("Failed building eventual storage diffs", "Address", val, "error", err)
return nil, err
- } else {
- code := ""
- codeBytes, err := sdb.chainDB.Get(val.CodeHash)
- if err == nil && len(codeBytes) != 0 {
- code = common.ToHex(codeBytes)
- } else {
- log.Debug("No code field.", "codehash", val.CodeHash, "Address", val, "error", err)
+ }
+
+ codeBytes, err := sdb.chainDB.Get(val.CodeHash)
+
+ codeHash := common.ToHex(val.CodeHash)
+ hexRoot := val.Root.Hex()
+
+ if created {
+ nonce := diffUint64{
+ NewValue: &val.Nonce,
}
- codeHash := common.ToHex(val.CodeHash)
- if created {
- nonce := diffUint64{
- NewValue: &val.Nonce,
- }
- balance := diffBigInt{
- NewValue: val.Balance,
- }
+ balance := diffBigInt{
+ NewValue: val.Balance,
+ }
- hexRoot := val.Root.Hex()
- contractRoot := diffString{
- NewValue: &hexRoot,
- }
- accountDiffs[addr] = AccountDiffEventual{
- Nonce: nonce,
- Balance: balance,
- CodeHash: codeHash,
- Code: code,
- ContractRoot: contractRoot,
- Storage: storageDiffs,
- }
- } else {
- nonce := diffUint64{
- OldValue: &val.Nonce,
- }
- balance := diffBigInt{
- OldValue: val.Balance,
- }
- hexRoot := val.Root.Hex()
- contractRoot := diffString{
- OldValue: &hexRoot,
- }
- accountDiffs[addr] = AccountDiffEventual{
- Nonce: nonce,
- Balance: balance,
- CodeHash: codeHash,
- ContractRoot: contractRoot,
- Storage: storageDiffs,
- }
+ contractRoot := diffString{
+ NewValue: &hexRoot,
+ }
+ accountDiffs[addr] = AccountDiffEventual{
+ Nonce: nonce,
+ Balance: balance,
+ CodeHash: codeHash,
+ Code: codeBytes,
+ ContractRoot: contractRoot,
+ Storage: storageDiffs,
+ }
+ } else {
+ nonce := diffUint64{
+ OldValue: &val.Nonce,
+ }
+ balance := diffBigInt{
+ OldValue: val.Balance,
+ }
+ contractRoot := diffString{
+ OldValue: &hexRoot,
+ }
+ accountDiffs[addr] = AccountDiffEventual{
+ Nonce: nonce,
+ Balance: balance,
+ CodeHash: codeHash,
+ ContractRoot: contractRoot,
+ Storage: storageDiffs,
}
}
}
+
return accountDiffs, nil
}
-func (sdb *builder) buildDiffIncremental(creations map[common.Address]*state.Account, deletions map[common.Address]*state.Account, updatedKeys *[]string) (map[common.Address]AccountDiffIncremental, error) {
+func (sdb *builder) buildDiffIncremental(creations map[common.Address]*state.Account, deletions map[common.Address]*state.Account, updatedKeys []string) (map[common.Address]AccountDiffIncremental, error) {
updatedAccounts := make(map[common.Address]AccountDiffIncremental)
- for _, val := range *updatedKeys {
+ for _, val := range updatedKeys {
createdAcc := creations[common.HexToAddress(val)]
deletedAcc := deletions[common.HexToAddress(val)]
oldSR := deletedAcc.Root
diff --git a/statediff/config.go b/statediff/config.go
index ee31f271e..ac207acd1 100644
--- a/statediff/config.go
+++ b/statediff/config.go
@@ -22,26 +22,27 @@ package statediff
import "fmt"
type Config struct {
- On bool
- Mode StateDiffMode
+ On bool // Whether or not to extract state diffs
+ Mode StateDiffMode // Mode for storing diffs
+ Path string // Path for storing diffs
}
type StateDiffMode int
const (
- IPFS StateDiffMode = iota
+ IPLD StateDiffMode = iota
LDB
SQL
)
func (mode StateDiffMode) IsValid() bool {
- return mode >= IPFS && mode <= SQL
+ return mode >= IPLD && mode <= SQL
}
// String implements the stringer interface.
func (mode StateDiffMode) String() string {
switch mode {
- case IPFS:
+ case IPLD:
return "ipfs"
case LDB:
return "ldb"
@@ -54,7 +55,7 @@ func (mode StateDiffMode) String() string {
func (mode StateDiffMode) MarshalText() ([]byte, error) {
switch mode {
- case IPFS:
+ case IPLD:
return []byte("ipfs"), nil
case LDB:
return []byte("ldb"), nil
@@ -68,7 +69,7 @@ func (mode StateDiffMode) MarshalText() ([]byte, error) {
func (mode *StateDiffMode) UnmarshalText(text []byte) error {
switch string(text) {
case "ipfs":
- *mode = IPFS
+ *mode = IPLD
case "ldb":
*mode = LDB
case "sql":
diff --git a/statediff/extractor.go b/statediff/extractor.go
index 2abe3ebb3..3cbb5a311 100644
--- a/statediff/extractor.go
+++ b/statediff/extractor.go
@@ -25,26 +25,31 @@ import (
)
type Extractor interface {
- ExtractStateDiff(parent, current types.Block) error
+ ExtractStateDiff(parent, current types.Block) (string, error)
}
type extractor struct {
- b *builder
- p *persister
+ *builder // Interface for building state diff objects from two blocks
+ *publisher // Interface for publishing state diff objects to a datastore (e.g. IPFS)
}
-func NewExtractor(db ethdb.Database) *extractor {
- return &extractor{
- b: NewBuilder(db),
- p: NewPersister(),
- }
-}
-
-func (e *extractor) Extract(parent, current types.Block) error {
- stateDiff, err := e.b.BuildStateDiff(parent.Root(), current.Root(), current.Number().Int64(), current.Hash())
+func NewExtractor(db ethdb.Database, config Config) (*extractor, error) {
+ publisher, err := NewPublisher(config)
if err != nil {
- return err
+ return nil, err
}
- return e.p.PersistStateDiff(stateDiff)
+ return &extractor{
+ builder: NewBuilder(db),
+ publisher: publisher,
+ }, nil
+}
+
+func (e *extractor) ExtractStateDiff(parent, current types.Block) (string, error) {
+ stateDiff, err := e.BuildStateDiff(parent.Root(), current.Root(), current.Number().Int64(), current.Hash())
+ if err != nil {
+ return "", err
+ }
+
+ return e.PublishStateDiff(stateDiff)
}
\ No newline at end of file
diff --git a/statediff/helpers.go b/statediff/helpers.go
index 50626ab2c..8a0a6466e 100644
--- a/statediff/helpers.go
+++ b/statediff/helpers.go
@@ -112,22 +112,7 @@ func decodeNibbles(nibbles []byte, bytes []byte) {
}
}
-// prefixLen returns the length of the common prefix of a and b.
-func prefixLen(a, b []byte) int {
- var i, length = 0, len(a)
- if len(b) < length {
- length = len(b)
- }
- for ; i < length; i++ {
- if a[i] != b[i] {
- break
- }
- }
-
- return i
-}
-
// hasTerm returns whether a hex key has the terminator flag.
func hasTerm(s []byte) bool {
return len(s) > 0 && s[len(s)-1] == 16
-}
+}
\ No newline at end of file
diff --git a/statediff/ipfs/adder.go b/statediff/ipfs/adder.go
new file mode 100644
index 000000000..23eea1c1f
--- /dev/null
+++ b/statediff/ipfs/adder.go
@@ -0,0 +1,58 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Contains a batch of utility type declarations used by the tests. As the node
+// operates on unique types, a lot of them are needed to check various features.
+
+package ipfs
+
+import (
+ "context"
+
+ "github.com/ipfs/go-ipfs/core"
+ "github.com/ipfs/go-ipfs/repo/fsrepo"
+ ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format"
+)
+
+type Adder interface {
+ Add(node ipld.Node) error
+}
+
+type adder struct {
+ n *core.IpfsNode
+ ctx context.Context
+}
+
+func (a adder) Add(node ipld.Node) error {
+ return a.n.DAG.Add(a.n.Context(), node) // For some reason DAG.Add method is not being exposed by the ipld.DAGService
+}
+
+func NewAdder(repoPath string) (*adder, error) {
+ r, err := fsrepo.Open(repoPath)
+ if err != nil {
+ return nil, err
+ }
+ ctx := context.Background()
+ cfg := &core.BuildCfg{
+ Online: false,
+ Repo: r,
+ }
+ ipfsNode, err := core.NewNode(ctx, cfg)
+ if err != nil {
+ return nil, err
+ }
+ return &adder{n: ipfsNode, ctx: ctx}, nil
+}
\ No newline at end of file
diff --git a/statediff/ipfs/dag_putter.go b/statediff/ipfs/dag_putter.go
new file mode 100644
index 000000000..92ffa1e8a
--- /dev/null
+++ b/statediff/ipfs/dag_putter.go
@@ -0,0 +1,79 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Contains a batch of utility type declarations used by the tests. As the node
+// operates on unique types, a lot of them are needed to check various features.
+
+package ipfs
+
+import (
+ "bytes"
+ "encoding/gob"
+ "github.com/i-norden/go-ethereum/statediff"
+
+ ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format"
+)
+
+const (
+ EthStateDiffCode = 0x99 // Register custom codec for state diff?
+)
+
+type DagPutter interface {
+ DagPut(sd *statediff.StateDiff) (string, error)
+}
+
+type dagPutter struct {
+ Adder
+}
+
+func NewDagPutter(adder Adder) *dagPutter {
+ return &dagPutter{Adder: adder}
+}
+
+func (bhdp *dagPutter) DagPut(sd *statediff.StateDiff) (string, error) {
+ nd, err := bhdp.getNode(sd)
+ if err != nil {
+ return "", err
+ }
+ err = bhdp.Add(nd)
+ if err != nil {
+ return "", err
+ }
+ return nd.Cid().String(), nil
+}
+
+func (bhdp *dagPutter) getNode(sd *statediff.StateDiff) (ipld.Node, error) {
+
+ var buff bytes.Buffer
+ enc := gob.NewEncoder(&buff)
+
+ err := enc.Encode(sd)
+ if err != nil {
+ return nil, err
+ }
+
+ raw := buff.Bytes()
+ cid, err := RawToCid(EthStateDiffCode, raw)
+ if err != nil {
+ return nil, err
+ }
+
+ return &StateDiffNode{
+ StateDiff: sd,
+ cid: cid,
+ rawdata: raw,
+ }, nil
+}
\ No newline at end of file
diff --git a/statediff/persister.go b/statediff/ipfs/helpers.go
similarity index 71%
rename from statediff/persister.go
rename to statediff/ipfs/helpers.go
index a3eba0826..a9904afa5 100644
--- a/statediff/persister.go
+++ b/statediff/ipfs/helpers.go
@@ -17,24 +17,22 @@
// Contains a batch of utility type declarations used by the tests. As the node
// operates on unique types, a lot of them are needed to check various features.
-package statediff
+package ipfs
-type Persister interface {
- PersistStateDiff(sd *StateDiff) error
-}
-
-type persister struct {
-
-}
-
-func NewPersister() *persister {
- return &persister{
+import (
+ mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
+ "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid"
+)
+func RawToCid(codec uint64, raw []byte) (*cid.Cid, error) {
+ c, err := cid.Prefix{
+ Codec: codec,
+ Version: 1,
+ MhType: mh.KECCAK_256,
+ MhLength: -1,
+ }.Sum(raw)
+ if err != nil {
+ return nil, err
}
-}
-
-func (p *persister) PersistStateDiff(sd *StateDiff) error {
- //TODO: Persist state diff in IPFS
-
- return nil
+ return c, nil
}
\ No newline at end of file
diff --git a/statediff/ipfs/node.go b/statediff/ipfs/node.go
new file mode 100644
index 000000000..dd7447a80
--- /dev/null
+++ b/statediff/ipfs/node.go
@@ -0,0 +1,78 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Contains a batch of utility type declarations used by the tests. As the node
+// operates on unique types, a lot of them are needed to check various features.
+
+package ipfs
+
+import (
+ ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format"
+ "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid"
+
+ "github.com/i-norden/go-ethereum/statediff"
+)
+
+type StateDiffNode struct {
+ *statediff.StateDiff
+
+ cid *cid.Cid
+ rawdata []byte
+}
+
+func (sdn *StateDiffNode) RawData() []byte {
+ return sdn.rawdata
+}
+
+func (sdn *StateDiffNode) Cid() *cid.Cid {
+ return sdn.cid
+}
+
+func (sdn StateDiffNode) String() string {
+ return sdn.cid.String()
+}
+
+func (sdn StateDiffNode) Loggable() map[string]interface{} {
+ return sdn.cid.Loggable()
+}
+
+func (sdn StateDiffNode) Resolve(path []string) (interface{}, []string, error) {
+ panic("implement me")
+}
+
+func (sdn StateDiffNode) Tree(path string, depth int) []string {
+ panic("implement me")
+}
+
+func (sdn StateDiffNode) ResolveLink(path []string) (*ipld.Link, []string, error) {
+ panic("implement me")
+}
+
+func (sdn StateDiffNode) Copy() ipld.Node {
+ panic("implement me")
+}
+
+func (sdn StateDiffNode) Links() []*ipld.Link {
+ panic("implement me")
+}
+
+func (sdn StateDiffNode) Stat() (*ipld.NodeStat, error) {
+ panic("implement me")
+}
+
+func (sdn StateDiffNode) Size() (uint64, error) {
+ panic("implement me")
+}
\ No newline at end of file
diff --git a/statediff/publisher.go b/statediff/publisher.go
new file mode 100644
index 000000000..163f8d3b0
--- /dev/null
+++ b/statediff/publisher.go
@@ -0,0 +1,63 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Contains a batch of utility type declarations used by the tests. As the node
+// operates on unique types, a lot of them are needed to check various features.
+
+package statediff
+
+import (
+ "errors"
+ "github.com/i-norden/go-ethereum/statediff/ipfs"
+)
+
+type Publisher interface {
+ PublishStateDiff(sd *StateDiff) (string, error)
+}
+
+type publisher struct {
+ ipfs.DagPutter
+ Config
+}
+
+func NewPublisher(config Config) (*publisher, error) {
+ adder, err := ipfs.NewAdder(config.Path)
+ if err != nil {
+ return nil, err
+ }
+
+ return &publisher{
+ DagPutter: ipfs.NewDagPutter(adder),
+ Config: config,
+ }, nil
+}
+
+func (p *publisher) PublishStateDiff(sd *StateDiff) (string, error) {
+ switch p.Mode {
+ case IPLD:
+ cidStr, err := p.DagPut(sd)
+ if err != nil {
+ return "", err
+ }
+
+ return cidStr, err
+ case LDB:
+ case SQL:
+ default:
+ }
+
+ return "", errors.New("state diff publisher: unhandled publishing mode")
+}
\ No newline at end of file
diff --git a/statediff/persister_test.go b/statediff/publisher_test.go
similarity index 100%
rename from statediff/persister_test.go
rename to statediff/publisher_test.go
diff --git a/statediff/statediff.go b/statediff/struct.go
similarity index 98%
rename from statediff/statediff.go
rename to statediff/struct.go
index f2e606936..2142ec755 100644
--- a/statediff/statediff.go
+++ b/statediff/struct.go
@@ -58,7 +58,7 @@ func (sd *StateDiff) Encode() ([]byte, error) {
type AccountDiffEventual struct {
Nonce diffUint64 `json:"nonce" gencodec:"required"`
Balance diffBigInt `json:"balance" gencodec:"required"`
- Code string `json:"code" gencodec:"required"`
+ Code []byte `json:"code" gencodec:"required"`
CodeHash string `json:"codeHash" gencodec:"required"`
ContractRoot diffString `json:"contractRoot" gencodec:"required"`
Storage map[string]diffString `json:"storage" gencodec:"required"`