Skip to content

Commit

Permalink
core, trie: new trie
Browse files Browse the repository at this point in the history
  • Loading branch information
fjl committed Sep 22, 2015
1 parent 6b91a4a commit 565d9f2
Show file tree
Hide file tree
Showing 20 changed files with 1,110 additions and 953 deletions.
3 changes: 2 additions & 1 deletion build/update-license.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ var (
skipPrefixes = []string{
// boring stuff
"Godeps/", "tests/files/", "build/",
// don't relicense vendored packages
// don't relicense vendored sources
"crypto/sha3/", "crypto/ecies/", "logger/glog/",
"crypto/curve.go",
"trie/arc.go",
}

// paths with this prefix are licensed as GPL. all other files are LGPL.
Expand Down
15 changes: 9 additions & 6 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,13 @@ type StateObject struct {

func NewStateObject(address common.Address, db ethdb.Database) *StateObject {
object := &StateObject{db: db, address: address, balance: new(big.Int), gasPool: new(big.Int), dirty: true}
object.trie = trie.NewSecure((common.Hash{}).Bytes(), db)
object.trie, _ = trie.NewSecure(common.Hash{}, db)
object.storage = make(Storage)
object.gasPool = new(big.Int)

return object
}

func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Database) *StateObject {
// TODO clean me up
var extobject struct {
Nonce uint64
Balance *big.Int
Expand All @@ -107,19 +105,24 @@ func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Datab
}
err := rlp.Decode(bytes.NewReader(data), &extobject)
if err != nil {
fmt.Println(err)
glog.Errorf("can't decode state object %x: %v", address, err)
return nil
}
trie, err := trie.NewSecure(extobject.Root, db)
if err != nil {
// TODO: bubble this up or panic
glog.Errorf("can't create account trie with root %x: %v", extobject.Root[:], err)
return nil
}

object := &StateObject{address: address, db: db}
object.nonce = extobject.Nonce
object.balance = extobject.Balance
object.codeHash = extobject.CodeHash
object.trie = trie.NewSecure(extobject.Root[:], db)
object.trie = trie
object.storage = make(map[string]common.Hash)
object.gasPool = new(big.Int)
object.code, _ = db.Get(extobject.CodeHash)

return object
}

Expand Down
24 changes: 16 additions & 8 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,20 @@ type StateDB struct {

// Create a new state from a given trie
func New(root common.Hash, db ethdb.Database) *StateDB {
trie := trie.NewSecure(root[:], db)
return &StateDB{root: root, db: db, trie: trie, stateObjects: make(map[string]*StateObject), refund: new(big.Int), logs: make(map[common.Hash]Logs)}
}

func (self *StateDB) PrintRoot() {
self.trie.Trie.PrintRoot()
tr, err := trie.NewSecure(root, db)
if err != nil {
// TODO: bubble this up
tr, _ = trie.NewSecure(common.Hash{}, db)
glog.Errorf("can't create state trie with root %x: %v", root[:], err)
}
return &StateDB{
root: root,
db: db,
trie: tr,
stateObjects: make(map[string]*StateObject),
refund: new(big.Int),
logs: make(map[common.Hash]Logs),
}
}

func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
Expand Down Expand Up @@ -304,7 +312,7 @@ func (self *StateDB) Set(state *StateDB) {
}

func (s *StateDB) Root() common.Hash {
return common.BytesToHash(s.trie.Root())
return s.trie.Hash()
}

// Syncs the trie and all siblings
Expand Down Expand Up @@ -348,7 +356,7 @@ func (self *StateDB) SyncIntermediate() {

// SyncObjects syncs the changed objects to the trie
func (self *StateDB) SyncObjects() {
self.trie = trie.NewSecure(self.root[:], self.db)
self.trie, _ = trie.NewSecure(self.root, self.db)

self.refund = new(big.Int)

Expand Down
15 changes: 8 additions & 7 deletions core/types/derive_sha.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
package types

import (
"bytes"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
Expand All @@ -29,12 +30,12 @@ type DerivableList interface {
}

func DeriveSha(list DerivableList) common.Hash {
db, _ := ethdb.NewMemDatabase()
trie := trie.New(nil, db)
keybuf := new(bytes.Buffer)
trie := new(trie.Trie)
for i := 0; i < list.Len(); i++ {
key, _ := rlp.EncodeToBytes(uint(i))
trie.Update(key, list.GetRlp(i))
keybuf.Reset()
rlp.Encode(keybuf, uint(i))
trie.Update(keybuf.Bytes(), list.GetRlp(i))
}

return common.BytesToHash(trie.Root())
return trie.Hash()
}
194 changes: 194 additions & 0 deletions trie/arc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// Copyright (c) 2015 Hans Alexander Gugel <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

// This file contains a modified version of package arc from
// https://github.com/alexanderGugel/arc
//
// It implements the ARC (Adaptive Replacement Cache) algorithm as detailed in
// https://www.usenix.org/legacy/event/fast03/tech/full_papers/megiddo/megiddo.pdf

package trie

import (
"container/list"
"sync"
)

type arc struct {
p int
c int
t1 *list.List
b1 *list.List
t2 *list.List
b2 *list.List
cache map[string]*entry
mutex sync.Mutex
}

type entry struct {
key hashNode
value node
ll *list.List
el *list.Element
}

// newARC returns a new Adaptive Replacement Cache with the
// given capacity.
func newARC(c int) *arc {
return &arc{
c: c,
t1: list.New(),
b1: list.New(),
t2: list.New(),
b2: list.New(),
cache: make(map[string]*entry, c),
}
}

// Put inserts a new key-value pair into the cache.
// This optimizes future access to this entry (side effect).
func (a *arc) Put(key hashNode, value node) bool {
a.mutex.Lock()
defer a.mutex.Unlock()
ent, ok := a.cache[string(key)]
if ok != true {
ent = &entry{key: key, value: value}
a.req(ent)
a.cache[string(key)] = ent
} else {
ent.value = value
a.req(ent)
}
return ok
}

// Get retrieves a previously via Set inserted entry.
// This optimizes future access to this entry (side effect).
func (a *arc) Get(key hashNode) (value node, ok bool) {
a.mutex.Lock()
defer a.mutex.Unlock()
ent, ok := a.cache[string(key)]
if ok {
a.req(ent)
return ent.value, ent.value != nil
}
return nil, false
}

func (a *arc) req(ent *entry) {
if ent.ll == a.t1 || ent.ll == a.t2 {
// Case I
ent.setMRU(a.t2)
} else if ent.ll == a.b1 {
// Case II
// Cache Miss in t1 and t2

// Adaptation
var d int
if a.b1.Len() >= a.b2.Len() {
d = 1
} else {
d = a.b2.Len() / a.b1.Len()
}
a.p = a.p + d
if a.p > a.c {
a.p = a.c
}

a.replace(ent)
ent.setMRU(a.t2)
} else if ent.ll == a.b2 {
// Case III
// Cache Miss in t1 and t2

// Adaptation
var d int
if a.b2.Len() >= a.b1.Len() {
d = 1
} else {
d = a.b1.Len() / a.b2.Len()
}
a.p = a.p - d
if a.p < 0 {
a.p = 0
}

a.replace(ent)
ent.setMRU(a.t2)
} else if ent.ll == nil {
// Case IV

if a.t1.Len()+a.b1.Len() == a.c {
// Case A
if a.t1.Len() < a.c {
a.delLRU(a.b1)
a.replace(ent)
} else {
a.delLRU(a.t1)
}
} else if a.t1.Len()+a.b1.Len() < a.c {
// Case B
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c {
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c {
a.delLRU(a.b2)
}
a.replace(ent)
}
}

ent.setMRU(a.t1)
}
}

func (a *arc) delLRU(list *list.List) {
lru := list.Back()
list.Remove(lru)
delete(a.cache, string(lru.Value.(*entry).key))
}

func (a *arc) replace(ent *entry) {
if a.t1.Len() > 0 && ((a.t1.Len() > a.p) || (ent.ll == a.b2 && a.t1.Len() == a.p)) {
lru := a.t1.Back().Value.(*entry)
lru.value = nil
lru.setMRU(a.b1)
} else {
lru := a.t2.Back().Value.(*entry)
lru.value = nil
lru.setMRU(a.b2)
}
}

func (e *entry) setLRU(list *list.List) {
e.detach()
e.ll = list
e.el = e.ll.PushBack(e)
}

func (e *entry) setMRU(list *list.List) {
e.detach()
e.ll = list
e.el = e.ll.PushFront(e)
}

func (e *entry) detach() {
if e.ll != nil {
e.ll.Remove(e.el)
}
}
Loading

0 comments on commit 565d9f2

Please sign in to comment.